Browse Source

first commit

Kevin 3 years ago
commit
6277454f7a
100 changed files with 36854 additions and 0 deletions
  1. 17 0
      .editorconfig
  2. 61 0
      .gitattributes
  3. 9 0
      .gitignore
  4. 95 0
      composer.json
  5. 6702 0
      composer.lock
  6. 40 0
      web/.csslintrc
  7. 8 0
      web/.eslintignore
  8. 3 0
      web/.eslintrc.json
  9. 65 0
      web/.ht.router.php
  10. 182 0
      web/.htaccess
  11. 3 0
      web/INSTALL.txt
  12. 143 0
      web/README.txt
  13. 16 0
      web/autoload.php
  14. 77 0
      web/core/.env.example
  15. 8 0
      web/core/.eslintignore
  16. 48 0
      web/core/.eslintrc.json
  17. 94 0
      web/core/.eslintrc.legacy.json
  18. 15 0
      web/core/.eslintrc.passing.json
  19. 21 0
      web/core/.gitignore
  20. 1 0
      web/core/.prettierignore
  21. 6 0
      web/core/.prettierrc.json
  22. 2 0
      web/core/.stylelintignore
  23. 446 0
      web/core/.stylelintrc.json
  24. 8 0
      web/core/CHANGELOG.txt
  25. 59 0
      web/core/COPYRIGHT.txt
  26. 45 0
      web/core/INSTALL.mysql.txt
  27. 44 0
      web/core/INSTALL.pgsql.txt
  28. 39 0
      web/core/INSTALL.sqlite.txt
  29. 459 0
      web/core/INSTALL.txt
  30. 339 0
      web/core/LICENSE.txt
  31. 549 0
      web/core/MAINTAINERS.txt
  32. 77 0
      web/core/UPDATE.txt
  33. 11 0
      web/core/assets/scaffold/README.txt
  34. 16 0
      web/core/assets/scaffold/TESTING.txt
  35. 40 0
      web/core/assets/scaffold/files/csslintrc
  36. 174 0
      web/core/assets/scaffold/files/default.services.yml
  37. 794 0
      web/core/assets/scaffold/files/default.settings.php
  38. 9 0
      web/core/assets/scaffold/files/development.services.yml
  39. 3 0
      web/core/assets/scaffold/files/drupal.INSTALL.txt
  40. 143 0
      web/core/assets/scaffold/files/drupal.README.txt
  41. 17 0
      web/core/assets/scaffold/files/editorconfig
  42. 8 0
      web/core/assets/scaffold/files/eslintignore
  43. 3 0
      web/core/assets/scaffold/files/eslintrc.json
  44. 42 0
      web/core/assets/scaffold/files/example.gitignore
  45. 155 0
      web/core/assets/scaffold/files/example.settings.local.php
  46. 57 0
      web/core/assets/scaffold/files/example.sites.php
  47. 61 0
      web/core/assets/scaffold/files/gitattributes
  48. 65 0
      web/core/assets/scaffold/files/ht.router.php
  49. 182 0
      web/core/assets/scaffold/files/htaccess
  50. 22 0
      web/core/assets/scaffold/files/index.php
  51. 42 0
      web/core/assets/scaffold/files/modules.README.txt
  52. 28 0
      web/core/assets/scaffold/files/profiles.README.txt
  53. 61 0
      web/core/assets/scaffold/files/robots.txt
  54. 10 0
      web/core/assets/scaffold/files/sites.README.txt
  55. 31 0
      web/core/assets/scaffold/files/themes.README.txt
  56. 31 0
      web/core/assets/scaffold/files/update.php
  57. 103 0
      web/core/assets/scaffold/files/web.config
  58. 202 0
      web/core/authorize.php
  59. 227 0
      web/core/composer.json
  60. 3 0
      web/core/config/install/core.extension.yml
  61. 1 0
      web/core/config/install/core.menu.static_menu_link_overrides.yml
  62. 832 0
      web/core/config/schema/core.data_types.schema.yml
  63. 417 0
      web/core/config/schema/core.entity.schema.yml
  64. 19 0
      web/core/config/schema/core.extension.schema.yml
  65. 26 0
      web/core/config/schema/core.menu.schema.yml
  66. 2633 0
      web/core/core.api.php
  67. 1036 0
      web/core/core.libraries.yml
  68. 195 0
      web/core/core.link_relation_types.yml
  69. 1766 0
      web/core/core.services.yml
  70. 57 0
      web/core/drupalci.yml
  71. 135 0
      web/core/globals.api.php
  72. 557 0
      web/core/includes/batch.inc
  73. 795 0
      web/core/includes/bootstrap.inc
  74. 1255 0
      web/core/includes/common.inc
  75. 1143 0
      web/core/includes/database.inc
  76. 485 0
      web/core/includes/entity.inc
  77. 365 0
      web/core/includes/errors.inc
  78. 1144 0
      web/core/includes/file.inc
  79. 1029 0
      web/core/includes/form.inc
  80. 2468 0
      web/core/includes/install.core.inc
  81. 1254 0
      web/core/includes/install.inc
  82. 187 0
      web/core/includes/menu.inc
  83. 240 0
      web/core/includes/module.inc
  84. 348 0
      web/core/includes/pager.inc
  85. 233 0
      web/core/includes/schema.inc
  86. 112 0
      web/core/includes/tablesort.inc
  87. 1895 0
      web/core/includes/theme.inc
  88. 137 0
      web/core/includes/theme.maintenance.inc
  89. 126 0
      web/core/includes/unicode.inc
  90. 786 0
      web/core/includes/update.inc
  91. 61 0
      web/core/includes/utility.inc
  92. 44 0
      web/core/install.php
  93. 791 0
      web/core/lib/Drupal.php
  94. 66 0
      web/core/lib/Drupal/Component/Annotation/AnnotationBase.php
  95. 50 0
      web/core/lib/Drupal/Component/Annotation/AnnotationInterface.php
  96. 1142 0
      web/core/lib/Drupal/Component/Annotation/Doctrine/DocParser.php
  97. 161 0
      web/core/lib/Drupal/Component/Annotation/Doctrine/SimpleAnnotationReader.php
  98. 339 0
      web/core/lib/Drupal/Component/Annotation/LICENSE.txt
  99. 112 0
      web/core/lib/Drupal/Component/Annotation/Plugin.php
  100. 191 0
      web/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php

+ 17 - 0
.editorconfig

@@ -0,0 +1,17 @@
+# Drupal editor configuration normalization
+# @see http://editorconfig.org/
+
+# This is the top-most .editorconfig file; do not search in parent directories.
+root = true
+
+# All files.
+[*]
+end_of_line = LF
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[composer.{json,lock}]
+indent_size = 4

+ 61 - 0
.gitattributes

@@ -0,0 +1,61 @@
+# Drupal git normalization
+# @see https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
+# @see https://www.drupal.org/node/1542048
+
+# Normally these settings would be done with macro attributes for improved
+# readability and easier maintenance. However macros can only be defined at the
+# repository root directory. Drupal avoids making any assumptions about where it
+# is installed.
+
+# Define text file attributes.
+# - Treat them as text.
+# - Ensure no CRLF line-endings, neither on checkout nor on checkin.
+# - Detect whitespace errors.
+#   - Exposed by default in `git diff --color` on the CLI.
+#   - Validate with `git diff --check`.
+#   - Deny applying with `git apply --whitespace=error-all`.
+#   - Fix automatically with `git apply --whitespace=fix`.
+
+*.config  text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.css     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.dist    text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.engine  text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.html    text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=html
+*.inc     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.install text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.js      text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.json    text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.lock    text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.map     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.md      text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.module  text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.php     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.po      text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.profile text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.script  text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.sh      text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.sql     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.svg     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.theme   text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.twig    text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.txt     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.xml     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.yml     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+
+# Define binary file attributes.
+# - Do not treat them as text.
+# - Include binary diff in patches instead of "binary files differ."
+*.eot     -text diff
+*.exe     -text diff
+*.gif     -text diff
+*.gz      -text diff
+*.ico     -text diff
+*.jpeg    -text diff
+*.jpg     -text diff
+*.otf     -text diff
+*.phar    -text diff
+*.png     -text diff
+*.svgz    -text diff
+*.ttf     -text diff
+*.woff    -text diff
+*.woff2   -text diff

+ 9 - 0
.gitignore

@@ -0,0 +1,9 @@
+vendor
+vendor/*
+vendor/.*
+
+web/modules/contrib/*
+web/sites/default/files/*
+web/sites/default/files/.*
+web/sites/default/settings.php
+web/sites/*/files

+ 95 - 0
composer.json

@@ -0,0 +1,95 @@
+{
+    "name": "drupal/recommended-project",
+    "description": "Project template for Drupal 8 projects with a relocated document root",
+    "type": "project",
+    "license": "GPL-2.0-or-later",
+    "homepage": "https://www.drupal.org/project/drupal",
+    "support": {
+        "docs": "https://www.drupal.org/docs/user_guide/en/index.html",
+        "chat": "https://www.drupal.org/node/314178"
+    },
+    "repositories": [
+        {
+            "type": "composer",
+            "url": "https://packages.drupal.org/8"
+        }
+    ],
+    "require": {
+        "composer/installers": "^1.2",
+        "drupal/address": "^1.8",
+        "drupal/audiofield": "^1.9",
+        "drupal/core-composer-scaffold": "^8.8",
+        "drupal/core-recommended": "^8.8",
+        "drupal/field_group": "^3.0",
+        "drupal/linkit": "^4.3",
+        "drupal/mediteran": "^1.10",
+        "drupal/metatag": "^1.13",
+        "drupal/paragraphs": "^1.12",
+        "drupal/pathauto": "^1.8",
+        "drupal/schema_metatag": "^1.5",
+        "drupal/video_embed_field": "^2.4",
+        "drush/drush": "^10.2"
+    },
+    "conflict": {
+        "drupal/drupal": "*"
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true,
+    "config": {
+        "sort-packages": true
+    },
+    "extra": {
+        "drupal-scaffold": {
+            "locations": {
+                "web-root": "web/"
+            }
+        },
+        "installer-paths": {
+            "web/core": [
+                "type:drupal-core"
+            ],
+            "web/libraries/{$name}": [
+                "type:drupal-library"
+            ],
+            "web/modules/contrib/{$name}": [
+                "type:drupal-module"
+            ],
+            "web/profiles/contrib/{$name}": [
+                "type:drupal-profile"
+            ],
+            "web/themes/contrib/{$name}": [
+                "type:drupal-theme"
+            ],
+            "drush/Commands/contrib/{$name}": [
+                "type:drupal-drush"
+            ],
+            "web/modules/custom/{$name}": [
+                "type:drupal-custom-module"
+            ],
+            "web/themes/custom/{$name}": [
+                "type:drupal-custom-theme"
+            ]
+        },
+        "drupal-core-project-message": {
+            "include-keys": [
+                "homepage",
+                "support"
+            ],
+            "post-create-project-cmd-message": [
+                "<bg=blue;fg=white>                                                         </>",
+                "<bg=blue;fg=white>  Congratulations, you’ve installed the Drupal codebase  </>",
+                "<bg=blue;fg=white>  from the drupal/recommended-project template!          </>",
+                "<bg=blue;fg=white>                                                         </>",
+                "",
+                "<bg=yellow;fg=black>Next steps</>:",
+                "  * Install the site: https://www.drupal.org/docs/8/install",
+                "  * Read the user guide: https://www.drupal.org/docs/user_guide/en/index.html",
+                "  * Get support: https://www.drupal.org/support",
+                "  * Get involved with the Drupal community:",
+                "      https://www.drupal.org/getting-involved",
+                "  * Remove the plugin that prints this message:",
+                "      composer remove drupal/core-project-message"
+            ]
+        }
+    }
+}

+ 6702 - 0
composer.lock

@@ -0,0 +1,6702 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "7a189ff08171900faf4041b04a7ad3b7",
+    "packages": [
+        {
+            "name": "asm89/stack-cors",
+            "version": "1.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/asm89/stack-cors.git",
+                "reference": "b9c31def6a83f84b4d4a40d35996d375755f0e08"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/asm89/stack-cors/zipball/b9c31def6a83f84b4d4a40d35996d375755f0e08",
+                "reference": "b9c31def6a83f84b4d4a40d35996d375755f0e08",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5.9",
+                "symfony/http-foundation": "~2.7|~3.0|~4.0|~5.0",
+                "symfony/http-kernel": "~2.7|~3.0|~4.0|~5.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^5.0 || ^4.8.10",
+                "squizlabs/php_codesniffer": "^2.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.2-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Asm89\\Stack\\": "src/Asm89/Stack/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Alexander",
+                    "email": "iam.asm89@gmail.com"
+                }
+            ],
+            "description": "Cross-origin resource sharing library and stack middleware",
+            "homepage": "https://github.com/asm89/stack-cors",
+            "keywords": [
+                "cors",
+                "stack"
+            ],
+            "time": "2019-12-24T22:41:47+00:00"
+        },
+        {
+            "name": "chi-teck/drupal-code-generator",
+            "version": "1.32.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Chi-teck/drupal-code-generator.git",
+                "reference": "0e045f7a7e747af3d8f603156bf4d73be5768246"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Chi-teck/drupal-code-generator/zipball/0e045f7a7e747af3d8f603156bf4d73be5768246",
+                "reference": "0e045f7a7e747af3d8f603156bf4d73be5768246",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "php": ">=5.5.9",
+                "symfony/console": "^3.4 || ^4.0",
+                "symfony/filesystem": "^2.7 || ^3.4 || ^4.0",
+                "twig/twig": "^1.41 || ^2.12"
+            },
+            "bin": [
+                "bin/dcg"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/bootstrap.php"
+                ],
+                "psr-4": {
+                    "DrupalCodeGenerator\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "GPL-2.0-or-later"
+            ],
+            "description": "Drupal code generator",
+            "time": "2020-04-16T06:45:06+00:00"
+        },
+        {
+            "name": "commerceguys/addressing",
+            "version": "v1.0.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/commerceguys/addressing.git",
+                "reference": "cf202c913c10d85085ab5ed9ec88607e312839ce"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/commerceguys/addressing/zipball/cf202c913c10d85085ab5ed9ec88607e312839ce",
+                "reference": "cf202c913c10d85085ab5ed9ec88607e312839ce",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/collections": "~1.0",
+                "php": ">=7.0.8"
+            },
+            "require-dev": {
+                "mikey179/vfsstream": "1.*",
+                "phpunit/phpunit": "^6.0",
+                "squizlabs/php_codesniffer": "2.*",
+                "symfony/validator": "^3.4"
+            },
+            "suggest": {
+                "symfony/validator": "to validate addresses"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "CommerceGuys\\Addressing\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Bojan Zivanovic"
+                },
+                {
+                    "name": "Damien Tournoud"
+                }
+            ],
+            "description": "Addressing library powered by CLDR and Google's address data.",
+            "keywords": [
+                "address",
+                "internationalization",
+                "localization",
+                "postal"
+            ],
+            "time": "2020-05-26T11:04:04+00:00"
+        },
+        {
+            "name": "composer/installers",
+            "version": "v1.9.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/composer/installers.git",
+                "reference": "b93bcf0fa1fccb0b7d176b0967d969691cd74cca"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/composer/installers/zipball/b93bcf0fa1fccb0b7d176b0967d969691cd74cca",
+                "reference": "b93bcf0fa1fccb0b7d176b0967d969691cd74cca",
+                "shasum": ""
+            },
+            "require": {
+                "composer-plugin-api": "^1.0 || ^2.0"
+            },
+            "replace": {
+                "roundcube/plugin-installer": "*",
+                "shama/baton": "*"
+            },
+            "require-dev": {
+                "composer/composer": "1.6.* || 2.0.*@dev",
+                "composer/semver": "1.0.* || 2.0.*@dev",
+                "phpunit/phpunit": "^4.8.36",
+                "sebastian/comparator": "^1.2.4",
+                "symfony/process": "^2.3"
+            },
+            "type": "composer-plugin",
+            "extra": {
+                "class": "Composer\\Installers\\Plugin",
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Composer\\Installers\\": "src/Composer/Installers"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Kyle Robinson Young",
+                    "email": "kyle@dontkry.com",
+                    "homepage": "https://github.com/shama"
+                }
+            ],
+            "description": "A multi-framework Composer library installer",
+            "homepage": "https://composer.github.io/installers/",
+            "keywords": [
+                "Craft",
+                "Dolibarr",
+                "Eliasis",
+                "Hurad",
+                "ImageCMS",
+                "Kanboard",
+                "Lan Management System",
+                "MODX Evo",
+                "MantisBT",
+                "Mautic",
+                "Maya",
+                "OXID",
+                "Plentymarkets",
+                "Porto",
+                "RadPHP",
+                "SMF",
+                "Thelia",
+                "Whmcs",
+                "WolfCMS",
+                "agl",
+                "aimeos",
+                "annotatecms",
+                "attogram",
+                "bitrix",
+                "cakephp",
+                "chef",
+                "cockpit",
+                "codeigniter",
+                "concrete5",
+                "croogo",
+                "dokuwiki",
+                "drupal",
+                "eZ Platform",
+                "elgg",
+                "expressionengine",
+                "fuelphp",
+                "grav",
+                "installer",
+                "itop",
+                "joomla",
+                "known",
+                "kohana",
+                "laravel",
+                "lavalite",
+                "lithium",
+                "magento",
+                "majima",
+                "mako",
+                "mediawiki",
+                "modulework",
+                "modx",
+                "moodle",
+                "osclass",
+                "phpbb",
+                "piwik",
+                "ppi",
+                "puppet",
+                "pxcms",
+                "reindex",
+                "roundcube",
+                "shopware",
+                "silverstripe",
+                "sydes",
+                "sylius",
+                "symfony",
+                "typo3",
+                "wordpress",
+                "yawik",
+                "zend",
+                "zikula"
+            ],
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-04-07T06:57:05+00:00"
+        },
+        {
+            "name": "composer/semver",
+            "version": "1.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/composer/semver.git",
+                "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/composer/semver/zipball/c6bea70230ef4dd483e6bbcab6005f682ed3a8de",
+                "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.2 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.5 || ^5.0.5"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Composer\\Semver\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nils Adermann",
+                    "email": "naderman@naderman.de",
+                    "homepage": "http://www.naderman.de"
+                },
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                },
+                {
+                    "name": "Rob Bast",
+                    "email": "rob.bast@gmail.com",
+                    "homepage": "http://robbast.nl"
+                }
+            ],
+            "description": "Semver library that offers utilities, version constraint parsing and validation.",
+            "keywords": [
+                "semantic",
+                "semver",
+                "validation",
+                "versioning"
+            ],
+            "time": "2020-01-13T12:06:48+00:00"
+        },
+        {
+            "name": "consolidation/annotated-command",
+            "version": "2.12.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/consolidation/annotated-command.git",
+                "reference": "512a2e54c98f3af377589de76c43b24652bcb789"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/consolidation/annotated-command/zipball/512a2e54c98f3af377589de76c43b24652bcb789",
+                "reference": "512a2e54c98f3af377589de76c43b24652bcb789",
+                "shasum": ""
+            },
+            "require": {
+                "consolidation/output-formatters": "^3.4",
+                "php": ">=5.4.5",
+                "psr/log": "^1",
+                "symfony/console": "^2.8|^3|^4",
+                "symfony/event-dispatcher": "^2.5|^3|^4",
+                "symfony/finder": "^2.5|^3|^4"
+            },
+            "require-dev": {
+                "g1a/composer-test-scenarios": "^3",
+                "php-coveralls/php-coveralls": "^1",
+                "phpunit/phpunit": "^6",
+                "squizlabs/php_codesniffer": "^2.7"
+            },
+            "type": "library",
+            "extra": {
+                "scenarios": {
+                    "symfony4": {
+                        "require": {
+                            "symfony/console": "^4.0"
+                        },
+                        "config": {
+                            "platform": {
+                                "php": "7.1.3"
+                            }
+                        }
+                    },
+                    "symfony2": {
+                        "require": {
+                            "symfony/console": "^2.8"
+                        },
+                        "require-dev": {
+                            "phpunit/phpunit": "^4.8.36"
+                        },
+                        "remove": [
+                            "php-coveralls/php-coveralls"
+                        ],
+                        "config": {
+                            "platform": {
+                                "php": "5.4.8"
+                            }
+                        },
+                        "scenario-options": {
+                            "create-lockfile": "false"
+                        }
+                    },
+                    "phpunit4": {
+                        "require-dev": {
+                            "phpunit/phpunit": "^4.8.36"
+                        },
+                        "remove": [
+                            "php-coveralls/php-coveralls"
+                        ],
+                        "config": {
+                            "platform": {
+                                "php": "5.4.8"
+                            }
+                        }
+                    }
+                },
+                "branch-alias": {
+                    "dev-master": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Consolidation\\AnnotatedCommand\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Greg Anderson",
+                    "email": "greg.1.anderson@greenknowe.org"
+                }
+            ],
+            "description": "Initialize Symfony Console commands from annotated command class methods.",
+            "time": "2019-03-08T16:55:03+00:00"
+        },
+        {
+            "name": "consolidation/config",
+            "version": "1.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/consolidation/config.git",
+                "reference": "cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/consolidation/config/zipball/cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1",
+                "reference": "cac1279bae7efb5c7fb2ca4c3ba4b8eb741a96c1",
+                "shasum": ""
+            },
+            "require": {
+                "dflydev/dot-access-data": "^1.1.0",
+                "grasmash/expander": "^1",
+                "php": ">=5.4.0"
+            },
+            "require-dev": {
+                "g1a/composer-test-scenarios": "^3",
+                "php-coveralls/php-coveralls": "^1",
+                "phpunit/phpunit": "^5",
+                "squizlabs/php_codesniffer": "2.*",
+                "symfony/console": "^2.5|^3|^4",
+                "symfony/yaml": "^2.8.11|^3|^4"
+            },
+            "suggest": {
+                "symfony/yaml": "Required to use Consolidation\\Config\\Loader\\YamlConfigLoader"
+            },
+            "type": "library",
+            "extra": {
+                "scenarios": {
+                    "symfony4": {
+                        "require-dev": {
+                            "symfony/console": "^4.0"
+                        },
+                        "config": {
+                            "platform": {
+                                "php": "7.1.3"
+                            }
+                        }
+                    },
+                    "symfony2": {
+                        "require-dev": {
+                            "symfony/console": "^2.8",
+                            "symfony/event-dispatcher": "^2.8",
+                            "phpunit/phpunit": "^4.8.36"
+                        },
+                        "remove": [
+                            "php-coveralls/php-coveralls"
+                        ],
+                        "config": {
+                            "platform": {
+                                "php": "5.4.8"
+                            }
+                        }
+                    }
+                },
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Consolidation\\Config\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Greg Anderson",
+                    "email": "greg.1.anderson@greenknowe.org"
+                }
+            ],
+            "description": "Provide configuration services for a commandline tool.",
+            "time": "2019-03-03T19:37:04+00:00"
+        },
+        {
+            "name": "consolidation/filter-via-dot-access-data",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/consolidation/filter-via-dot-access-data.git",
+                "reference": "a53e96c6b9f7f042f5e085bf911f3493cea823c6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/consolidation/filter-via-dot-access-data/zipball/a53e96c6b9f7f042f5e085bf911f3493cea823c6",
+                "reference": "a53e96c6b9f7f042f5e085bf911f3493cea823c6",
+                "shasum": ""
+            },
+            "require": {
+                "dflydev/dot-access-data": "^1.1.0",
+                "php": ">=5.5.0"
+            },
+            "require-dev": {
+                "consolidation/robo": "^1.2.3",
+                "g1a/composer-test-scenarios": "^3",
+                "knplabs/github-api": "^2.7",
+                "php-coveralls/php-coveralls": "^1",
+                "php-http/guzzle6-adapter": "^1.1",
+                "phpunit/phpunit": "^5",
+                "squizlabs/php_codesniffer": "^2.8",
+                "symfony/console": "^2.8|^3|^4"
+            },
+            "type": "library",
+            "extra": {
+                "scenarios": {
+                    "phpunit5": {
+                        "require-dev": {
+                            "phpunit/phpunit": "^5.7.27"
+                        },
+                        "remove": [
+                            "php-coveralls/php-coveralls"
+                        ],
+                        "config": {
+                            "platform": {
+                                "php": "5.6.33"
+                            }
+                        }
+                    }
+                },
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Consolidation\\Filter\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Greg Anderson",
+                    "email": "greg.1.anderson@greenknowe.org"
+                }
+            ],
+            "description": "This project uses dflydev/dot-access-data to provide simple output filtering for applications built with annotated-command / Robo.",
+            "time": "2019-01-18T06:05:07+00:00"
+        },
+        {
+            "name": "consolidation/log",
+            "version": "1.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/consolidation/log.git",
+                "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/consolidation/log/zipball/b2e887325ee90abc96b0a8b7b474cd9e7c896e3a",
+                "reference": "b2e887325ee90abc96b0a8b7b474cd9e7c896e3a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.5",
+                "psr/log": "^1.0",
+                "symfony/console": "^2.8|^3|^4"
+            },
+            "require-dev": {
+                "g1a/composer-test-scenarios": "^3",
+                "php-coveralls/php-coveralls": "^1",
+                "phpunit/phpunit": "^6",
+                "squizlabs/php_codesniffer": "^2"
+            },
+            "type": "library",
+            "extra": {
+                "scenarios": {
+                    "symfony4": {
+                        "require": {
+                            "symfony/console": "^4.0"
+                        },
+                        "config": {
+                            "platform": {
+                                "php": "7.1.3"
+                            }
+                        }
+                    },
+                    "symfony2": {
+                        "require": {
+                            "symfony/console": "^2.8"
+                        },
+                        "require-dev": {
+                            "phpunit/phpunit": "^4.8.36"
+                        },
+                        "remove": [
+                            "php-coveralls/php-coveralls"
+                        ],
+                        "config": {
+                            "platform": {
+                                "php": "5.4.8"
+                            }
+                        }
+                    },
+                    "phpunit4": {
+                        "require-dev": {
+                            "phpunit/phpunit": "^4.8.36"
+                        },
+                        "remove": [
+                            "php-coveralls/php-coveralls"
+                        ],
+                        "config": {
+                            "platform": {
+                                "php": "5.4.8"
+                            }
+                        }
+                    }
+                },
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Consolidation\\Log\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Greg Anderson",
+                    "email": "greg.1.anderson@greenknowe.org"
+                }
+            ],
+            "description": "Improved Psr-3 / Psr\\Log logger based on Symfony Console components.",
+            "time": "2019-01-01T17:30:51+00:00"
+        },
+        {
+            "name": "consolidation/output-formatters",
+            "version": "3.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/consolidation/output-formatters.git",
+                "reference": "99ec998ffb697e0eada5aacf81feebfb13023605"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/consolidation/output-formatters/zipball/99ec998ffb697e0eada5aacf81feebfb13023605",
+                "reference": "99ec998ffb697e0eada5aacf81feebfb13023605",
+                "shasum": ""
+            },
+            "require": {
+                "dflydev/dot-access-data": "^1.1.0",
+                "php": ">=5.4.0",
+                "symfony/console": "^2.8|^3|^4",
+                "symfony/finder": "^2.5|^3|^4"
+            },
+            "require-dev": {
+                "g1a/composer-test-scenarios": "^3",
+                "php-coveralls/php-coveralls": "^1",
+                "phpunit/phpunit": "^5.7.27",
+                "squizlabs/php_codesniffer": "^2.7",
+                "symfony/var-dumper": "^2.8|^3|^4",
+                "victorjonsson/markdowndocs": "^1.3"
+            },
+            "suggest": {
+                "symfony/var-dumper": "For using the var_dump formatter"
+            },
+            "type": "library",
+            "extra": {
+                "scenarios": {
+                    "symfony4": {
+                        "require": {
+                            "symfony/console": "^4.0"
+                        },
+                        "require-dev": {
+                            "phpunit/phpunit": "^6"
+                        },
+                        "config": {
+                            "platform": {
+                                "php": "7.1.3"
+                            }
+                        }
+                    },
+                    "symfony3": {
+                        "require": {
+                            "symfony/console": "^3.4",
+                            "symfony/finder": "^3.4",
+                            "symfony/var-dumper": "^3.4"
+                        },
+                        "config": {
+                            "platform": {
+                                "php": "5.6.32"
+                            }
+                        }
+                    },
+                    "symfony2": {
+                        "require": {
+                            "symfony/console": "^2.8"
+                        },
+                        "require-dev": {
+                            "phpunit/phpunit": "^4.8.36"
+                        },
+                        "remove": [
+                            "php-coveralls/php-coveralls"
+                        ],
+                        "config": {
+                            "platform": {
+                                "php": "5.4.8"
+                            }
+                        },
+                        "scenario-options": {
+                            "create-lockfile": "false"
+                        }
+                    }
+                },
+                "branch-alias": {
+                    "dev-master": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Consolidation\\OutputFormatters\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Greg Anderson",
+                    "email": "greg.1.anderson@greenknowe.org"
+                }
+            ],
+            "description": "Format text by applying transformations provided by plug-in formatters.",
+            "time": "2019-05-30T23:16:01+00:00"
+        },
+        {
+            "name": "consolidation/robo",
+            "version": "1.4.12",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/consolidation/Robo.git",
+                "reference": "eb45606f498b3426b9a98b7c85e300666a968e51"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/consolidation/Robo/zipball/eb45606f498b3426b9a98b7c85e300666a968e51",
+                "reference": "eb45606f498b3426b9a98b7c85e300666a968e51",
+                "shasum": ""
+            },
+            "require": {
+                "consolidation/annotated-command": "^2.11.0|^4.1",
+                "consolidation/config": "^1.2.1",
+                "consolidation/log": "^1.1.1|^2",
+                "consolidation/output-formatters": "^3.1.13|^4.1",
+                "consolidation/self-update": "^1.1.5",
+                "grasmash/yaml-expander": "^1.4",
+                "league/container": "^2.4.1",
+                "php": ">=5.5.0",
+                "symfony/console": "^2.8|^3|^4",
+                "symfony/event-dispatcher": "^2.5|^3|^4",
+                "symfony/filesystem": "^2.5|^3|^4",
+                "symfony/finder": "^2.5|^3|^4",
+                "symfony/process": "^2.5|^3|^4"
+            },
+            "replace": {
+                "codegyre/robo": "< 1.0"
+            },
+            "require-dev": {
+                "g1a/composer-test-scenarios": "^3",
+                "natxet/cssmin": "3.0.4",
+                "patchwork/jsqueeze": "^2",
+                "pear/archive_tar": "^1.4.4",
+                "php-coveralls/php-coveralls": "^1",
+                "phpunit/phpunit": "^5.7.27",
+                "squizlabs/php_codesniffer": "^3"
+            },
+            "suggest": {
+                "henrikbjorn/lurker": "For monitoring filesystem changes in taskWatch",
+                "natxet/CssMin": "For minifying CSS files in taskMinify",
+                "patchwork/jsqueeze": "For minifying JS files in taskMinify",
+                "pear/archive_tar": "Allows tar archives to be created and extracted in taskPack and taskExtract, respectively."
+            },
+            "bin": [
+                "robo"
+            ],
+            "type": "library",
+            "extra": {
+                "scenarios": {
+                    "symfony4": {
+                        "require": {
+                            "symfony/console": "^4"
+                        },
+                        "config": {
+                            "platform": {
+                                "php": "7.1.3"
+                            }
+                        }
+                    },
+                    "symfony2": {
+                        "require": {
+                            "symfony/console": "^2.8"
+                        },
+                        "require-dev": {
+                            "phpunit/phpunit": "^4.8.36"
+                        },
+                        "remove": [
+                            "php-coveralls/php-coveralls"
+                        ],
+                        "config": {
+                            "platform": {
+                                "php": "5.5.9"
+                            }
+                        },
+                        "scenario-options": {
+                            "create-lockfile": "false"
+                        }
+                    }
+                },
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Robo\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Davert",
+                    "email": "davert.php@resend.cc"
+                }
+            ],
+            "description": "Modern task runner",
+            "time": "2020-02-18T17:31:26+00:00"
+        },
+        {
+            "name": "consolidation/self-update",
+            "version": "1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/consolidation/self-update.git",
+                "reference": "dba6b2c0708f20fa3ba8008a2353b637578849b4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/consolidation/self-update/zipball/dba6b2c0708f20fa3ba8008a2353b637578849b4",
+                "reference": "dba6b2c0708f20fa3ba8008a2353b637578849b4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5.0",
+                "symfony/console": "^2.8|^3|^4|^5",
+                "symfony/filesystem": "^2.5|^3|^4|^5"
+            },
+            "bin": [
+                "scripts/release"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "SelfUpdate\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Alexander Menk",
+                    "email": "menk@mestrona.net"
+                },
+                {
+                    "name": "Greg Anderson",
+                    "email": "greg.1.anderson@greenknowe.org"
+                }
+            ],
+            "description": "Provides a self:update command for Symfony Console applications.",
+            "time": "2020-04-13T02:49:20+00:00"
+        },
+        {
+            "name": "consolidation/site-alias",
+            "version": "3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/consolidation/site-alias.git",
+                "reference": "fd40a03f80f8fd4684b10bef8c8c4ec5a9a9bf26"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/consolidation/site-alias/zipball/fd40a03f80f8fd4684b10bef8c8c4ec5a9a9bf26",
+                "reference": "fd40a03f80f8fd4684b10bef8c8c4ec5a9a9bf26",
+                "shasum": ""
+            },
+            "require": {
+                "consolidation/config": "^1.2.1|^2",
+                "php": ">=5.5.0"
+            },
+            "require-dev": {
+                "consolidation/robo": "^1.2.3|^2",
+                "g1a/composer-test-scenarios": "^3",
+                "knplabs/github-api": "^2.7",
+                "php-coveralls/php-coveralls": "^2.2",
+                "php-http/guzzle6-adapter": "^1.1",
+                "phpunit/phpunit": "^6",
+                "squizlabs/php_codesniffer": "^2.8",
+                "symfony/yaml": "~2.3|^3|^4.4|^5"
+            },
+            "type": "library",
+            "extra": {
+                "scenarios": {
+                    "phpunit5": {
+                        "require-dev": {
+                            "phpunit/phpunit": "^5.7.27"
+                        },
+                        "remove": [
+                            "php-coveralls/php-coveralls"
+                        ],
+                        "config": {
+                            "platform": {
+                                "php": "5.6.33"
+                            }
+                        }
+                    }
+                },
+                "branch-alias": {
+                    "dev-master": "3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Consolidation\\SiteAlias\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Greg Anderson",
+                    "email": "greg.1.anderson@greenknowe.org"
+                },
+                {
+                    "name": "Moshe Weitzman",
+                    "email": "weitzman@tejasa.com"
+                }
+            ],
+            "description": "Manage alias records for local and remote sites.",
+            "time": "2020-05-28T00:33:41+00:00"
+        },
+        {
+            "name": "consolidation/site-process",
+            "version": "2.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/consolidation/site-process.git",
+                "reference": "f3211fa4c60671c6f068184221f06f932556e443"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/consolidation/site-process/zipball/f3211fa4c60671c6f068184221f06f932556e443",
+                "reference": "f3211fa4c60671c6f068184221f06f932556e443",
+                "shasum": ""
+            },
+            "require": {
+                "consolidation/config": "^1.2.1",
+                "consolidation/site-alias": "^3",
+                "php": ">=5.6.0",
+                "symfony/process": "^3.4"
+            },
+            "require-dev": {
+                "consolidation/robo": "^1.3",
+                "g1a/composer-test-scenarios": "^3",
+                "knplabs/github-api": "^2.7",
+                "php-coveralls/php-coveralls": "^1",
+                "php-http/guzzle6-adapter": "^1.1",
+                "phpunit/phpunit": "^6",
+                "squizlabs/php_codesniffer": "^2.8"
+            },
+            "type": "library",
+            "extra": {
+                "scenarios": {
+                    "phpunit5": {
+                        "require-dev": {
+                            "phpunit/phpunit": "^5.7.27"
+                        },
+                        "remove": [
+                            "php-coveralls/php-coveralls"
+                        ],
+                        "config": {
+                            "platform": {
+                                "php": "5.6.33"
+                            }
+                        }
+                    }
+                },
+                "branch-alias": {
+                    "dev-master": "0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Consolidation\\SiteProcess\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Greg Anderson",
+                    "email": "greg.1.anderson@greenknowe.org"
+                },
+                {
+                    "name": "Moshe Weitzman",
+                    "email": "weitzman@tejasa.com"
+                }
+            ],
+            "description": "A thin wrapper around the Symfony Process Component that allows applications to use the Site Alias library to specify the target for a remote call.",
+            "time": "2019-09-10T17:56:24+00:00"
+        },
+        {
+            "name": "container-interop/container-interop",
+            "version": "1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/container-interop/container-interop.git",
+                "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/container-interop/container-interop/zipball/79cbf1341c22ec75643d841642dd5d6acd83bdb8",
+                "reference": "79cbf1341c22ec75643d841642dd5d6acd83bdb8",
+                "shasum": ""
+            },
+            "require": {
+                "psr/container": "^1.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Interop\\Container\\": "src/Interop/Container/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Promoting the interoperability of container objects (DIC, SL, etc.)",
+            "homepage": "https://github.com/container-interop/container-interop",
+            "abandoned": "psr/container",
+            "time": "2017-02-14T19:40:03+00:00"
+        },
+        {
+            "name": "dflydev/dot-access-data",
+            "version": "v1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/dflydev/dflydev-dot-access-data.git",
+                "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/3fbd874921ab2c041e899d044585a2ab9795df8a",
+                "reference": "3fbd874921ab2c041e899d044585a2ab9795df8a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Dflydev\\DotAccessData": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Dragonfly Development Inc.",
+                    "email": "info@dflydev.com",
+                    "homepage": "http://dflydev.com"
+                },
+                {
+                    "name": "Beau Simensen",
+                    "email": "beau@dflydev.com",
+                    "homepage": "http://beausimensen.com"
+                },
+                {
+                    "name": "Carlos Frutos",
+                    "email": "carlos@kiwing.it",
+                    "homepage": "https://github.com/cfrutos"
+                }
+            ],
+            "description": "Given a deep data structure, access data by dot notation.",
+            "homepage": "https://github.com/dflydev/dflydev-dot-access-data",
+            "keywords": [
+                "access",
+                "data",
+                "dot",
+                "notation"
+            ],
+            "time": "2017-01-20T21:14:22+00:00"
+        },
+        {
+            "name": "dnoegel/php-xdg-base-dir",
+            "version": "v0.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/dnoegel/php-xdg-base-dir.git",
+                "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd",
+                "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "XdgBaseDir\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "implementation of xdg base directory specification for php",
+            "time": "2019-12-04T15:06:13+00:00"
+        },
+        {
+            "name": "doctrine/annotations",
+            "version": "v1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/annotations.git",
+                "reference": "54cacc9b81758b14e3ce750f205a393d52339e97"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97",
+                "reference": "54cacc9b81758b14e3ce750f205a393d52339e97",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/lexer": "1.*",
+                "php": "^5.6 || ^7.0"
+            },
+            "require-dev": {
+                "doctrine/cache": "1.*",
+                "phpunit/phpunit": "^5.7"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Docblock Annotations Parser",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "annotations",
+                "docblock",
+                "parser"
+            ],
+            "time": "2017-02-24T16:22:25+00:00"
+        },
+        {
+            "name": "doctrine/cache",
+            "version": "v1.6.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/cache.git",
+                "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/cache/zipball/eb152c5100571c7a45470ff2a35095ab3f3b900b",
+                "reference": "eb152c5100571c7a45470ff2a35095ab3f3b900b",
+                "shasum": ""
+            },
+            "require": {
+                "php": "~5.5|~7.0"
+            },
+            "conflict": {
+                "doctrine/common": ">2.2,<2.4"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~4.8|~5.0",
+                "predis/predis": "~1.0",
+                "satooshi/php-coveralls": "~0.6"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.6.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Caching library offering an object-oriented API for many cache backends",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "cache",
+                "caching"
+            ],
+            "time": "2017-07-22T12:49:21+00:00"
+        },
+        {
+            "name": "doctrine/collections",
+            "version": "v1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/collections.git",
+                "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/collections/zipball/1a4fb7e902202c33cce8c55989b945612943c2ba",
+                "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.6 || ^7.0"
+            },
+            "require-dev": {
+                "doctrine/coding-standard": "~0.1@dev",
+                "phpunit/phpunit": "^5.7"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Doctrine\\Common\\Collections\\": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Collections Abstraction library",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "array",
+                "collections",
+                "iterator"
+            ],
+            "time": "2017-01-03T10:49:41+00:00"
+        },
+        {
+            "name": "doctrine/common",
+            "version": "v2.7.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/common.git",
+                "reference": "4acb8f89626baafede6ee5475bc5844096eba8a9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/common/zipball/4acb8f89626baafede6ee5475bc5844096eba8a9",
+                "reference": "4acb8f89626baafede6ee5475bc5844096eba8a9",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/annotations": "1.*",
+                "doctrine/cache": "1.*",
+                "doctrine/collections": "1.*",
+                "doctrine/inflector": "1.*",
+                "doctrine/lexer": "1.*",
+                "php": "~5.6|~7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^5.4.6"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.7.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Common\\": "lib/Doctrine/Common"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Common Library for Doctrine projects",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "annotations",
+                "collections",
+                "eventmanager",
+                "persistence",
+                "spl"
+            ],
+            "time": "2017-07-22T08:35:12+00:00"
+        },
+        {
+            "name": "doctrine/inflector",
+            "version": "v1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/inflector.git",
+                "reference": "e11d84c6e018beedd929cff5220969a3c6d1d462"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/inflector/zipball/e11d84c6e018beedd929cff5220969a3c6d1d462",
+                "reference": "e11d84c6e018beedd929cff5220969a3c6d1d462",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Benjamin Eberlei",
+                    "email": "kontakt@beberlei.de"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Jonathan Wage",
+                    "email": "jonwage@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "Common String Manipulations with regard to casing and singular/plural rules.",
+            "homepage": "http://www.doctrine-project.org",
+            "keywords": [
+                "inflection",
+                "pluralize",
+                "singularize",
+                "string"
+            ],
+            "time": "2017-07-22T12:18:28+00:00"
+        },
+        {
+            "name": "doctrine/lexer",
+            "version": "1.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/lexer.git",
+                "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8",
+                "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.2"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.5"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Roman Borschel",
+                    "email": "roman@code-factory.org"
+                },
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "guilhermeblanco@gmail.com"
+                },
+                {
+                    "name": "Johannes Schmitt",
+                    "email": "schmittjoh@gmail.com"
+                }
+            ],
+            "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.",
+            "homepage": "https://www.doctrine-project.org/projects/lexer.html",
+            "keywords": [
+                "annotations",
+                "docblock",
+                "lexer",
+                "parser",
+                "php"
+            ],
+            "time": "2019-06-08T11:03:04+00:00"
+        },
+        {
+            "name": "drupal/address",
+            "version": "1.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/address.git",
+                "reference": "8.x-1.8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/address-8.x-1.8.zip",
+                "reference": "8.x-1.8",
+                "shasum": "ded73be392ba9257fca9289719757928a8826275"
+            },
+            "require": {
+                "commerceguys/addressing": "^1.0.7",
+                "drupal/core": "^8.7.7 || ^9"
+            },
+            "require-dev": {
+                "drupal/token": "^1.0"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "drupal": {
+                    "version": "8.x-1.8",
+                    "datestamp": "1583988119",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "bojanz",
+                    "homepage": "https://www.drupal.org/user/86106"
+                },
+                {
+                    "name": "dww",
+                    "homepage": "https://www.drupal.org/user/46549"
+                },
+                {
+                    "name": "googletorp",
+                    "homepage": "https://www.drupal.org/user/386230"
+                },
+                {
+                    "name": "jsacksick",
+                    "homepage": "https://www.drupal.org/user/972218"
+                },
+                {
+                    "name": "mglaman",
+                    "homepage": "https://www.drupal.org/user/2416470"
+                },
+                {
+                    "name": "rszrama",
+                    "homepage": "https://www.drupal.org/user/49344"
+                }
+            ],
+            "description": "Provides functionality for storing, validating and displaying international postal addresses.",
+            "homepage": "http://drupal.org/project/address",
+            "support": {
+                "source": "https://git.drupalcode.org/project/address"
+            }
+        },
+        {
+            "name": "drupal/audiofield",
+            "version": "1.9.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/audiofield.git",
+                "reference": "8.x-1.9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/audiofield-8.x-1.9.zip",
+                "reference": "8.x-1.9",
+                "shasum": "ddb2ca8dd5125a2bd0104e0001037c6fa2716682"
+            },
+            "require": {
+                "drupal/core": "~8.0"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "drupal": {
+                    "version": "8.x-1.9",
+                    "datestamp": "1553712781",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                },
+                "drush": {
+                    "services": {
+                        "drush.services.yml": "^9"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "Daniel Moberly",
+                    "homepage": "https://www.drupal.org/u/danielmoberly",
+                    "role": "Maintainer"
+                },
+                {
+                    "name": "josipsaric",
+                    "homepage": "https://www.drupal.org/user/3063287"
+                },
+                {
+                    "name": "tamerzg",
+                    "homepage": "https://www.drupal.org/user/464564"
+                }
+            ],
+            "description": "AudioField Module",
+            "homepage": "https://www.drupal.org/project/audiofield",
+            "support": {
+                "source": "https://git.drupalcode.org/project/audiofield",
+                "issues": "https://www.drupal.org/project/issues/audiofield"
+            }
+        },
+        {
+            "name": "drupal/core",
+            "version": "8.9.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/drupal/core.git",
+                "reference": "f90882ab0723becda2333e4d33e1a6ab27cb8f0c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/drupal/core/zipball/f90882ab0723becda2333e4d33e1a6ab27cb8f0c",
+                "reference": "f90882ab0723becda2333e4d33e1a6ab27cb8f0c",
+                "shasum": ""
+            },
+            "require": {
+                "asm89/stack-cors": "^1.1",
+                "composer/semver": "^1.0",
+                "doctrine/annotations": "^1.4",
+                "doctrine/common": "^2.7",
+                "easyrdf/easyrdf": "^0.9",
+                "egulias/email-validator": "^2.0",
+                "ext-date": "*",
+                "ext-dom": "*",
+                "ext-filter": "*",
+                "ext-gd": "*",
+                "ext-hash": "*",
+                "ext-json": "*",
+                "ext-pcre": "*",
+                "ext-pdo": "*",
+                "ext-session": "*",
+                "ext-simplexml": "*",
+                "ext-spl": "*",
+                "ext-tokenizer": "*",
+                "ext-xml": "*",
+                "guzzlehttp/guzzle": "^6.3",
+                "laminas/laminas-diactoros": "^1.8",
+                "laminas/laminas-feed": "^2.12",
+                "masterminds/html5": "^2.1",
+                "pear/archive_tar": "^1.4.9",
+                "php": ">=7.0.8",
+                "psr/log": "^1.0",
+                "stack/builder": "^1.0",
+                "symfony-cmf/routing": "^1.4",
+                "symfony/class-loader": "~3.4.0",
+                "symfony/console": "~3.4.0",
+                "symfony/dependency-injection": "~3.4.26",
+                "symfony/event-dispatcher": "~3.4.0",
+                "symfony/http-foundation": "~3.4.35",
+                "symfony/http-kernel": "~3.4.14",
+                "symfony/polyfill-iconv": "^1.0",
+                "symfony/process": "~3.4.0",
+                "symfony/psr-http-message-bridge": "^1.1.2",
+                "symfony/routing": "~3.4.0",
+                "symfony/serializer": "~3.4.0",
+                "symfony/translation": "~3.4.0",
+                "symfony/validator": "~3.4.0",
+                "symfony/yaml": "~3.4.5",
+                "twig/twig": "^1.38.2",
+                "typo3/phar-stream-wrapper": "^3.1.3"
+            },
+            "conflict": {
+                "drupal/pathauto": "<1.6",
+                "drush/drush": "<8.1.10"
+            },
+            "replace": {
+                "drupal/action": "self.version",
+                "drupal/aggregator": "self.version",
+                "drupal/automated_cron": "self.version",
+                "drupal/ban": "self.version",
+                "drupal/bartik": "self.version",
+                "drupal/basic_auth": "self.version",
+                "drupal/big_pipe": "self.version",
+                "drupal/block": "self.version",
+                "drupal/block_content": "self.version",
+                "drupal/block_place": "self.version",
+                "drupal/book": "self.version",
+                "drupal/breakpoint": "self.version",
+                "drupal/ckeditor": "self.version",
+                "drupal/claro": "self.version",
+                "drupal/classy": "self.version",
+                "drupal/color": "self.version",
+                "drupal/comment": "self.version",
+                "drupal/config": "self.version",
+                "drupal/config_translation": "self.version",
+                "drupal/contact": "self.version",
+                "drupal/content_moderation": "self.version",
+                "drupal/content_translation": "self.version",
+                "drupal/contextual": "self.version",
+                "drupal/core-annotation": "self.version",
+                "drupal/core-assertion": "self.version",
+                "drupal/core-bridge": "self.version",
+                "drupal/core-class-finder": "self.version",
+                "drupal/core-datetime": "self.version",
+                "drupal/core-dependency-injection": "self.version",
+                "drupal/core-diff": "self.version",
+                "drupal/core-discovery": "self.version",
+                "drupal/core-event-dispatcher": "self.version",
+                "drupal/core-file-cache": "self.version",
+                "drupal/core-file-security": "self.version",
+                "drupal/core-filesystem": "self.version",
+                "drupal/core-gettext": "self.version",
+                "drupal/core-graph": "self.version",
+                "drupal/core-http-foundation": "self.version",
+                "drupal/core-php-storage": "self.version",
+                "drupal/core-plugin": "self.version",
+                "drupal/core-proxy-builder": "self.version",
+                "drupal/core-render": "self.version",
+                "drupal/core-serialization": "self.version",
+                "drupal/core-transliteration": "self.version",
+                "drupal/core-utility": "self.version",
+                "drupal/core-uuid": "self.version",
+                "drupal/core-version": "self.version",
+                "drupal/datetime": "self.version",
+                "drupal/datetime_range": "self.version",
+                "drupal/dblog": "self.version",
+                "drupal/dynamic_page_cache": "self.version",
+                "drupal/editor": "self.version",
+                "drupal/entity_reference": "self.version",
+                "drupal/field": "self.version",
+                "drupal/field_layout": "self.version",
+                "drupal/field_ui": "self.version",
+                "drupal/file": "self.version",
+                "drupal/filter": "self.version",
+                "drupal/forum": "self.version",
+                "drupal/hal": "self.version",
+                "drupal/help": "self.version",
+                "drupal/help_topics": "self.version",
+                "drupal/history": "self.version",
+                "drupal/image": "self.version",
+                "drupal/inline_form_errors": "self.version",
+                "drupal/jsonapi": "self.version",
+                "drupal/language": "self.version",
+                "drupal/layout_builder": "self.version",
+                "drupal/layout_discovery": "self.version",
+                "drupal/link": "self.version",
+                "drupal/locale": "self.version",
+                "drupal/media": "self.version",
+                "drupal/media_library": "self.version",
+                "drupal/menu_link_content": "self.version",
+                "drupal/menu_ui": "self.version",
+                "drupal/migrate": "self.version",
+                "drupal/migrate_drupal": "self.version",
+                "drupal/migrate_drupal_multilingual": "self.version",
+                "drupal/migrate_drupal_ui": "self.version",
+                "drupal/minimal": "self.version",
+                "drupal/node": "self.version",
+                "drupal/options": "self.version",
+                "drupal/page_cache": "self.version",
+                "drupal/path": "self.version",
+                "drupal/path_alias": "self.version",
+                "drupal/quickedit": "self.version",
+                "drupal/rdf": "self.version",
+                "drupal/responsive_image": "self.version",
+                "drupal/rest": "self.version",
+                "drupal/search": "self.version",
+                "drupal/serialization": "self.version",
+                "drupal/settings_tray": "self.version",
+                "drupal/seven": "self.version",
+                "drupal/shortcut": "self.version",
+                "drupal/simpletest": "self.version",
+                "drupal/standard": "self.version",
+                "drupal/stark": "self.version",
+                "drupal/statistics": "self.version",
+                "drupal/syslog": "self.version",
+                "drupal/system": "self.version",
+                "drupal/taxonomy": "self.version",
+                "drupal/telephone": "self.version",
+                "drupal/text": "self.version",
+                "drupal/toolbar": "self.version",
+                "drupal/tour": "self.version",
+                "drupal/tracker": "self.version",
+                "drupal/update": "self.version",
+                "drupal/user": "self.version",
+                "drupal/views": "self.version",
+                "drupal/views_ui": "self.version",
+                "drupal/workflows": "self.version",
+                "drupal/workspaces": "self.version"
+            },
+            "type": "drupal-core",
+            "extra": {
+                "drupal-scaffold": {
+                    "file-mapping": {
+                        "[project-root]/.editorconfig": "assets/scaffold/files/editorconfig",
+                        "[project-root]/.gitattributes": "assets/scaffold/files/gitattributes",
+                        "[web-root]/.csslintrc": "assets/scaffold/files/csslintrc",
+                        "[web-root]/.eslintignore": "assets/scaffold/files/eslintignore",
+                        "[web-root]/.eslintrc.json": "assets/scaffold/files/eslintrc.json",
+                        "[web-root]/.ht.router.php": "assets/scaffold/files/ht.router.php",
+                        "[web-root]/.htaccess": "assets/scaffold/files/htaccess",
+                        "[web-root]/example.gitignore": "assets/scaffold/files/example.gitignore",
+                        "[web-root]/index.php": "assets/scaffold/files/index.php",
+                        "[web-root]/INSTALL.txt": "assets/scaffold/files/drupal.INSTALL.txt",
+                        "[web-root]/README.txt": "assets/scaffold/files/drupal.README.txt",
+                        "[web-root]/robots.txt": "assets/scaffold/files/robots.txt",
+                        "[web-root]/update.php": "assets/scaffold/files/update.php",
+                        "[web-root]/web.config": "assets/scaffold/files/web.config",
+                        "[web-root]/sites/README.txt": "assets/scaffold/files/sites.README.txt",
+                        "[web-root]/sites/development.services.yml": "assets/scaffold/files/development.services.yml",
+                        "[web-root]/sites/example.settings.local.php": "assets/scaffold/files/example.settings.local.php",
+                        "[web-root]/sites/example.sites.php": "assets/scaffold/files/example.sites.php",
+                        "[web-root]/sites/default/default.services.yml": "assets/scaffold/files/default.services.yml",
+                        "[web-root]/sites/default/default.settings.php": "assets/scaffold/files/default.settings.php",
+                        "[web-root]/modules/README.txt": "assets/scaffold/files/modules.README.txt",
+                        "[web-root]/profiles/README.txt": "assets/scaffold/files/profiles.README.txt",
+                        "[web-root]/themes/README.txt": "assets/scaffold/files/themes.README.txt"
+                    }
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Drupal\\Core\\": "lib/Drupal/Core",
+                    "Drupal\\Component\\": "lib/Drupal/Component",
+                    "Drupal\\Driver\\": "../drivers/lib/Drupal/Driver"
+                },
+                "classmap": [
+                    "lib/Drupal.php",
+                    "lib/Drupal/Component/Utility/Timer.php",
+                    "lib/Drupal/Component/Utility/Unicode.php",
+                    "lib/Drupal/Core/Database/Database.php",
+                    "lib/Drupal/Core/DrupalKernel.php",
+                    "lib/Drupal/Core/DrupalKernelInterface.php",
+                    "lib/Drupal/Core/Site/Settings.php"
+                ]
+            },
+            "scripts": {
+                "pre-autoload-dump": [
+                    "Drupal\\Core\\Composer\\Composer::preAutoloadDump"
+                ],
+                "post-autoload-dump": [
+                    "Drupal\\Core\\Composer\\Composer::ensureHtaccess"
+                ]
+            },
+            "license": [
+                "GPL-2.0-or-later"
+            ],
+            "description": "Drupal is an open source content management platform powering millions of websites and applications."
+        },
+        {
+            "name": "drupal/core-composer-scaffold",
+            "version": "8.9.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/drupal/core-composer-scaffold.git",
+                "reference": "07cdfe2799789fc0c2d0e3e1ba64cb5e2a973ece"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/drupal/core-composer-scaffold/zipball/07cdfe2799789fc0c2d0e3e1ba64cb5e2a973ece",
+                "reference": "07cdfe2799789fc0c2d0e3e1ba64cb5e2a973ece",
+                "shasum": ""
+            },
+            "require": {
+                "composer-plugin-api": "^1 || ^2",
+                "php": ">=7.0.8"
+            },
+            "conflict": {
+                "drupal-composer/drupal-scaffold": "*"
+            },
+            "require-dev": {
+                "composer/composer": "^1.8@stable"
+            },
+            "type": "composer-plugin",
+            "extra": {
+                "class": "Drupal\\Composer\\Plugin\\Scaffold\\Plugin",
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Drupal\\Composer\\Plugin\\Scaffold\\": ""
+                }
+            },
+            "license": [
+                "GPL-2.0-or-later"
+            ],
+            "description": "A flexible Composer project scaffold builder.",
+            "homepage": "https://www.drupal.org/project/drupal",
+            "keywords": [
+                "drupal"
+            ]
+        },
+        {
+            "name": "drupal/core-recommended",
+            "version": "8.9.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/drupal/core-recommended.git",
+                "reference": "1b87cf5dea633a66a1c4f22d635bc92c127071d9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/drupal/core-recommended/zipball/1b87cf5dea633a66a1c4f22d635bc92c127071d9",
+                "reference": "1b87cf5dea633a66a1c4f22d635bc92c127071d9",
+                "shasum": ""
+            },
+            "require": {
+                "asm89/stack-cors": "1.3.0",
+                "composer/semver": "1.5.1",
+                "doctrine/annotations": "v1.4.0",
+                "doctrine/cache": "v1.6.2",
+                "doctrine/collections": "v1.4.0",
+                "doctrine/common": "v2.7.3",
+                "doctrine/inflector": "v1.2.0",
+                "doctrine/lexer": "1.0.2",
+                "drupal/core": "8.9.0",
+                "easyrdf/easyrdf": "0.9.1",
+                "egulias/email-validator": "2.1.17",
+                "guzzlehttp/guzzle": "6.5.4",
+                "guzzlehttp/promises": "v1.3.1",
+                "guzzlehttp/psr7": "1.6.1",
+                "laminas/laminas-diactoros": "1.8.7p2",
+                "laminas/laminas-escaper": "2.6.1",
+                "laminas/laminas-feed": "2.12.2",
+                "laminas/laminas-stdlib": "3.2.1",
+                "laminas/laminas-zendframework-bridge": "1.0.4",
+                "masterminds/html5": "2.3.0",
+                "paragonie/random_compat": "v9.99.99",
+                "pear/archive_tar": "1.4.9",
+                "pear/console_getopt": "v1.4.3",
+                "pear/pear-core-minimal": "v1.10.10",
+                "pear/pear_exception": "v1.0.1",
+                "psr/container": "1.0.0",
+                "psr/http-message": "1.0.1",
+                "psr/log": "1.1.3",
+                "ralouphie/getallheaders": "3.0.3",
+                "stack/builder": "v1.0.5",
+                "symfony-cmf/routing": "1.4.1",
+                "symfony/class-loader": "v3.4.41",
+                "symfony/console": "v3.4.41",
+                "symfony/debug": "v3.4.41",
+                "symfony/dependency-injection": "v3.4.41",
+                "symfony/event-dispatcher": "v3.4.41",
+                "symfony/http-foundation": "v3.4.41",
+                "symfony/http-kernel": "v3.4.41",
+                "symfony/polyfill-ctype": "v1.17.0",
+                "symfony/polyfill-iconv": "v1.17.0",
+                "symfony/polyfill-intl-idn": "v1.17.0",
+                "symfony/polyfill-mbstring": "v1.17.0",
+                "symfony/polyfill-php56": "v1.17.0",
+                "symfony/polyfill-php70": "v1.17.0",
+                "symfony/polyfill-php72": "v1.17.0",
+                "symfony/polyfill-util": "v1.17.0",
+                "symfony/process": "v3.4.41",
+                "symfony/psr-http-message-bridge": "v1.1.2",
+                "symfony/routing": "v3.4.41",
+                "symfony/serializer": "v3.4.41",
+                "symfony/translation": "v3.4.41",
+                "symfony/validator": "v3.4.41",
+                "symfony/yaml": "v3.4.41",
+                "twig/twig": "v1.42.5",
+                "typo3/phar-stream-wrapper": "v3.1.4"
+            },
+            "conflict": {
+                "webflo/drupal-core-strict": "*"
+            },
+            "type": "metapackage",
+            "license": [
+                "GPL-2.0-or-later"
+            ],
+            "description": "Locked core dependencies; require this project INSTEAD OF drupal/core."
+        },
+        {
+            "name": "drupal/ctools",
+            "version": "3.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/ctools.git",
+                "reference": "8.x-3.4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/ctools-8.x-3.4.zip",
+                "reference": "8.x-3.4",
+                "shasum": "016ca5abb7ac4ca720352a72e8989f3ef0e20539"
+            },
+            "require": {
+                "drupal/core": "^8.7.7 || ^9"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "drupal": {
+                    "version": "8.x-3.4",
+                    "datestamp": "1585763383",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "Kris Vanderwater (EclipseGc)",
+                    "homepage": "https://www.drupal.org/u/eclipsegc",
+                    "role": "Maintainer"
+                },
+                {
+                    "name": "Jakob Perry (japerry)",
+                    "homepage": "https://www.drupal.org/u/japerry",
+                    "role": "Maintainer"
+                },
+                {
+                    "name": "Tim Plunkett (tim.plunkett)",
+                    "homepage": "https://www.drupal.org/u/timplunkett",
+                    "role": "Maintainer"
+                },
+                {
+                    "name": "James Gilliland (neclimdul)",
+                    "homepage": "https://www.drupal.org/u/neclimdul",
+                    "role": "Maintainer"
+                },
+                {
+                    "name": "Daniel Wehner (dawehner)",
+                    "homepage": "https://www.drupal.org/u/dawehner",
+                    "role": "Maintainer"
+                },
+                {
+                    "name": "joelpittet",
+                    "homepage": "https://www.drupal.org/user/160302"
+                },
+                {
+                    "name": "merlinofchaos",
+                    "homepage": "https://www.drupal.org/user/26979"
+                },
+                {
+                    "name": "neclimdul",
+                    "homepage": "https://www.drupal.org/user/48673"
+                },
+                {
+                    "name": "sdboyer",
+                    "homepage": "https://www.drupal.org/user/146719"
+                },
+                {
+                    "name": "sun",
+                    "homepage": "https://www.drupal.org/user/54136"
+                },
+                {
+                    "name": "tim.plunkett",
+                    "homepage": "https://www.drupal.org/user/241634"
+                }
+            ],
+            "description": "Provides a number of utility and helper APIs for Drupal developers and site builders.",
+            "homepage": "https://www.drupal.org/project/ctools",
+            "support": {
+                "source": "https://git.drupalcode.org/project/ctools",
+                "issues": "https://www.drupal.org/project/issues/ctools"
+            }
+        },
+        {
+            "name": "drupal/entity_reference_revisions",
+            "version": "1.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/entity_reference_revisions.git",
+                "reference": "8.x-1.8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/entity_reference_revisions-8.x-1.8.zip",
+                "reference": "8.x-1.8",
+                "shasum": "c1279e6c683edc2dbccedba8de1505340c8a62b6"
+            },
+            "require": {
+                "drupal/core": "^8.7.7 || ^9"
+            },
+            "require-dev": {
+                "drupal/diff": "1.x-dev"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "branch-alias": {
+                    "dev-1.x": "1.x-dev"
+                },
+                "drupal": {
+                    "version": "8.x-1.8",
+                    "datestamp": "1583961846",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Berdir",
+                    "homepage": "https://www.drupal.org/user/214652"
+                },
+                {
+                    "name": "Frans",
+                    "homepage": "https://www.drupal.org/user/514222"
+                },
+                {
+                    "name": "jeroen.b",
+                    "homepage": "https://www.drupal.org/user/1853532"
+                },
+                {
+                    "name": "miro_dietiker",
+                    "homepage": "https://www.drupal.org/user/227761"
+                }
+            ],
+            "description": "Entity Reference Revisions",
+            "homepage": "https://www.drupal.org/project/entity_reference_revisions",
+            "support": {
+                "source": "https://git.drupalcode.org/project/entity_reference_revisions"
+            }
+        },
+        {
+            "name": "drupal/field_group",
+            "version": "3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/field_group.git",
+                "reference": "8.x-3.0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/field_group-8.x-3.0.zip",
+                "reference": "8.x-3.0",
+                "shasum": "8d87cdc4abc417aa4d411bffcaeb0a3ef1afa497"
+            },
+            "require": {
+                "drupal/core": "^8 || ^9"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "drupal": {
+                    "version": "8.x-3.0",
+                    "datestamp": "1580250787",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "Hydra",
+                    "homepage": "https://www.drupal.org/user/647364"
+                },
+                {
+                    "name": "Stalski",
+                    "homepage": "https://www.drupal.org/user/322618"
+                },
+                {
+                    "name": "jyve",
+                    "homepage": "https://www.drupal.org/user/591438"
+                },
+                {
+                    "name": "nils.destoop",
+                    "homepage": "https://www.drupal.org/user/361625"
+                },
+                {
+                    "name": "swentel",
+                    "homepage": "https://www.drupal.org/user/107403"
+                }
+            ],
+            "description": "Provides the field_group module.",
+            "homepage": "https://www.drupal.org/project/field_group",
+            "support": {
+                "source": "https://git.drupalcode.org/project/field_group"
+            }
+        },
+        {
+            "name": "drupal/linkit",
+            "version": "4.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/linkit.git",
+                "reference": "8.x-4.3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/linkit-8.x-4.3.zip",
+                "reference": "8.x-4.3",
+                "shasum": "e624ea2f18a6100b76a8337e24f7c08df6e2235e"
+            },
+            "require": {
+                "drupal/core": "~8.0"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "drupal": {
+                    "version": "8.x-4.3",
+                    "datestamp": "1490205830",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "Emil Stjerneman",
+                    "homepage": "https://stjerneman.com",
+                    "email": "emil@stjerneman.com",
+                    "role": "Maintainer"
+                }
+            ],
+            "description": "Linkit - Enriched linking experience",
+            "homepage": "http://drupal.org/project/linkit",
+            "support": {
+                "source": "http://cgit.drupalcode.org/linkit",
+                "issues": "http://drupal.org/project/linkit"
+            }
+        },
+        {
+            "name": "drupal/mediteran",
+            "version": "1.10.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/mediteran.git",
+                "reference": "8.x-1.10"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/mediteran-8.x-1.10.zip",
+                "reference": "8.x-1.10",
+                "shasum": "da1b7389a7e33189ebdb9106d162c07aa68ced5d"
+            },
+            "require": {
+                "drupal/core": "^8 || ^9"
+            },
+            "type": "drupal-theme",
+            "extra": {
+                "drupal": {
+                    "version": "8.x-1.10",
+                    "datestamp": "1590662208",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0-or-later"
+            ],
+            "authors": [
+                {
+                    "name": "bataboza",
+                    "homepage": "https://www.drupal.org/user/3581173"
+                },
+                {
+                    "name": "diqidoq",
+                    "homepage": "https://www.drupal.org/user/1001934"
+                },
+                {
+                    "name": "doxigo",
+                    "homepage": "https://www.drupal.org/user/1331334"
+                }
+            ],
+            "description": "Mediteran administrator is responsive theme for Drupal 8 with clean and modern design.",
+            "homepage": "https://www.drupal.org/project/mediteran",
+            "support": {
+                "source": "https://git.drupalcode.org/project/mediteran"
+            }
+        },
+        {
+            "name": "drupal/metatag",
+            "version": "1.13.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/metatag.git",
+                "reference": "8.x-1.13"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/metatag-8.x-1.13.zip",
+                "reference": "8.x-1.13",
+                "shasum": "c471d9982a6540fd7baccc94572947923634fb6b"
+            },
+            "require": {
+                "drupal/core": "^8 || ^9",
+                "drupal/token": "^1.0"
+            },
+            "require-dev": {
+                "drupal/metatag_dc": "*",
+                "drupal/metatag_open_graph": "*",
+                "drupal/page_manager": "4.x-dev",
+                "drupal/redirect": "1.x-dev"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "drupal": {
+                    "version": "8.x-1.13",
+                    "datestamp": "1587478404",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "See contributors",
+                    "homepage": "https://www.drupal.org/node/640498/committers",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Dave Reid",
+                    "homepage": "https://www.drupal.org/user/53892"
+                }
+            ],
+            "description": "Manage meta tags for all entities.",
+            "homepage": "https://www.drupal.org/project/metatag",
+            "keywords": [
+                "Drupal",
+                "seo"
+            ],
+            "support": {
+                "source": "https://git.drupalcode.org/project/metatag",
+                "issues": "https://www.drupal.org/project/issues/metatag",
+                "docs": "https://www.drupal.org/docs/8/modules/metatag"
+            }
+        },
+        {
+            "name": "drupal/paragraphs",
+            "version": "1.12.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/paragraphs.git",
+                "reference": "8.x-1.12"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/paragraphs-8.x-1.12.zip",
+                "reference": "8.x-1.12",
+                "shasum": "3b67d8af1160af42d93a4610be1e02869e428965"
+            },
+            "require": {
+                "drupal/core": "^8.8 || ^9",
+                "drupal/entity_reference_revisions": "~1.3"
+            },
+            "require-dev": {
+                "drupal/block_field": "~1.0",
+                "drupal/ctools": "3.x-dev",
+                "drupal/diff": "~1.0",
+                "drupal/entity_browser": "2.x-dev",
+                "drupal/entity_usage": "2.x-dev",
+                "drupal/field_group": "3.x-dev",
+                "drupal/inline_entity_form": "~1.0",
+                "drupal/paragraphs-paragraphs_library": "*",
+                "drupal/replicate": "~1.0",
+                "drupal/search_api": "~1.0",
+                "drupal/search_api_db": "*"
+            },
+            "suggest": {
+                "drupal/entity_browser": "Recommended for an improved user experience when using the Paragraphs library module"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "drupal": {
+                    "version": "8.x-1.12",
+                    "datestamp": "1590140081",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0-or-later"
+            ],
+            "authors": [
+                {
+                    "name": "Berdir",
+                    "homepage": "https://www.drupal.org/user/214652"
+                },
+                {
+                    "name": "Frans",
+                    "homepage": "https://www.drupal.org/user/514222"
+                },
+                {
+                    "name": "Primsi",
+                    "homepage": "https://www.drupal.org/user/282629"
+                },
+                {
+                    "name": "jeroen.b",
+                    "homepage": "https://www.drupal.org/user/1853532"
+                },
+                {
+                    "name": "jstoller",
+                    "homepage": "https://www.drupal.org/user/99012"
+                },
+                {
+                    "name": "miro_dietiker",
+                    "homepage": "https://www.drupal.org/user/227761"
+                }
+            ],
+            "description": "Enables the creation of Paragraphs entities.",
+            "homepage": "https://www.drupal.org/project/paragraphs",
+            "support": {
+                "source": "https://git.drupalcode.org/project/paragraphs"
+            }
+        },
+        {
+            "name": "drupal/pathauto",
+            "version": "1.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/pathauto.git",
+                "reference": "8.x-1.8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/pathauto-8.x-1.8.zip",
+                "reference": "8.x-1.8",
+                "shasum": "ede3216abb9c4f77709338d9147334c595046329"
+            },
+            "require": {
+                "drupal/core": "^8.8 || ^9",
+                "drupal/ctools": "*",
+                "drupal/token": "*"
+            },
+            "suggest": {
+                "drupal/redirect": "When installed Pathauto will provide a new \"Update Action\" in case your URLs change. This is the recommended update action and is considered the best practice for SEO and usability."
+            },
+            "type": "drupal-module",
+            "extra": {
+                "drupal": {
+                    "version": "8.x-1.8",
+                    "datestamp": "1588103046",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                },
+                "drush": {
+                    "services": {
+                        "drush.services.yml": "^9 || ^10"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0-or-later"
+            ],
+            "authors": [
+                {
+                    "name": "Berdir",
+                    "homepage": "https://www.drupal.org/user/214652"
+                },
+                {
+                    "name": "Dave Reid",
+                    "homepage": "https://www.drupal.org/user/53892"
+                },
+                {
+                    "name": "Freso",
+                    "homepage": "https://www.drupal.org/user/27504"
+                },
+                {
+                    "name": "greggles",
+                    "homepage": "https://www.drupal.org/user/36762"
+                }
+            ],
+            "description": "Provides a mechanism for modules to automatically generate aliases for the content they manage.",
+            "homepage": "https://www.drupal.org/project/pathauto",
+            "support": {
+                "source": "https://cgit.drupalcode.org/pathauto",
+                "issues": "https://www.drupal.org/project/issues/pathauto",
+                "documentation": "https://www.drupal.org/docs/8/modules/pathauto"
+            }
+        },
+        {
+            "name": "drupal/schema_metatag",
+            "version": "1.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/schema_metatag.git",
+                "reference": "8.x-1.5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/schema_metatag-8.x-1.5.zip",
+                "reference": "8.x-1.5",
+                "shasum": "06334fadc2c6b80312319cbce11b1c3621b2e918"
+            },
+            "require": {
+                "drupal/core": "^8 || ^9",
+                "drupal/metatag": "*"
+            },
+            "require-dev": {
+                "drupal/metatag_views": "*",
+                "drupal/schema_article": "*",
+                "drupal/schema_organization": "*"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "drupal": {
+                    "version": "8.x-1.5",
+                    "datestamp": "1590323806",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "KarenS",
+                    "homepage": "https://www.drupal.org/user/45874"
+                }
+            ],
+            "description": "Metatag implementation of Schema.org structured data (JSON-LD)",
+            "homepage": "https://www.drupal.org/project/schema_metatag",
+            "keywords": [
+                "Drupal"
+            ],
+            "support": {
+                "source": "http://cgit.drupalcode.org/schema_metatag",
+                "issues": "https://www.drupal.org/project/issues/schema_metatag"
+            }
+        },
+        {
+            "name": "drupal/token",
+            "version": "1.7.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/token.git",
+                "reference": "8.x-1.7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/token-8.x-1.7.zip",
+                "reference": "8.x-1.7",
+                "shasum": "c7e3a3757282e4c94e3c1fff08d01e22155cb853"
+            },
+            "require": {
+                "drupal/core": "^8.8 || ^9"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "drupal": {
+                    "version": "8.x-1.7",
+                    "datestamp": "1589314266",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                },
+                "drush": {
+                    "services": {
+                        "drush.services.yml": "^9 || ^10"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "Berdir",
+                    "homepage": "https://www.drupal.org/user/214652"
+                },
+                {
+                    "name": "Dave Reid",
+                    "homepage": "https://www.drupal.org/user/53892"
+                },
+                {
+                    "name": "eaton",
+                    "homepage": "https://www.drupal.org/user/16496"
+                },
+                {
+                    "name": "fago",
+                    "homepage": "https://www.drupal.org/user/16747"
+                },
+                {
+                    "name": "greggles",
+                    "homepage": "https://www.drupal.org/user/36762"
+                },
+                {
+                    "name": "mikeryan",
+                    "homepage": "https://www.drupal.org/user/4420"
+                }
+            ],
+            "description": "Provides a user interface for the Token API, some missing core tokens.",
+            "homepage": "https://www.drupal.org/project/token",
+            "support": {
+                "source": "https://git.drupalcode.org/project/token"
+            }
+        },
+        {
+            "name": "drupal/video_embed_field",
+            "version": "2.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://git.drupalcode.org/project/video_embed_field.git",
+                "reference": "8.x-2.4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://ftp.drupal.org/files/projects/video_embed_field-8.x-2.4.zip",
+                "reference": "8.x-2.4",
+                "shasum": "96b14bb93c1bb6a07b84315a4f87367870f8b574"
+            },
+            "require": {
+                "drupal/core": "^8.8 || ^9"
+            },
+            "require-dev": {
+                "drupal/colorbox": "^1.0",
+                "drupal/video_embed_media": "*"
+            },
+            "type": "drupal-module",
+            "extra": {
+                "drupal": {
+                    "version": "8.x-2.4",
+                    "datestamp": "1587686284",
+                    "security-coverage": {
+                        "status": "covered",
+                        "message": "Covered by Drupal's security advisory policy"
+                    }
+                }
+            },
+            "notification-url": "https://packages.drupal.org/8/downloads",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "Sam152",
+                    "homepage": "https://www.drupal.org/user/1485048"
+                },
+                {
+                    "name": "jec006",
+                    "homepage": "https://www.drupal.org/user/855980"
+                },
+                {
+                    "name": "plopesc",
+                    "homepage": "https://www.drupal.org/user/282415"
+                }
+            ],
+            "description": "A pluggable field type for storing videos from external video hosts such as Vimeo and YouTube.",
+            "homepage": "https://www.drupal.org/project/video_embed_field",
+            "support": {
+                "source": "https://git.drupalcode.org/project/video_embed_field"
+            }
+        },
+        {
+            "name": "drush/drush",
+            "version": "10.2.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/drush-ops/drush.git",
+                "reference": "8307a4e7dae9c63a279d8b59b9dca6c1973576c2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/drush-ops/drush/zipball/8307a4e7dae9c63a279d8b59b9dca6c1973576c2",
+                "reference": "8307a4e7dae9c63a279d8b59b9dca6c1973576c2",
+                "shasum": ""
+            },
+            "require": {
+                "chi-teck/drupal-code-generator": "^1.30.5",
+                "composer/semver": "^1.4",
+                "consolidation/config": "^1.2",
+                "consolidation/filter-via-dot-access-data": "^1",
+                "consolidation/robo": "^1 || ^2",
+                "consolidation/site-alias": "^3.0.0@stable",
+                "consolidation/site-process": "^2.1 || ^4",
+                "ext-dom": "*",
+                "grasmash/yaml-expander": "^1.1.1",
+                "league/container": "~2",
+                "php": ">=7.1.3",
+                "psr/log": "~1.0",
+                "psy/psysh": "~0.6",
+                "symfony/event-dispatcher": "^3.4 || ^4.0",
+                "symfony/finder": "^3.4 || ^4.0",
+                "symfony/var-dumper": "^3.4 || ^4.0 || ^5.0",
+                "symfony/yaml": "^3.4 || ^4.0",
+                "webflo/drupal-finder": "^1.2",
+                "webmozart/path-util": "^2.1.0"
+            },
+            "require-dev": {
+                "composer/installers": "^1.7",
+                "cweagans/composer-patches": "~1.0",
+                "drupal/alinks": "1.0.0",
+                "drupal/core-recommended": "^8.8",
+                "g1a/composer-test-scenarios": "^3",
+                "lox/xhprof": "dev-master",
+                "phpunit/phpunit": "^4.8.36 || ^6.1",
+                "squizlabs/php_codesniffer": "^2.7 || ^3",
+                "vlucas/phpdotenv": "^2.4"
+            },
+            "bin": [
+                "drush"
+            ],
+            "type": "library",
+            "extra": {
+                "installer-paths": {
+                    "sut/core": [
+                        "type:drupal-core"
+                    ],
+                    "sut/libraries/{$name}": [
+                        "type:drupal-library"
+                    ],
+                    "sut/modules/unish/{$name}": [
+                        "drupal/devel"
+                    ],
+                    "sut/themes/unish/{$name}": [
+                        "drupal/empty_theme"
+                    ],
+                    "sut/modules/contrib/{$name}": [
+                        "type:drupal-module"
+                    ],
+                    "sut/profiles/contrib/{$name}": [
+                        "type:drupal-profile"
+                    ],
+                    "sut/themes/contrib/{$name}": [
+                        "type:drupal-theme"
+                    ],
+                    "sut/drush/contrib/{$name}": [
+                        "type:drupal-drush"
+                    ]
+                },
+                "branch-alias": {
+                    "dev-master": "10.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Drush\\": "src/",
+                    "Drush\\Internal\\": "src/internal-forks"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "GPL-2.0-or-later"
+            ],
+            "authors": [
+                {
+                    "name": "Moshe Weitzman",
+                    "email": "weitzman@tejasa.com"
+                },
+                {
+                    "name": "Owen Barton",
+                    "email": "drupal@owenbarton.com"
+                },
+                {
+                    "name": "Greg Anderson",
+                    "email": "greg.1.anderson@greenknowe.org"
+                },
+                {
+                    "name": "Jonathan Araña Cruz",
+                    "email": "jonhattan@faita.net"
+                },
+                {
+                    "name": "Jonathan Hedstrom",
+                    "email": "jhedstrom@gmail.com"
+                },
+                {
+                    "name": "Christopher Gervais",
+                    "email": "chris@ergonlogic.com"
+                },
+                {
+                    "name": "Dave Reid",
+                    "email": "dave@davereid.net"
+                },
+                {
+                    "name": "Damian Lee",
+                    "email": "damiankloip@googlemail.com"
+                }
+            ],
+            "description": "Drush is a command line shell and scripting interface for Drupal, a veritable Swiss Army knife designed to make life easier for those of us who spend some of our working hours hacking away at the command prompt.",
+            "homepage": "http://www.drush.org",
+            "time": "2020-02-26T21:07:53+00:00"
+        },
+        {
+            "name": "easyrdf/easyrdf",
+            "version": "0.9.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/easyrdf/easyrdf.git",
+                "reference": "acd09dfe0555fbcfa254291e433c45fdd4652566"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/easyrdf/easyrdf/zipball/acd09dfe0555fbcfa254291e433c45fdd4652566",
+                "reference": "acd09dfe0555fbcfa254291e433c45fdd4652566",
+                "shasum": ""
+            },
+            "require": {
+                "ext-mbstring": "*",
+                "ext-pcre": "*",
+                "php": ">=5.2.8"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~3.5",
+                "sami/sami": "~1.4",
+                "squizlabs/php_codesniffer": "~1.4.3"
+            },
+            "suggest": {
+                "ml/json-ld": "~1.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "EasyRdf_": "lib/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Nicholas Humfrey",
+                    "email": "njh@aelius.com",
+                    "homepage": "http://www.aelius.com/njh/",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Alexey Zakhlestin",
+                    "email": "indeyets@gmail.com",
+                    "role": "Developer"
+                }
+            ],
+            "description": "EasyRdf is a PHP library designed to make it easy to consume and produce RDF.",
+            "homepage": "http://www.easyrdf.org/",
+            "keywords": [
+                "Linked Data",
+                "RDF",
+                "Semantic Web",
+                "Turtle",
+                "rdfa",
+                "sparql"
+            ],
+            "time": "2015-02-27T09:45:49+00:00"
+        },
+        {
+            "name": "egulias/email-validator",
+            "version": "2.1.17",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/egulias/EmailValidator.git",
+                "reference": "ade6887fd9bd74177769645ab5c474824f8a418a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ade6887fd9bd74177769645ab5c474824f8a418a",
+                "reference": "ade6887fd9bd74177769645ab5c474824f8a418a",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/lexer": "^1.0.1",
+                "php": ">=5.5",
+                "symfony/polyfill-intl-idn": "^1.10"
+            },
+            "require-dev": {
+                "dominicsayers/isemail": "^3.0.7",
+                "phpunit/phpunit": "^4.8.36|^7.5.15",
+                "satooshi/php-coveralls": "^1.0.1"
+            },
+            "suggest": {
+                "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Egulias\\EmailValidator\\": "EmailValidator"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Eduardo Gulias Davis"
+                }
+            ],
+            "description": "A library for validating emails against several RFCs",
+            "homepage": "https://github.com/egulias/EmailValidator",
+            "keywords": [
+                "email",
+                "emailvalidation",
+                "emailvalidator",
+                "validation",
+                "validator"
+            ],
+            "time": "2020-02-13T22:36:52+00:00"
+        },
+        {
+            "name": "grasmash/expander",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/grasmash/expander.git",
+                "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/grasmash/expander/zipball/95d6037344a4be1dd5f8e0b0b2571a28c397578f",
+                "reference": "95d6037344a4be1dd5f8e0b0b2571a28c397578f",
+                "shasum": ""
+            },
+            "require": {
+                "dflydev/dot-access-data": "^1.1.0",
+                "php": ">=5.4"
+            },
+            "require-dev": {
+                "greg-1-anderson/composer-test-scenarios": "^1",
+                "phpunit/phpunit": "^4|^5.5.4",
+                "satooshi/php-coveralls": "^1.0.2|dev-master",
+                "squizlabs/php_codesniffer": "^2.7"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Grasmash\\Expander\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Matthew Grasmick"
+                }
+            ],
+            "description": "Expands internal property references in PHP arrays file.",
+            "time": "2017-12-21T22:14:55+00:00"
+        },
+        {
+            "name": "grasmash/yaml-expander",
+            "version": "1.4.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/grasmash/yaml-expander.git",
+                "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/grasmash/yaml-expander/zipball/3f0f6001ae707a24f4d9733958d77d92bf9693b1",
+                "reference": "3f0f6001ae707a24f4d9733958d77d92bf9693b1",
+                "shasum": ""
+            },
+            "require": {
+                "dflydev/dot-access-data": "^1.1.0",
+                "php": ">=5.4",
+                "symfony/yaml": "^2.8.11|^3|^4"
+            },
+            "require-dev": {
+                "greg-1-anderson/composer-test-scenarios": "^1",
+                "phpunit/phpunit": "^4.8|^5.5.4",
+                "satooshi/php-coveralls": "^1.0.2|dev-master",
+                "squizlabs/php_codesniffer": "^2.7"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Grasmash\\YamlExpander\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Matthew Grasmick"
+                }
+            ],
+            "description": "Expands internal property references in a yaml file.",
+            "time": "2017-12-16T16:06:03+00:00"
+        },
+        {
+            "name": "guzzlehttp/guzzle",
+            "version": "6.5.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle.git",
+                "reference": "a4a1b6930528a8f7ee03518e6442ec7a44155d9d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/a4a1b6930528a8f7ee03518e6442ec7a44155d9d",
+                "reference": "a4a1b6930528a8f7ee03518e6442ec7a44155d9d",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "guzzlehttp/promises": "^1.0",
+                "guzzlehttp/psr7": "^1.6.1",
+                "php": ">=5.5",
+                "symfony/polyfill-intl-idn": "1.17.0"
+            },
+            "require-dev": {
+                "ext-curl": "*",
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
+                "psr/log": "^1.1"
+            },
+            "suggest": {
+                "psr/log": "Required for using the Log middleware"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "6.5-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Guzzle is a PHP HTTP client library",
+            "homepage": "http://guzzlephp.org/",
+            "keywords": [
+                "client",
+                "curl",
+                "framework",
+                "http",
+                "http client",
+                "rest",
+                "web service"
+            ],
+            "time": "2020-05-25T19:35:05+00:00"
+        },
+        {
+            "name": "guzzlehttp/promises",
+            "version": "v1.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/promises.git",
+                "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+                "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Promise\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Guzzle promises library",
+            "keywords": [
+                "promise"
+            ],
+            "time": "2016-12-20T10:07:11+00:00"
+        },
+        {
+            "name": "guzzlehttp/psr7",
+            "version": "1.6.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/psr7.git",
+                "reference": "239400de7a173fe9901b9ac7c06497751f00727a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a",
+                "reference": "239400de7a173fe9901b9ac7c06497751f00727a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0",
+                "psr/http-message": "~1.0",
+                "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
+            },
+            "provide": {
+                "psr/http-message-implementation": "1.0"
+            },
+            "require-dev": {
+                "ext-zlib": "*",
+                "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
+            },
+            "suggest": {
+                "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Psr7\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "homepage": "https://github.com/Tobion"
+                }
+            ],
+            "description": "PSR-7 message implementation that also provides common utility methods",
+            "keywords": [
+                "http",
+                "message",
+                "psr-7",
+                "request",
+                "response",
+                "stream",
+                "uri",
+                "url"
+            ],
+            "time": "2019-07-01T23:21:34+00:00"
+        },
+        {
+            "name": "laminas/laminas-diactoros",
+            "version": "1.8.7p2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laminas/laminas-diactoros.git",
+                "reference": "6991c1af7c8d2c8efee81b22ba97024781824aaa"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/6991c1af7c8d2c8efee81b22ba97024781824aaa",
+                "reference": "6991c1af7c8d2c8efee81b22ba97024781824aaa",
+                "shasum": ""
+            },
+            "require": {
+                "laminas/laminas-zendframework-bridge": "^1.0",
+                "php": "^5.6 || ^7.0",
+                "psr/http-message": "^1.0"
+            },
+            "provide": {
+                "psr/http-message-implementation": "1.0"
+            },
+            "replace": {
+                "zendframework/zend-diactoros": "~1.8.7.0"
+            },
+            "require-dev": {
+                "ext-dom": "*",
+                "ext-libxml": "*",
+                "laminas/laminas-coding-standard": "~1.0",
+                "php-http/psr7-integration-tests": "dev-master",
+                "phpunit/phpunit": "^5.7.16 || ^6.0.8 || ^7.2.7"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-release-1.8": "1.8.x-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions/create_uploaded_file.php",
+                    "src/functions/marshal_headers_from_sapi.php",
+                    "src/functions/marshal_method_from_sapi.php",
+                    "src/functions/marshal_protocol_version_from_sapi.php",
+                    "src/functions/marshal_uri_from_sapi.php",
+                    "src/functions/normalize_server.php",
+                    "src/functions/normalize_uploaded_files.php",
+                    "src/functions/parse_cookie_header.php",
+                    "src/functions/create_uploaded_file.legacy.php",
+                    "src/functions/marshal_headers_from_sapi.legacy.php",
+                    "src/functions/marshal_method_from_sapi.legacy.php",
+                    "src/functions/marshal_protocol_version_from_sapi.legacy.php",
+                    "src/functions/marshal_uri_from_sapi.legacy.php",
+                    "src/functions/normalize_server.legacy.php",
+                    "src/functions/normalize_uploaded_files.legacy.php",
+                    "src/functions/parse_cookie_header.legacy.php"
+                ],
+                "psr-4": {
+                    "Laminas\\Diactoros\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "PSR HTTP Message implementations",
+            "homepage": "https://laminas.dev",
+            "keywords": [
+                "http",
+                "laminas",
+                "psr",
+                "psr-7"
+            ],
+            "time": "2020-03-23T15:28:28+00:00"
+        },
+        {
+            "name": "laminas/laminas-escaper",
+            "version": "2.6.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laminas/laminas-escaper.git",
+                "reference": "25f2a053eadfa92ddacb609dcbbc39362610da70"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/25f2a053eadfa92ddacb609dcbbc39362610da70",
+                "reference": "25f2a053eadfa92ddacb609dcbbc39362610da70",
+                "shasum": ""
+            },
+            "require": {
+                "laminas/laminas-zendframework-bridge": "^1.0",
+                "php": "^5.6 || ^7.0"
+            },
+            "replace": {
+                "zendframework/zend-escaper": "self.version"
+            },
+            "require-dev": {
+                "laminas/laminas-coding-standard": "~1.0.0",
+                "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.6.x-dev",
+                    "dev-develop": "2.7.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laminas\\Escaper\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs",
+            "homepage": "https://laminas.dev",
+            "keywords": [
+                "escaper",
+                "laminas"
+            ],
+            "time": "2019-12-31T16:43:30+00:00"
+        },
+        {
+            "name": "laminas/laminas-feed",
+            "version": "2.12.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laminas/laminas-feed.git",
+                "reference": "8a193ac96ebcb3e16b6ee754ac2a889eefacb654"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laminas/laminas-feed/zipball/8a193ac96ebcb3e16b6ee754ac2a889eefacb654",
+                "reference": "8a193ac96ebcb3e16b6ee754ac2a889eefacb654",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-libxml": "*",
+                "laminas/laminas-escaper": "^2.5.2",
+                "laminas/laminas-stdlib": "^3.2.1",
+                "laminas/laminas-zendframework-bridge": "^1.0",
+                "php": "^5.6 || ^7.0"
+            },
+            "replace": {
+                "zendframework/zend-feed": "^2.12.0"
+            },
+            "require-dev": {
+                "laminas/laminas-cache": "^2.7.2",
+                "laminas/laminas-coding-standard": "~1.0.0",
+                "laminas/laminas-db": "^2.8.2",
+                "laminas/laminas-http": "^2.7",
+                "laminas/laminas-servicemanager": "^2.7.8 || ^3.3",
+                "laminas/laminas-validator": "^2.10.1",
+                "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.20",
+                "psr/http-message": "^1.0.1"
+            },
+            "suggest": {
+                "laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests",
+                "laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub",
+                "laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader",
+                "laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations",
+                "laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent",
+                "psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.12.x-dev",
+                    "dev-develop": "2.13.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laminas\\Feed\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "provides functionality for consuming RSS and Atom feeds",
+            "homepage": "https://laminas.dev",
+            "keywords": [
+                "feed",
+                "laminas"
+            ],
+            "time": "2020-03-29T12:36:29+00:00"
+        },
+        {
+            "name": "laminas/laminas-stdlib",
+            "version": "3.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laminas/laminas-stdlib.git",
+                "reference": "2b18347625a2f06a1a485acfbc870f699dbe51c6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/2b18347625a2f06a1a485acfbc870f699dbe51c6",
+                "reference": "2b18347625a2f06a1a485acfbc870f699dbe51c6",
+                "shasum": ""
+            },
+            "require": {
+                "laminas/laminas-zendframework-bridge": "^1.0",
+                "php": "^5.6 || ^7.0"
+            },
+            "replace": {
+                "zendframework/zend-stdlib": "self.version"
+            },
+            "require-dev": {
+                "laminas/laminas-coding-standard": "~1.0.0",
+                "phpbench/phpbench": "^0.13",
+                "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.2.x-dev",
+                    "dev-develop": "3.3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Laminas\\Stdlib\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "SPL extensions, array utilities, error handlers, and more",
+            "homepage": "https://laminas.dev",
+            "keywords": [
+                "laminas",
+                "stdlib"
+            ],
+            "time": "2019-12-31T17:51:15+00:00"
+        },
+        {
+            "name": "laminas/laminas-zendframework-bridge",
+            "version": "1.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/laminas/laminas-zendframework-bridge.git",
+                "reference": "fcd87520e4943d968557803919523772475e8ea3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/laminas/laminas-zendframework-bridge/zipball/fcd87520e4943d968557803919523772475e8ea3",
+                "reference": "fcd87520e4943d968557803919523772475e8ea3",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.6 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^5.7 || ^6.5 || ^7.5 || ^8.1",
+                "squizlabs/php_codesniffer": "^3.5"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev",
+                    "dev-develop": "1.1.x-dev"
+                },
+                "laminas": {
+                    "module": "Laminas\\ZendFrameworkBridge"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/autoload.php"
+                ],
+                "psr-4": {
+                    "Laminas\\ZendFrameworkBridge\\": "src//"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "Alias legacy ZF class names to Laminas Project equivalents.",
+            "keywords": [
+                "ZendFramework",
+                "autoloading",
+                "laminas",
+                "zf"
+            ],
+            "funding": [
+                {
+                    "url": "https://funding.communitybridge.org/projects/laminas-project",
+                    "type": "community_bridge"
+                }
+            ],
+            "time": "2020-05-20T16:45:56+00:00"
+        },
+        {
+            "name": "league/container",
+            "version": "2.4.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/container.git",
+                "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/container/zipball/43f35abd03a12977a60ffd7095efd6a7808488c0",
+                "reference": "43f35abd03a12977a60ffd7095efd6a7808488c0",
+                "shasum": ""
+            },
+            "require": {
+                "container-interop/container-interop": "^1.2",
+                "php": "^5.4.0 || ^7.0"
+            },
+            "provide": {
+                "container-interop/container-interop-implementation": "^1.2",
+                "psr/container-implementation": "^1.0"
+            },
+            "replace": {
+                "orno/di": "~2.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "4.*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-2.x": "2.x-dev",
+                    "dev-1.x": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "League\\Container\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Phil Bennett",
+                    "email": "philipobenito@gmail.com",
+                    "homepage": "http://www.philipobenito.com",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A fast and intuitive dependency injection container.",
+            "homepage": "https://github.com/thephpleague/container",
+            "keywords": [
+                "container",
+                "dependency",
+                "di",
+                "injection",
+                "league",
+                "provider",
+                "service"
+            ],
+            "time": "2017-05-10T09:20:27+00:00"
+        },
+        {
+            "name": "masterminds/html5",
+            "version": "2.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Masterminds/html5-php.git",
+                "reference": "2c37c6c520b995b761674de3be8455a381679067"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/2c37c6c520b995b761674de3be8455a381679067",
+                "reference": "2c37c6c520b995b761674de3be8455a381679067",
+                "shasum": ""
+            },
+            "require": {
+                "ext-libxml": "*",
+                "php": ">=5.3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "4.*",
+                "sami/sami": "~2.0",
+                "satooshi/php-coveralls": "1.0.*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.2-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Masterminds\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Matt Butcher",
+                    "email": "technosophos@gmail.com"
+                },
+                {
+                    "name": "Asmir Mustafic",
+                    "email": "goetas@gmail.com"
+                },
+                {
+                    "name": "Matt Farina",
+                    "email": "matt@mattfarina.com"
+                }
+            ],
+            "description": "An HTML5 parser and serializer.",
+            "homepage": "http://masterminds.github.io/html5-php",
+            "keywords": [
+                "HTML5",
+                "dom",
+                "html",
+                "parser",
+                "querypath",
+                "serializer",
+                "xml"
+            ],
+            "time": "2017-09-04T12:26:28+00:00"
+        },
+        {
+            "name": "nikic/php-parser",
+            "version": "v4.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/nikic/PHP-Parser.git",
+                "reference": "53c2753d756f5adb586dca79c2ec0e2654dd9463"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/53c2753d756f5adb586dca79c2ec0e2654dd9463",
+                "reference": "53c2753d756f5adb586dca79c2ec0e2654dd9463",
+                "shasum": ""
+            },
+            "require": {
+                "ext-tokenizer": "*",
+                "php": ">=7.0"
+            },
+            "require-dev": {
+                "ircmaxell/php-yacc": "0.0.5",
+                "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0"
+            },
+            "bin": [
+                "bin/php-parse"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PhpParser\\": "lib/PhpParser"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Nikita Popov"
+                }
+            ],
+            "description": "A PHP parser written in PHP",
+            "keywords": [
+                "parser",
+                "php"
+            ],
+            "time": "2020-06-03T07:24:19+00:00"
+        },
+        {
+            "name": "paragonie/random_compat",
+            "version": "v9.99.99",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/paragonie/random_compat.git",
+                "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
+                "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "4.*|5.*",
+                "vimeo/psalm": "^1"
+            },
+            "suggest": {
+                "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
+            },
+            "type": "library",
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Paragon Initiative Enterprises",
+                    "email": "security@paragonie.com",
+                    "homepage": "https://paragonie.com"
+                }
+            ],
+            "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
+            "keywords": [
+                "csprng",
+                "polyfill",
+                "pseudorandom",
+                "random"
+            ],
+            "time": "2018-07-02T15:55:56+00:00"
+        },
+        {
+            "name": "pear/archive_tar",
+            "version": "1.4.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/pear/Archive_Tar.git",
+                "reference": "c5b00053770e1d72128252c62c2c1a12c26639f0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/c5b00053770e1d72128252c62c2c1a12c26639f0",
+                "reference": "c5b00053770e1d72128252c62c2c1a12c26639f0",
+                "shasum": ""
+            },
+            "require": {
+                "pear/pear-core-minimal": "^1.10.0alpha2",
+                "php": ">=5.2.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "*"
+            },
+            "suggest": {
+                "ext-bz2": "Bz2 compression support.",
+                "ext-xz": "Lzma2 compression support.",
+                "ext-zlib": "Gzip compression support."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Archive_Tar": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                "./"
+            ],
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Vincent Blavet",
+                    "email": "vincent@phpconcept.net"
+                },
+                {
+                    "name": "Greg Beaver",
+                    "email": "greg@chiaraquartet.net"
+                },
+                {
+                    "name": "Michiel Rook",
+                    "email": "mrook@php.net"
+                }
+            ],
+            "description": "Tar file management class with compression support (gzip, bzip2, lzma2)",
+            "homepage": "https://github.com/pear/Archive_Tar",
+            "keywords": [
+                "archive",
+                "tar"
+            ],
+            "time": "2019-12-04T10:17:28+00:00"
+        },
+        {
+            "name": "pear/console_getopt",
+            "version": "v1.4.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/pear/Console_Getopt.git",
+                "reference": "a41f8d3e668987609178c7c4a9fe48fecac53fa0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/pear/Console_Getopt/zipball/a41f8d3e668987609178c7c4a9fe48fecac53fa0",
+                "reference": "a41f8d3e668987609178c7c4a9fe48fecac53fa0",
+                "shasum": ""
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "Console": "./"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                "./"
+            ],
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Andrei Zmievski",
+                    "email": "andrei@php.net",
+                    "role": "Lead"
+                },
+                {
+                    "name": "Stig Bakken",
+                    "email": "stig@php.net",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Greg Beaver",
+                    "email": "cellog@php.net",
+                    "role": "Helper"
+                }
+            ],
+            "description": "More info available on: http://pear.php.net/package/Console_Getopt",
+            "time": "2019-11-20T18:27:48+00:00"
+        },
+        {
+            "name": "pear/pear-core-minimal",
+            "version": "v1.10.10",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/pear/pear-core-minimal.git",
+                "reference": "625a3c429d9b2c1546438679074cac1b089116a7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/625a3c429d9b2c1546438679074cac1b089116a7",
+                "reference": "625a3c429d9b2c1546438679074cac1b089116a7",
+                "shasum": ""
+            },
+            "require": {
+                "pear/console_getopt": "~1.4",
+                "pear/pear_exception": "~1.0"
+            },
+            "replace": {
+                "rsky/pear-core-min": "self.version"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-0": {
+                    "": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                "src/"
+            ],
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Christian Weiske",
+                    "email": "cweiske@php.net",
+                    "role": "Lead"
+                }
+            ],
+            "description": "Minimal set of PEAR core files to be used as composer dependency",
+            "time": "2019-11-19T19:00:24+00:00"
+        },
+        {
+            "name": "pear/pear_exception",
+            "version": "v1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/pear/PEAR_Exception.git",
+                "reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
+                "reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=4.4.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "*"
+            },
+            "type": "class",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "PEAR/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "include-path": [
+                "."
+            ],
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Helgi Thormar",
+                    "email": "dufuz@php.net"
+                },
+                {
+                    "name": "Greg Beaver",
+                    "email": "cellog@php.net"
+                }
+            ],
+            "description": "The PEAR Exception base class.",
+            "homepage": "https://github.com/pear/PEAR_Exception",
+            "keywords": [
+                "exception"
+            ],
+            "time": "2019-12-10T10:24:42+00:00"
+        },
+        {
+            "name": "psr/container",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "time": "2017-02-14T16:28:37+00:00"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "time": "2016-08-06T14:39:51+00:00"
+        },
+        {
+            "name": "psr/log",
+            "version": "1.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log.git",
+                "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
+                "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Log\\": "Psr/Log/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "homepage": "https://github.com/php-fig/log",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ],
+            "time": "2020-03-23T09:12:05+00:00"
+        },
+        {
+            "name": "psy/psysh",
+            "version": "v0.10.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/bobthecow/psysh.git",
+                "reference": "a8aec1b2981ab66882a01cce36a49b6317dc3560"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/bobthecow/psysh/zipball/a8aec1b2981ab66882a01cce36a49b6317dc3560",
+                "reference": "a8aec1b2981ab66882a01cce36a49b6317dc3560",
+                "shasum": ""
+            },
+            "require": {
+                "dnoegel/php-xdg-base-dir": "0.1.*",
+                "ext-json": "*",
+                "ext-tokenizer": "*",
+                "nikic/php-parser": "~4.0|~3.0|~2.0|~1.3",
+                "php": "^8.0 || ^7.0 || ^5.5.9",
+                "symfony/console": "~5.0|~4.0|~3.0|^2.4.2|~2.3.10",
+                "symfony/var-dumper": "~5.0|~4.0|~3.0|~2.7"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.2",
+                "hoa/console": "3.17.*"
+            },
+            "suggest": {
+                "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)",
+                "ext-pdo-sqlite": "The doc command requires SQLite to work.",
+                "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well.",
+                "ext-readline": "Enables support for arrow-key history navigation, and showing and manipulating command history.",
+                "hoa/console": "A pure PHP readline implementation. You'll want this if your PHP install doesn't already support readline or libedit."
+            },
+            "bin": [
+                "bin/psysh"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "0.10.x-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions.php"
+                ],
+                "psr-4": {
+                    "Psy\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Justin Hileman",
+                    "email": "justin@justinhileman.info",
+                    "homepage": "http://justinhileman.com"
+                }
+            ],
+            "description": "An interactive shell for modern PHP.",
+            "homepage": "http://psysh.org",
+            "keywords": [
+                "REPL",
+                "console",
+                "interactive",
+                "shell"
+            ],
+            "time": "2020-05-03T19:32:03+00:00"
+        },
+        {
+            "name": "ralouphie/getallheaders",
+            "version": "3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ralouphie/getallheaders.git",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.1",
+                "phpunit/phpunit": "^5 || ^6.5"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/getallheaders.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ralph Khattar",
+                    "email": "ralph.khattar@gmail.com"
+                }
+            ],
+            "description": "A polyfill for getallheaders.",
+            "time": "2019-03-08T08:55:37+00:00"
+        },
+        {
+            "name": "stack/builder",
+            "version": "v1.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/stackphp/builder.git",
+                "reference": "fb3d136d04c6be41120ebf8c0cc71fe9507d750a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/stackphp/builder/zipball/fb3d136d04c6be41120ebf8c0cc71fe9507d750a",
+                "reference": "fb3d136d04c6be41120ebf8c0cc71fe9507d750a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "symfony/http-foundation": "~2.1|~3.0|~4.0",
+                "symfony/http-kernel": "~2.1|~3.0|~4.0"
+            },
+            "require-dev": {
+                "silex/silex": "~1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Stack": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Igor Wiedler",
+                    "email": "igor@wiedler.ch"
+                }
+            ],
+            "description": "Builder for stack middlewares based on HttpKernelInterface.",
+            "keywords": [
+                "stack"
+            ],
+            "time": "2017-11-18T14:57:29+00:00"
+        },
+        {
+            "name": "symfony-cmf/routing",
+            "version": "1.4.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony-cmf/routing.git",
+                "reference": "fb1e7f85ff8c6866238b7e73a490a0a0243ae8ac"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony-cmf/routing/zipball/fb1e7f85ff8c6866238b7e73a490a0a0243ae8ac",
+                "reference": "fb1e7f85ff8c6866238b7e73a490a0a0243ae8ac",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.9|^7.0",
+                "psr/log": "1.*",
+                "symfony/http-kernel": "^2.2|3.*",
+                "symfony/routing": "^2.2|3.*"
+            },
+            "require-dev": {
+                "friendsofsymfony/jsrouting-bundle": "^1.1",
+                "symfony-cmf/testing": "^1.3",
+                "symfony/config": "^2.2|3.*",
+                "symfony/dependency-injection": "^2.0.5|3.*",
+                "symfony/event-dispatcher": "^2.1|3.*"
+            },
+            "suggest": {
+                "symfony/event-dispatcher": "DynamicRouter can optionally trigger an event at the start of matching. Minimal version (~2.1)"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Cmf\\Component\\Routing\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Symfony CMF Community",
+                    "homepage": "https://github.com/symfony-cmf/Routing/contributors"
+                }
+            ],
+            "description": "Extends the Symfony2 routing component for dynamic routes and chaining several routers",
+            "homepage": "http://cmf.symfony.com",
+            "keywords": [
+                "database",
+                "routing"
+            ],
+            "time": "2017-05-09T08:10:41+00:00"
+        },
+        {
+            "name": "symfony/class-loader",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/class-loader.git",
+                "reference": "e4636a4f23f157278a19e5db160c63de0da297d8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/class-loader/zipball/e4636a4f23f157278a19e5db160c63de0da297d8",
+                "reference": "e4636a4f23f157278a19e5db160c63de0da297d8",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8"
+            },
+            "require-dev": {
+                "symfony/finder": "~2.8|~3.0|~4.0",
+                "symfony/polyfill-apcu": "~1.1"
+            },
+            "suggest": {
+                "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\ClassLoader\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony ClassLoader Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-03-15T09:38:08+00:00"
+        },
+        {
+            "name": "symfony/console",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/console.git",
+                "reference": "bfe29ead7e7b1cc9ce74c6a40d06ad1f96fced13"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/console/zipball/bfe29ead7e7b1cc9ce74c6a40d06ad1f96fced13",
+                "reference": "bfe29ead7e7b1cc9ce74c6a40d06ad1f96fced13",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "symfony/debug": "~2.8|~3.0|~4.0",
+                "symfony/polyfill-mbstring": "~1.0"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<3.4",
+                "symfony/process": "<3.3"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0"
+            },
+            "require-dev": {
+                "psr/log": "~1.0",
+                "symfony/config": "~3.3|~4.0",
+                "symfony/dependency-injection": "~3.4|~4.0",
+                "symfony/event-dispatcher": "~2.8|~3.0|~4.0",
+                "symfony/lock": "~3.4|~4.0",
+                "symfony/process": "~3.3|~4.0"
+            },
+            "suggest": {
+                "psr/log": "For using the console logger",
+                "symfony/event-dispatcher": "",
+                "symfony/lock": "",
+                "symfony/process": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Console\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Console Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-30T18:58:05+00:00"
+        },
+        {
+            "name": "symfony/debug",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/debug.git",
+                "reference": "518c6a00d0872da30bd06aee3ea59a0a5cf54d6d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/debug/zipball/518c6a00d0872da30bd06aee3ea59a0a5cf54d6d",
+                "reference": "518c6a00d0872da30bd06aee3ea59a0a5cf54d6d",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "psr/log": "~1.0"
+            },
+            "conflict": {
+                "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
+            },
+            "require-dev": {
+                "symfony/http-kernel": "~2.8|~3.0|~4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Debug\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Debug Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-22T18:25:20+00:00"
+        },
+        {
+            "name": "symfony/dependency-injection",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/dependency-injection.git",
+                "reference": "e39380b7104b0ec538a075ae919f00c7e5267bac"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/e39380b7104b0ec538a075ae919f00c7e5267bac",
+                "reference": "e39380b7104b0ec538a075ae919f00c7e5267bac",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "psr/container": "^1.0"
+            },
+            "conflict": {
+                "symfony/config": "<3.3.7",
+                "symfony/finder": "<3.3",
+                "symfony/proxy-manager-bridge": "<3.4",
+                "symfony/yaml": "<3.4"
+            },
+            "provide": {
+                "psr/container-implementation": "1.0"
+            },
+            "require-dev": {
+                "symfony/config": "~3.3|~4.0",
+                "symfony/expression-language": "~2.8|~3.0|~4.0",
+                "symfony/yaml": "~3.4|~4.0"
+            },
+            "suggest": {
+                "symfony/config": "",
+                "symfony/expression-language": "For using expressions in service container configuration",
+                "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required",
+                "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them",
+                "symfony/yaml": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\DependencyInjection\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony DependencyInjection Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-30T21:06:01+00:00"
+        },
+        {
+            "name": "symfony/event-dispatcher",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/event-dispatcher.git",
+                "reference": "14d978f8e8555f2de719c00eb65376be7d2e9081"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/14d978f8e8555f2de719c00eb65376be7d2e9081",
+                "reference": "14d978f8e8555f2de719c00eb65376be7d2e9081",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<3.3"
+            },
+            "require-dev": {
+                "psr/log": "~1.0",
+                "symfony/config": "~2.8|~3.0|~4.0",
+                "symfony/dependency-injection": "~3.3|~4.0",
+                "symfony/expression-language": "~2.8|~3.0|~4.0",
+                "symfony/stopwatch": "~2.8|~3.0|~4.0"
+            },
+            "suggest": {
+                "symfony/dependency-injection": "",
+                "symfony/http-kernel": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\EventDispatcher\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony EventDispatcher Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-05T15:06:23+00:00"
+        },
+        {
+            "name": "symfony/filesystem",
+            "version": "v4.4.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/filesystem.git",
+                "reference": "b27f491309db5757816db672b256ea2e03677d30"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/b27f491309db5757816db672b256ea2e03677d30",
+                "reference": "b27f491309db5757816db672b256ea2e03677d30",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "symfony/polyfill-ctype": "~1.8"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Filesystem\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Filesystem Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-30T18:50:54+00:00"
+        },
+        {
+            "name": "symfony/finder",
+            "version": "v4.4.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/finder.git",
+                "reference": "5729f943f9854c5781984ed4907bbb817735776b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/5729f943f9854c5781984ed4907bbb817735776b",
+                "reference": "5729f943f9854c5781984ed4907bbb817735776b",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Finder\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Finder Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-03-27T16:54:36+00:00"
+        },
+        {
+            "name": "symfony/http-foundation",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-foundation.git",
+                "reference": "fbd216d2304b1a3fe38d6392b04729c8dd356359"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/fbd216d2304b1a3fe38d6392b04729c8dd356359",
+                "reference": "fbd216d2304b1a3fe38d6392b04729c8dd356359",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "symfony/polyfill-mbstring": "~1.1",
+                "symfony/polyfill-php70": "~1.6"
+            },
+            "require-dev": {
+                "symfony/expression-language": "~2.8|~3.0|~4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\HttpFoundation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony HttpFoundation Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-16T13:15:54+00:00"
+        },
+        {
+            "name": "symfony/http-kernel",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-kernel.git",
+                "reference": "e4e4ed6c008c983645b4eee6b67d8f258cde54df"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/e4e4ed6c008c983645b4eee6b67d8f258cde54df",
+                "reference": "e4e4ed6c008c983645b4eee6b67d8f258cde54df",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "psr/log": "~1.0",
+                "symfony/debug": "^3.3.3|~4.0",
+                "symfony/event-dispatcher": "~2.8|~3.0|~4.0",
+                "symfony/http-foundation": "~3.4.12|~4.0.12|^4.1.1",
+                "symfony/polyfill-ctype": "~1.8",
+                "symfony/polyfill-php56": "~1.8"
+            },
+            "conflict": {
+                "symfony/config": "<2.8",
+                "symfony/dependency-injection": "<3.4.10|<4.0.10,>=4",
+                "symfony/var-dumper": "<3.3",
+                "twig/twig": "<1.34|<2.4,>=2"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0"
+            },
+            "require-dev": {
+                "psr/cache": "~1.0",
+                "symfony/browser-kit": "~2.8|~3.0|~4.0",
+                "symfony/class-loader": "~2.8|~3.0",
+                "symfony/config": "~2.8|~3.0|~4.0",
+                "symfony/console": "~2.8|~3.0|~4.0",
+                "symfony/css-selector": "~2.8|~3.0|~4.0",
+                "symfony/dependency-injection": "^3.4.10|^4.0.10",
+                "symfony/dom-crawler": "~2.8|~3.0|~4.0",
+                "symfony/expression-language": "~2.8|~3.0|~4.0",
+                "symfony/finder": "~2.8|~3.0|~4.0",
+                "symfony/process": "~2.8|~3.0|~4.0",
+                "symfony/routing": "~3.4|~4.0",
+                "symfony/stopwatch": "~2.8|~3.0|~4.0",
+                "symfony/templating": "~2.8|~3.0|~4.0",
+                "symfony/translation": "~2.8|~3.0|~4.0",
+                "symfony/var-dumper": "~3.3|~4.0"
+            },
+            "suggest": {
+                "symfony/browser-kit": "",
+                "symfony/config": "",
+                "symfony/console": "",
+                "symfony/dependency-injection": "",
+                "symfony/finder": "",
+                "symfony/var-dumper": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\HttpKernel\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony HttpKernel Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-31T05:14:17+00:00"
+        },
+        {
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.17.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-ctype.git",
+                "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
+                "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-ctype": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.17-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Ctype\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Gert de Pagter",
+                    "email": "BackEndTea@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for ctype functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "ctype",
+                "polyfill",
+                "portable"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-12T16:14:59+00:00"
+        },
+        {
+            "name": "symfony/polyfill-iconv",
+            "version": "v1.17.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-iconv.git",
+                "reference": "c4de7601eefbf25f9d47190abe07f79fe0a27424"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/c4de7601eefbf25f9d47190abe07f79fe0a27424",
+                "reference": "c4de7601eefbf25f9d47190abe07f79fe0a27424",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-iconv": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.17-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Iconv\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Iconv extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "iconv",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-12T16:47:27+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-idn",
+            "version": "v1.17.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-idn.git",
+                "reference": "3bff59ea7047e925be6b7f2059d60af31bb46d6a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/3bff59ea7047e925be6b7f2059d60af31bb46d6a",
+                "reference": "3bff59ea7047e925be6b7f2059d60af31bb46d6a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "symfony/polyfill-mbstring": "^1.3",
+                "symfony/polyfill-php72": "^1.10"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.17-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Idn\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Laurent Bassin",
+                    "email": "laurent@bassin.info"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "idn",
+                "intl",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-12T16:47:27+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.17.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "fa79b11539418b02fc5e1897267673ba2c19419c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fa79b11539418b02fc5e1897267673ba2c19419c",
+                "reference": "fa79b11539418b02fc5e1897267673ba2c19419c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.17-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-12T16:47:27+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php56",
+            "version": "v1.17.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php56.git",
+                "reference": "e3c8c138280cdfe4b81488441555583aa1984e23"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/e3c8c138280cdfe4b81488441555583aa1984e23",
+                "reference": "e3c8c138280cdfe4b81488441555583aa1984e23",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "symfony/polyfill-util": "~1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.17-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php56\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-12T16:47:27+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php70",
+            "version": "v1.17.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php70.git",
+                "reference": "82225c2d7d23d7e70515496d249c0152679b468e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/82225c2d7d23d7e70515496d249c0152679b468e",
+                "reference": "82225c2d7d23d7e70515496d249c0152679b468e",
+                "shasum": ""
+            },
+            "require": {
+                "paragonie/random_compat": "~1.0|~2.0|~9.99",
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.17-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php70\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-12T16:47:27+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php72",
+            "version": "v1.17.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php72.git",
+                "reference": "f048e612a3905f34931127360bdd2def19a5e582"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/f048e612a3905f34931127360bdd2def19a5e582",
+                "reference": "f048e612a3905f34931127360bdd2def19a5e582",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.17-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php72\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-12T16:47:27+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.17.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "5e30b2799bc1ad68f7feb62b60a73743589438dd"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/5e30b2799bc1ad68f7feb62b60a73743589438dd",
+                "reference": "5e30b2799bc1ad68f7feb62b60a73743589438dd",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0.8"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.17-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-12T16:47:27+00:00"
+        },
+        {
+            "name": "symfony/polyfill-util",
+            "version": "v1.17.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-util.git",
+                "reference": "4afb4110fc037752cf0ce9869f9ab8162c4e20d7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/4afb4110fc037752cf0ce9869f9ab8162c4e20d7",
+                "reference": "4afb4110fc037752cf0ce9869f9ab8162c4e20d7",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.17-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Util\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony utilities for portability of PHP codes",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compat",
+                "compatibility",
+                "polyfill",
+                "shim"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-12T16:14:59+00:00"
+        },
+        {
+            "name": "symfony/process",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/process.git",
+                "reference": "8a895f0c92a7c4b10db95139bcff71bdf66d4d21"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/process/zipball/8a895f0c92a7c4b10db95139bcff71bdf66d4d21",
+                "reference": "8a895f0c92a7c4b10db95139bcff71bdf66d4d21",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Process\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Process Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-23T17:05:51+00:00"
+        },
+        {
+            "name": "symfony/psr-http-message-bridge",
+            "version": "v1.1.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/psr-http-message-bridge.git",
+                "reference": "a33352af16f78a5ff4f9d90811536abf210df12b"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/a33352af16f78a5ff4f9d90811536abf210df12b",
+                "reference": "a33352af16f78a5ff4f9d90811536abf210df12b",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.3 || ^7.0",
+                "psr/http-message": "^1.0",
+                "symfony/http-foundation": "^2.3.42 || ^3.4 || ^4.0"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "^3.4 || ^4.0"
+            },
+            "suggest": {
+                "nyholm/psr7": "For a super lightweight PSR-7/17 implementation"
+            },
+            "type": "symfony-bridge",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Bridge\\PsrHttpMessage\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Symfony Community",
+                    "homepage": "http://symfony.com/contributors"
+                },
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "PSR HTTP message bridge",
+            "homepage": "http://symfony.com",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr-17",
+                "psr-7"
+            ],
+            "time": "2019-04-03T17:09:40+00:00"
+        },
+        {
+            "name": "symfony/routing",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/routing.git",
+                "reference": "e0d43b6f9417ad59ecaa8e2f799b79eef417387f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/e0d43b6f9417ad59ecaa8e2f799b79eef417387f",
+                "reference": "e0d43b6f9417ad59ecaa8e2f799b79eef417387f",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8"
+            },
+            "conflict": {
+                "symfony/config": "<3.3.1",
+                "symfony/dependency-injection": "<3.3",
+                "symfony/yaml": "<3.4"
+            },
+            "require-dev": {
+                "doctrine/annotations": "~1.0",
+                "psr/log": "~1.0",
+                "symfony/config": "^3.3.1|~4.0",
+                "symfony/dependency-injection": "~3.3|~4.0",
+                "symfony/expression-language": "~2.8|~3.0|~4.0",
+                "symfony/http-foundation": "~2.8|~3.0|~4.0",
+                "symfony/yaml": "~3.4|~4.0"
+            },
+            "suggest": {
+                "doctrine/annotations": "For using the annotation loader",
+                "symfony/config": "For using the all-in-one router or any loader",
+                "symfony/expression-language": "For using expression matching",
+                "symfony/http-foundation": "For using a Symfony Request object",
+                "symfony/yaml": "For using the YAML loader"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Routing\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Routing Component",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "router",
+                "routing",
+                "uri",
+                "url"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-30T19:50:06+00:00"
+        },
+        {
+            "name": "symfony/serializer",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/serializer.git",
+                "reference": "0db90db012b1b0a04fbb2d64ae9160871cad9d4f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/serializer/zipball/0db90db012b1b0a04fbb2d64ae9160871cad9d4f",
+                "reference": "0db90db012b1b0a04fbb2d64ae9160871cad9d4f",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "symfony/polyfill-ctype": "~1.8"
+            },
+            "conflict": {
+                "phpdocumentor/type-resolver": "<0.2.1",
+                "symfony/dependency-injection": "<3.2",
+                "symfony/property-access": ">=3.0,<3.0.4|>=2.8,<2.8.4",
+                "symfony/property-info": "<3.1",
+                "symfony/yaml": "<3.4"
+            },
+            "require-dev": {
+                "doctrine/annotations": "~1.0",
+                "doctrine/cache": "~1.0",
+                "phpdocumentor/reflection-docblock": "^3.0|^4.0",
+                "symfony/cache": "~3.1|~4.0",
+                "symfony/config": "~2.8|~3.0|~4.0",
+                "symfony/dependency-injection": "~3.2|~4.0",
+                "symfony/http-foundation": "~2.8|~3.0|~4.0",
+                "symfony/property-access": "~2.8|~3.0|~4.0",
+                "symfony/property-info": "^3.4.13|~4.0",
+                "symfony/yaml": "~3.4|~4.0"
+            },
+            "suggest": {
+                "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.",
+                "doctrine/cache": "For using the default cached annotation reader and metadata cache.",
+                "psr/cache-implementation": "For using the metadata cache.",
+                "symfony/config": "For using the XML mapping loader.",
+                "symfony/http-foundation": "For using a MIME type guesser within the DataUriNormalizer.",
+                "symfony/property-access": "For using the ObjectNormalizer.",
+                "symfony/property-info": "To deserialize relations.",
+                "symfony/yaml": "For using the default YAML mapping loader."
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Serializer\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Serializer Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-30T18:58:05+00:00"
+        },
+        {
+            "name": "symfony/translation",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/translation.git",
+                "reference": "b0cd62ef0ff7ec31b67d78d7fc818e2bda4e844f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/b0cd62ef0ff7ec31b67d78d7fc818e2bda4e844f",
+                "reference": "b0cd62ef0ff7ec31b67d78d7fc818e2bda4e844f",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "symfony/polyfill-mbstring": "~1.0"
+            },
+            "conflict": {
+                "symfony/config": "<2.8",
+                "symfony/dependency-injection": "<3.4",
+                "symfony/yaml": "<3.4"
+            },
+            "require-dev": {
+                "psr/log": "~1.0",
+                "symfony/config": "~2.8|~3.0|~4.0",
+                "symfony/dependency-injection": "~3.4|~4.0",
+                "symfony/finder": "~2.8|~3.0|~4.0",
+                "symfony/http-kernel": "~3.4|~4.0",
+                "symfony/intl": "^2.8.18|^3.2.5|~4.0",
+                "symfony/var-dumper": "~3.4|~4.0",
+                "symfony/yaml": "~3.4|~4.0"
+            },
+            "suggest": {
+                "psr/log-implementation": "To use logging capability in translator",
+                "symfony/config": "",
+                "symfony/yaml": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Translation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Translation Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-30T18:58:05+00:00"
+        },
+        {
+            "name": "symfony/validator",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/validator.git",
+                "reference": "5fb88120a11a75e17b602103a893dd8b27804529"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/validator/zipball/5fb88120a11a75e17b602103a893dd8b27804529",
+                "reference": "5fb88120a11a75e17b602103a893dd8b27804529",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "symfony/polyfill-ctype": "~1.8",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/translation": "~2.8|~3.0|~4.0"
+            },
+            "conflict": {
+                "doctrine/lexer": "<1.0.2",
+                "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
+                "symfony/dependency-injection": "<3.3",
+                "symfony/http-kernel": "<3.3.5",
+                "symfony/yaml": "<3.4"
+            },
+            "require-dev": {
+                "doctrine/annotations": "~1.7",
+                "doctrine/cache": "~1.0",
+                "egulias/email-validator": "^2.1.10",
+                "symfony/cache": "~3.1|~4.0",
+                "symfony/config": "~2.8|~3.0|~4.0",
+                "symfony/dependency-injection": "~3.3|~4.0",
+                "symfony/expression-language": "~2.8|~3.0|~4.0",
+                "symfony/http-foundation": "~2.8|~3.0|~4.0",
+                "symfony/http-kernel": "^3.3.5|~4.0",
+                "symfony/intl": "^2.8.18|^3.2.5|~4.0",
+                "symfony/property-access": "~2.8|~3.0|~4.0",
+                "symfony/var-dumper": "~3.3|~4.0",
+                "symfony/yaml": "~3.4|~4.0"
+            },
+            "suggest": {
+                "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.",
+                "doctrine/cache": "For using the default cached annotation reader and metadata cache.",
+                "egulias/email-validator": "Strict (RFC compliant) email validation",
+                "psr/cache-implementation": "For using the metadata cache.",
+                "symfony/config": "",
+                "symfony/expression-language": "For using the Expression validator",
+                "symfony/http-foundation": "",
+                "symfony/intl": "",
+                "symfony/property-access": "For accessing properties within comparison constraints",
+                "symfony/yaml": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Validator\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Validator Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-30T18:43:38+00:00"
+        },
+        {
+            "name": "symfony/var-dumper",
+            "version": "v4.4.9",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/var-dumper.git",
+                "reference": "56b3aa5eab0ac6720dcd559fd1d590ce301594ac"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/56b3aa5eab0ac6720dcd559fd1d590ce301594ac",
+                "reference": "56b3aa5eab0ac6720dcd559fd1d590ce301594ac",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/polyfill-php72": "~1.5",
+                "symfony/polyfill-php80": "^1.15"
+            },
+            "conflict": {
+                "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
+                "symfony/console": "<3.4"
+            },
+            "require-dev": {
+                "ext-iconv": "*",
+                "symfony/console": "^3.4|^4.0|^5.0",
+                "symfony/process": "^4.4|^5.0",
+                "twig/twig": "^1.34|^2.4|^3.0"
+            },
+            "suggest": {
+                "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
+                "ext-intl": "To show region name in time zone dump",
+                "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
+            },
+            "bin": [
+                "Resources/bin/var-dump-server"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.4-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "Resources/functions/dump.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Component\\VarDumper\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony mechanism for exploring and dumping PHP variables",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "debug",
+                "dump"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-30T20:06:45+00:00"
+        },
+        {
+            "name": "symfony/yaml",
+            "version": "v3.4.41",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/yaml.git",
+                "reference": "7233ac2bfdde24d672f5305f2b3f6b5d741ef8eb"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/7233ac2bfdde24d672f5305f2b3f6b5d741ef8eb",
+                "reference": "7233ac2bfdde24d672f5305f2b3f6b5d741ef8eb",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "symfony/polyfill-ctype": "~1.8"
+            },
+            "conflict": {
+                "symfony/console": "<3.4"
+            },
+            "require-dev": {
+                "symfony/console": "~3.4|~4.0"
+            },
+            "suggest": {
+                "symfony/console": "For validating YAML files using the lint command"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Yaml\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Yaml Component",
+            "homepage": "https://symfony.com",
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-11T07:51:54+00:00"
+        },
+        {
+            "name": "twig/twig",
+            "version": "v1.42.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/twigphp/Twig.git",
+                "reference": "87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/twigphp/Twig/zipball/87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e",
+                "reference": "87b2ea9d8f6fd014d0621ca089bb1b3769ea3f8e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5.0",
+                "symfony/polyfill-ctype": "^1.8"
+            },
+            "require-dev": {
+                "psr/container": "^1.0",
+                "symfony/phpunit-bridge": "^4.4|^5.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.42-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Twig_": "lib/"
+                },
+                "psr-4": {
+                    "Twig\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com",
+                    "homepage": "http://fabien.potencier.org",
+                    "role": "Lead Developer"
+                },
+                {
+                    "name": "Twig Team",
+                    "role": "Contributors"
+                },
+                {
+                    "name": "Armin Ronacher",
+                    "email": "armin.ronacher@active-4.com",
+                    "role": "Project Founder"
+                }
+            ],
+            "description": "Twig, the flexible, fast, and secure template language for PHP",
+            "homepage": "https://twig.symfony.com",
+            "keywords": [
+                "templating"
+            ],
+            "time": "2020-02-11T05:59:23+00:00"
+        },
+        {
+            "name": "typo3/phar-stream-wrapper",
+            "version": "v3.1.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/TYPO3/phar-stream-wrapper.git",
+                "reference": "e0c1b495cfac064f4f5c4bcb6bf67bb7f345ed04"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/TYPO3/phar-stream-wrapper/zipball/e0c1b495cfac064f4f5c4bcb6bf67bb7f345ed04",
+                "reference": "e0c1b495cfac064f4f5c4bcb6bf67bb7f345ed04",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "php": "^7.0"
+            },
+            "require-dev": {
+                "ext-xdebug": "*",
+                "phpunit/phpunit": "^6.5"
+            },
+            "suggest": {
+                "ext-fileinfo": "For PHP builtin file type guessing, otherwise uses internal processing"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "v3.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "TYPO3\\PharStreamWrapper\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Interceptors for PHP's native phar:// stream handling",
+            "homepage": "https://typo3.org/",
+            "keywords": [
+                "phar",
+                "php",
+                "security",
+                "stream-wrapper"
+            ],
+            "time": "2019-12-10T11:53:27+00:00"
+        },
+        {
+            "name": "webflo/drupal-finder",
+            "version": "1.2.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webflo/drupal-finder.git",
+                "reference": "123e248e14ee8dd3fbe89fb5a733a6cf91f5820e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webflo/drupal-finder/zipball/123e248e14ee8dd3fbe89fb5a733a6cf91f5820e",
+                "reference": "123e248e14ee8dd3fbe89fb5a733a6cf91f5820e",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*"
+            },
+            "require-dev": {
+                "mikey179/vfsstream": "^1.6",
+                "phpunit/phpunit": "^4.8"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/DrupalFinder.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "GPL-2.0+"
+            ],
+            "authors": [
+                {
+                    "name": "Florian Weber",
+                    "email": "florian@webflo.org"
+                }
+            ],
+            "description": "Helper class to locate a Drupal installation from a given path.",
+            "time": "2019-08-02T08:06:18+00:00"
+        },
+        {
+            "name": "webmozart/assert",
+            "version": "1.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webmozart/assert.git",
+                "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webmozart/assert/zipball/ab2cb0b3b559010b75981b1bdce728da3ee90ad6",
+                "reference": "ab2cb0b3b559010b75981b1bdce728da3ee90ad6",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.3 || ^7.0",
+                "symfony/polyfill-ctype": "^1.8"
+            },
+            "conflict": {
+                "vimeo/psalm": "<3.9.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.36 || ^7.5.13"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Webmozart\\Assert\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                }
+            ],
+            "description": "Assertions to validate method input/output with nice error messages.",
+            "keywords": [
+                "assert",
+                "check",
+                "validate"
+            ],
+            "time": "2020-04-18T12:12:48+00:00"
+        },
+        {
+            "name": "webmozart/path-util",
+            "version": "2.3.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webmozart/path-util.git",
+                "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725",
+                "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3",
+                "webmozart/assert": "~1.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.6",
+                "sebastian/version": "^1.0.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Webmozart\\PathUtil\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                }
+            ],
+            "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.",
+            "time": "2015-12-17T08:42:14+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "dev",
+    "stability-flags": [],
+    "prefer-stable": true,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": [],
+    "plugin-api-version": "1.1.0"
+}

+ 40 - 0
web/.csslintrc

@@ -0,0 +1,40 @@
+--errors=box-model,
+         display-property-grouping,
+         duplicate-background-images,
+         duplicate-properties,
+         empty-rules,
+         ids,
+         import,
+         important,
+         known-properties,
+         outline-none,
+         overqualified-elements,
+         qualified-headings,
+         shorthand,
+         star-property-hack,
+         text-indent,
+         underscore-property-hack,
+         unique-headings,
+         unqualified-attributes,
+         vendor-prefix,
+         zero-units
+--ignore=adjoining-classes,
+         box-sizing,
+         bulletproof-font-face,
+         compatible-vendor-prefixes,
+         errors,
+         fallback-colors,
+         floats,
+         font-faces,
+         font-sizes,
+         gradients,
+         import-ie-limit,
+         order-alphabetical,
+         regex-selectors,
+         rules-count,
+         selector-max,
+         selector-max-approaching,
+         selector-newline,
+         universal-selector
+--exclude-list=core/assets,
+               vendor

+ 8 - 0
web/.eslintignore

@@ -0,0 +1,8 @@
+core/**/*
+vendor/**/*
+sites/**/files/**/*
+libraries/**/*
+sites/**/libraries/**/*
+profiles/**/libraries/**/*
+**/js_test_files/**/*
+**/node_modules/**/*

+ 3 - 0
web/.eslintrc.json

@@ -0,0 +1,3 @@
+{
+  "extends": "./core/.eslintrc.json"
+}

+ 65 - 0
web/.ht.router.php

@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ * Router script for the built-in PHP web server.
+ *
+ * The built-in web server should only be used for development and testing as it
+ * has a number of limitations that makes running Drupal on it highly insecure
+ * and somewhat limited.
+ *
+ * Note that:
+ * - The server is single-threaded, any requests made during the execution of
+ *   the main request will hang until the main request has been completed.
+ * - The web server does not enforce any of the settings in .htaccess in
+ *   particular a remote user will be able to download files that normally would
+ *   be protected from direct access such as .module files.
+ *
+ * The router script is needed to work around a bug in PHP, see
+ * https://bugs.php.net/bug.php?id=61286.
+ *
+ * Usage:
+ * php -S localhost:8888 .ht.router.php
+ *
+ * @see http://php.net/manual/en/features.commandline.webserver.php
+ */
+
+$url = parse_url($_SERVER['REQUEST_URI']);
+if (file_exists(__DIR__ . $url['path'])) {
+  // Serve the requested resource as-is.
+  return FALSE;
+}
+
+// Work around the PHP bug.
+$path = $url['path'];
+$script = 'index.php';
+if (strpos($path, '.php') !== FALSE) {
+  // Work backwards through the path to check if a script exists. Otherwise
+  // fallback to index.php.
+  do {
+    $path = dirname($path);
+    if (preg_match('/\.php$/', $path) && is_file(__DIR__ . $path)) {
+      // Discovered that the path contains an existing PHP file. Use that as the
+      // script to include.
+      $script = ltrim($path, '/');
+      break;
+    }
+  } while ($path !== '/' && $path !== '.');
+}
+
+// Update $_SERVER variables to point to the correct index-file.
+$index_file_absolute = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $script;
+$index_file_relative = DIRECTORY_SEPARATOR . $script;
+
+// SCRIPT_FILENAME will point to the router script itself, it should point to
+// the full path of index.php.
+$_SERVER['SCRIPT_FILENAME'] = $index_file_absolute;
+
+// SCRIPT_NAME and PHP_SELF will either point to index.php or contain the full
+// virtual path being requested depending on the URL being requested. They
+// should always point to index.php relative to document root.
+$_SERVER['SCRIPT_NAME'] = $index_file_relative;
+$_SERVER['PHP_SELF'] = $index_file_relative;
+
+// Require the script and let core take over.
+require $_SERVER['SCRIPT_FILENAME'];

+ 182 - 0
web/.htaccess

@@ -0,0 +1,182 @@
+#
+# Apache/PHP/Drupal settings:
+#
+
+# Protect files and directories from prying eyes.
+<FilesMatch "\.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock)|web\.config)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$">
+  <IfModule mod_authz_core.c>
+    Require all denied
+  </IfModule>
+  <IfModule !mod_authz_core.c>
+    Order allow,deny
+  </IfModule>
+</FilesMatch>
+
+# Don't show directory listings for URLs which map to a directory.
+Options -Indexes
+
+# Set the default handler.
+DirectoryIndex index.php index.html index.htm
+
+# Add correct encoding for SVGZ.
+AddType image/svg+xml svg svgz
+AddEncoding gzip svgz
+
+# Most of the following PHP settings cannot be changed at runtime. See
+# sites/default/default.settings.php and
+# Drupal\Core\DrupalKernel::bootEnvironment() for settings that can be
+# changed at runtime.
+
+# PHP 7, Apache 1 and 2.
+<IfModule mod_php7.c>
+  php_value assert.active                   0
+</IfModule>
+
+# Requires mod_expires to be enabled.
+<IfModule mod_expires.c>
+  # Enable expirations.
+  ExpiresActive On
+
+  # Cache all files for 2 weeks after access (A).
+  ExpiresDefault A1209600
+
+  <FilesMatch \.php$>
+    # Do not allow PHP scripts to be cached unless they explicitly send cache
+    # headers themselves. Otherwise all scripts would have to overwrite the
+    # headers set by mod_expires if they want another caching behavior. This may
+    # fail if an error occurs early in the bootstrap process, and it may cause
+    # problems if a non-Drupal PHP file is installed in a subdirectory.
+    ExpiresActive Off
+  </FilesMatch>
+</IfModule>
+
+# Set a fallback resource if mod_rewrite is not enabled. This allows Drupal to
+# work without clean URLs. This requires Apache version >= 2.2.16. If Drupal is
+# not accessed by the top level URL (i.e.: http://example.com/drupal/ instead of
+# http://example.com/), the path to index.php will need to be adjusted.
+<IfModule !mod_rewrite.c>
+  FallbackResource /index.php
+</IfModule>
+
+# Various rewrite rules.
+<IfModule mod_rewrite.c>
+  RewriteEngine on
+
+  # Set "protossl" to "s" if we were accessed via https://.  This is used later
+  # if you enable "www." stripping or enforcement, in order to ensure that
+  # you don't bounce between http and https.
+  RewriteRule ^ - [E=protossl]
+  RewriteCond %{HTTPS} on
+  RewriteRule ^ - [E=protossl:s]
+
+  # Make sure Authorization HTTP header is available to PHP
+  # even when running as CGI or FastCGI.
+  RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+
+  # Block access to "hidden" directories whose names begin with a period. This
+  # includes directories used by version control systems such as Subversion or
+  # Git to store control files. Files whose names begin with a period, as well
+  # as the control files used by CVS, are protected by the FilesMatch directive
+  # above.
+  #
+  # NOTE: This only works when mod_rewrite is loaded. Without mod_rewrite, it is
+  # not possible to block access to entire directories from .htaccess because
+  # <DirectoryMatch> is not allowed here.
+  #
+  # If you do not have mod_rewrite installed, you should remove these
+  # directories from your webroot or otherwise protect them from being
+  # downloaded.
+  RewriteRule "/\.|^\.(?!well-known/)" - [F]
+
+  # If your site can be accessed both with and without the 'www.' prefix, you
+  # can use one of the following settings to redirect users to your preferred
+  # URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
+  #
+  # To redirect all users to access the site WITH the 'www.' prefix,
+  # (http://example.com/foo will be redirected to http://www.example.com/foo)
+  # uncomment the following:
+  # RewriteCond %{HTTP_HOST} .
+  # RewriteCond %{HTTP_HOST} !^www\. [NC]
+  # RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+  #
+  # To redirect all users to access the site WITHOUT the 'www.' prefix,
+  # (http://www.example.com/foo will be redirected to http://example.com/foo)
+  # uncomment the following:
+  # RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
+  # RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301]
+
+  # Modify the RewriteBase if you are using Drupal in a subdirectory or in a
+  # VirtualDocumentRoot and the rewrite rules are not working properly.
+  # For example if your site is at http://example.com/drupal uncomment and
+  # modify the following line:
+  # RewriteBase /drupal
+  #
+  # If your site is running in a VirtualDocumentRoot at http://example.com/,
+  # uncomment the following line:
+  # RewriteBase /
+
+  # Redirect common PHP files to their new locations.
+  RewriteCond %{REQUEST_URI} ^(.*)?/(install.php) [OR]
+  RewriteCond %{REQUEST_URI} ^(.*)?/(rebuild.php)
+  RewriteCond %{REQUEST_URI} !core
+  RewriteRule ^ %1/core/%2 [L,QSA,R=301]
+
+  # Rewrite install.php during installation to see if mod_rewrite is working
+  RewriteRule ^core/install.php core/install.php?rewrite=ok [QSA,L]
+
+  # Pass all requests not referring directly to files in the filesystem to
+  # index.php.
+  RewriteCond %{REQUEST_FILENAME} !-f
+  RewriteCond %{REQUEST_FILENAME} !-d
+  RewriteCond %{REQUEST_URI} !=/favicon.ico
+  RewriteRule ^ index.php [L]
+
+  # For security reasons, deny access to other PHP files on public sites.
+  # Note: The following URI conditions are not anchored at the start (^),
+  # because Drupal may be located in a subdirectory. To further improve
+  # security, you can replace '!/' with '!^/'.
+  # Allow access to PHP files in /core (like authorize.php or install.php):
+  RewriteCond %{REQUEST_URI} !/core/[^/]*\.php$
+  # Allow access to test-specific PHP files:
+  RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?.php
+  # Allow access to Statistics module's custom front controller.
+  # Copy and adapt this rule to directly execute PHP files in contributed or
+  # custom modules or to run another PHP application in the same directory.
+  RewriteCond %{REQUEST_URI} !/core/modules/statistics/statistics.php$
+  # Deny access to any other PHP files that do not match the rules above.
+  # Specifically, disallow autoload.php from being served directly.
+  RewriteRule "^(.+/.*|autoload)\.php($|/)" - [F]
+
+  # Rules to correctly serve gzip compressed CSS and JS files.
+  # Requires both mod_rewrite and mod_headers to be enabled.
+  <IfModule mod_headers.c>
+    # Serve gzip compressed CSS files if they exist and the client accepts gzip.
+    RewriteCond %{HTTP:Accept-encoding} gzip
+    RewriteCond %{REQUEST_FILENAME}\.gz -s
+    RewriteRule ^(.*)\.css $1\.css\.gz [QSA]
+
+    # Serve gzip compressed JS files if they exist and the client accepts gzip.
+    RewriteCond %{HTTP:Accept-encoding} gzip
+    RewriteCond %{REQUEST_FILENAME}\.gz -s
+    RewriteRule ^(.*)\.js $1\.js\.gz [QSA]
+
+    # Serve correct content types, and prevent double compression.
+    RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1,E=no-brotli:1]
+    RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1,E=no-brotli:1]
+
+    <FilesMatch "(\.js\.gz|\.css\.gz)$">
+      # Serve correct encoding type.
+      Header set Content-Encoding gzip
+      # Force proxies to cache gzipped & non-gzipped css/js files separately.
+      Header append Vary Accept-Encoding
+    </FilesMatch>
+  </IfModule>
+</IfModule>
+
+# Various header fixes.
+<IfModule mod_headers.c>
+  # Disable content sniffing, since it's an attack vector.
+  Header always set X-Content-Type-Options nosniff
+  # Disable Proxy header, since it's an attack vector.
+  RequestHeader unset Proxy
+</IfModule>

+ 3 - 0
web/INSTALL.txt

@@ -0,0 +1,3 @@
+
+Please read core/INSTALL.txt for detailed installation instructions for your
+Drupal web site.

+ 143 - 0
web/README.txt

@@ -0,0 +1,143 @@
+
+CONTENTS OF THIS FILE
+---------------------
+
+ * About Drupal
+ * Configuration and features
+ * Installation profiles
+ * Appearance
+ * Developing for Drupal
+ * More information
+
+
+ABOUT DRUPAL
+------------
+
+Drupal is an open source content management platform supporting a variety of
+websites ranging from personal weblogs to large community-driven websites. For
+more information, see the Drupal website at https://www.drupal.org, and join
+the Drupal community at https://www.drupal.org/community.
+
+Legal information about Drupal:
+ * Know your rights when using Drupal:
+   See LICENSE.txt in the "core" directory.
+ * Learn about the Drupal trademark and logo policy:
+   https://www.drupal.com/trademark
+
+
+CONFIGURATION AND FEATURES
+--------------------------
+
+Drupal core (what you get when you download and extract a drupal-x.y.tar.gz or
+drupal-x.y.zip file from https://www.drupal.org/project/drupal) has what you
+need to get started with your website. It includes several modules (extensions
+that add functionality) for common website features, such as managing content,
+user accounts, image uploading, and search. Core comes with many options that
+allow site-specific configuration. In addition to the core modules, there are
+thousands of contributed modules (for functionality not included with Drupal
+core) available for download.
+
+More about configuration:
+ * Install, update, and maintain Drupal:
+   See INSTALL.txt and UPDATE.txt in the "core" directory.
+ * Learn about how to use Drupal to create your site:
+   https://www.drupal.org/documentation
+ * Follow best practices:
+   https://www.drupal.org/best-practices
+ * Download contributed modules to /modules to extend Drupal's functionality:
+   https://www.drupal.org/project/modules
+ * See also: "Developing for Drupal" for writing your own modules, below.
+
+
+INSTALLATION PROFILES
+---------------------
+
+Installation profiles define additional steps (such as enabling modules,
+defining content types, etc.) that run after the base installation provided
+by core when Drupal is first installed. There are two basic installation
+profiles provided with Drupal core.
+
+Installation profiles from the Drupal community modify the installation process
+to provide a website for a specific use case, such as a CMS for media
+publishers, a web-based project tracking tool, or a full-fledged CRM for
+non-profit organizations raising money and accepting donations. They can be
+distributed as bare installation profiles or as "distributions". Distributions
+include Drupal core, the installation profile, and all other required
+extensions, such as contributed and custom modules, themes, and third-party
+libraries. Bare installation profiles require you to download Drupal Core and
+the required extensions separately; place the downloaded profile in the
+/profiles directory before you start the installation process.
+
+More about installation profiles and distributions:
+ * Read about the difference between installation profiles and distributions:
+   https://www.drupal.org/docs/8/distributions/creating-distributions
+ * Download contributed installation profiles and distributions:
+   https://www.drupal.org/project/distributions
+ * Develop your own installation profile or distribution:
+   https://www.drupal.org/docs/8/creating-distributions
+
+
+APPEARANCE
+----------
+
+In Drupal, the appearance of your site is set by the theme (themes are
+extensions that set fonts, colors, and layout). Drupal core comes with several
+themes. More themes are available for download, and you can also create your own
+custom theme.
+
+More about themes:
+ * Download contributed themes to /themes to modify Drupal's appearance:
+   https://www.drupal.org/project/themes
+ * Develop your own theme:
+   https://www.drupal.org/docs/8/theming
+
+
+DEVELOPING FOR DRUPAL
+---------------------
+
+Drupal contains an extensive API that allows you to add to and modify the
+functionality of your site. The API consists of "hooks", which allow modules to
+react to system events and customize Drupal's behavior, and functions that
+standardize common operations such as database queries and form generation. The
+flexible hook architecture means that you should never need to directly modify
+the files that come with Drupal core to achieve the functionality you want;
+instead, functionality modifications take the form of modules.
+
+When you need new functionality for your Drupal site, search for existing
+contributed modules. If you find a module that matches except for a bug or an
+additional needed feature, change the module and contribute your improvements
+back to the project in the form of a "patch". Create new custom modules only
+when nothing existing comes close to what you need.
+
+More about developing:
+ * Search for existing contributed modules:
+   https://www.drupal.org/project/modules
+ * Contribute a patch:
+   https://www.drupal.org/patch/submit
+ * Develop your own module:
+   https://www.drupal.org/developing/modules
+ * Follow programming best practices:
+   https://www.drupal.org/developing/best-practices
+ * Refer to the API documentation:
+   https://api.drupal.org/api/drupal/8
+ * Learn from documented Drupal API examples:
+   https://www.drupal.org/project/examples
+
+
+MORE INFORMATION
+----------------
+
+ * See the Drupal.org online documentation:
+   https://www.drupal.org/documentation
+
+ * For a list of security announcements, see the "Security advisories" page at
+   https://www.drupal.org/security (available as an RSS feed). This page also
+   describes how to subscribe to these announcements via email.
+
+ * For information about the Drupal security process, or to find out how to
+   report a potential security issue to the Drupal security team, see the
+   "Security team" page at https://www.drupal.org/security-team
+
+ * For information about the wide range of available support options, visit
+   https://www.drupal.org and click on Community and Support in the top or
+   bottom navigation.

+ 16 - 0
web/autoload.php

@@ -0,0 +1,16 @@
+<?php
+
+/**
+ * @file
+ * Includes the autoloader created by Composer.
+ *
+ * This file was generated by drupal-scaffold.
+ *.
+ * @see composer.json
+ * @see index.php
+ * @see core/install.php
+ * @see core/rebuild.php
+ * @see core/modules/statistics/statistics.php
+ */
+
+return require __DIR__ . '/../vendor/autoload.php';

+ 77 - 0
web/core/.env.example

@@ -0,0 +1,77 @@
+# This is a dotenv file used by JavaScript tasks.
+# Copy this to '.env' to override.
+
+#############################
+# General Test Environment #
+#############################
+# This is the URL that Drupal can be accessed by. You don't need an installed
+# site here, just make sure you can at least access the installer screen. If you
+# don't already have one running, e.g. Apache, you can use PHP's built-in web
+# server by running the following command in your Drupal root folder:
+# php -S localhost:8888 .ht.router.php
+# DRUPAL_TEST_BASE_URL=http://localhost:8888
+DRUPAL_TEST_BASE_URL=
+
+# Tests need to be executed with a user in the same group as the web server
+# user.
+#DRUPAL_TEST_WEBSERVER_USER=www-data
+
+# By default we use sqlite as database. Use
+# mysql://username:password@localhost/databasename#table_prefix for mysql.
+DRUPAL_TEST_DB_URL=sqlite://localhost/sites/default/files/db.sqlite
+
+#############
+# Webdriver #
+#############
+
+# If Chromedriver is running as a service elsewhere, set it here.
+# When using DRUPAL_TEST_CHROMEDRIVER_AUTOSTART leave this at the default settings.
+DRUPAL_TEST_WEBDRIVER_HOSTNAME=localhost
+DRUPAL_TEST_WEBDRIVER_PORT=9515
+
+# If using Selenium, override the path prefix here.
+# See http://nightwatchjs.org/gettingstarted#browser-drivers-setup
+#DRUPAL_TEST_WEBDRIVER_PATH_PREFIX=/wd/hub
+
+################
+# Chromedriver #
+################
+
+# Automatically start chromedriver for local development. Set to false when you
+# use your own webdriver or chromedriver setup.
+# Also set it to false when you use a different browser for testing.
+DRUPAL_TEST_CHROMEDRIVER_AUTOSTART=true
+
+# A list of arguments to pass to Chrome, separated by spaces
+# e.g. `--disable-gpu --headless --no-sandbox`.
+#DRUPAL_TEST_WEBDRIVER_CHROME_ARGS=
+
+##############
+# Nightwatch #
+##############
+
+# Nightwatch generates output files. Use this to specify the location where these
+# files need to be stored. The default location is ignored by git, if you modify
+# the location you will probably want to add this location to your .gitignore.
+DRUPAL_NIGHTWATCH_OUTPUT=reports/nightwatch
+
+# The path that Nightwatch searches for assumes the same directory structure as
+# when you download Drupal core. If you have Drupal installed into a docroot
+# folder, you can use the following folder structure to add integration tests
+# for your project, outside of tests specifically for custom modules/themes/profiles.
+#
+# .
+# ├── docroot
+# │   ├── core
+# ├── tests
+# │   ├── Nightwatch
+# │   │   ├── Tests
+# │   │   │   ├── myTest.js
+#
+# and then set DRUPAL_NIGHTWATCH_SEARCH_DIRECTORY=../
+#
+#DRUPAL_NIGHTWATCH_SEARCH_DIRECTORY=
+
+# Filter directories to look for tests. This uses minimatch syntax.
+# Separate folders with a comma.
+DRUPAL_NIGHTWATCH_IGNORE_DIRECTORIES=node_modules,vendor,.*,sites/*/files,sites/*/private,sites/simpletest

+ 8 - 0
web/core/.eslintignore

@@ -0,0 +1,8 @@
+assets/vendor/**/*
+node_modules/**/*
+**/js_test_files/**/*
+*.js
+!*.es6.js
+modules/locale/tests/locale_test.es6.js
+!tests/Drupal/Nightwatch/**/*.js
+misc/polyfills/object.assign.es6.js

+ 48 - 0
web/core/.eslintrc.json

@@ -0,0 +1,48 @@
+{
+  "extends": [
+    "airbnb",
+    "plugin:prettier/recommended"
+  ],
+  "root": true,
+  "env": {
+    "browser": true,
+    "es6": true,
+    "node": true
+  },
+  "globals": {
+    "Drupal": true,
+    "drupalSettings": true,
+    "drupalTranslations": true,
+    "domready": true,
+    "jQuery": true,
+    "_": true,
+    "matchMedia": true,
+    "Cookies": true,
+    "Backbone": true,
+    "Modernizr": true,
+    "Popper": true,
+    "Sortable": true,
+    "CKEDITOR": true
+  },
+  "rules": {
+    "prettier/prettier": "error",
+    "consistent-return": ["off"],
+    "no-underscore-dangle": ["off"],
+    "max-nested-callbacks": ["warn", 3],
+    "import/no-mutable-exports": ["warn"],
+    "no-plusplus": ["warn", {
+      "allowForLoopAfterthoughts": true
+    }],
+    "no-param-reassign": ["off"],
+    "no-prototype-builtins": ["off"],
+    "valid-jsdoc": ["warn", {
+      "prefer": {
+        "returns": "return",
+        "property": "prop"
+      },
+      "requireReturn": false
+    }],
+    "no-unused-vars": ["warn"],
+    "operator-linebreak": ["error", "after", { "overrides": { "?": "ignore", ":": "ignore" } }]
+  }
+}

+ 94 - 0
web/core/.eslintrc.legacy.json

@@ -0,0 +1,94 @@
+{
+  "extends": "eslint:recommended",
+  "root": true,
+  "env": {
+    "browser": true
+  },
+  "globals": {
+    "Drupal": true,
+    "drupalSettings": true,
+    "drupalTranslations": true,
+    "domready": true,
+    "jQuery": true,
+    "_": true,
+    "matchMedia": true,
+    "Backbone": true,
+    "Modernizr": true,
+    "CKEDITOR": true
+  },
+  "rules": {
+    "array-bracket-spacing": ["error", "never"],
+    "block-scoped-var": "error",
+    "brace-style": ["error", "stroustrup", {"allowSingleLine": true}],
+    "comma-dangle": ["error", "never"],
+    "comma-spacing": "error",
+    "comma-style": ["error", "last"],
+    "computed-property-spacing": ["error", "never"],
+    "curly": ["error", "all"],
+    "eol-last": "error",
+    "eqeqeq": ["error", "smart"],
+    "guard-for-in": "error",
+    "indent": ["error", 2, {"SwitchCase": 1}],
+    "key-spacing": ["error", {"beforeColon": false, "afterColon": true}],
+    "keyword-spacing": ["error", {"before": true, "after": true}],
+    "linebreak-style": ["error", "unix"],
+    "lines-around-comment": ["error", {"beforeBlockComment": true, "afterBlockComment": false}],
+    "new-parens": "error",
+    "no-array-constructor": "error",
+    "no-caller": "error",
+    "no-catch-shadow": "error",
+    "no-eval": "error",
+    "no-extend-native": "error",
+    "no-extra-bind": "error",
+    "no-extra-parens": ["error", "functions"],
+    "no-implied-eval": "error",
+    "no-iterator": "error",
+    "no-label-var": "error",
+    "no-labels": "error",
+    "no-lone-blocks": "error",
+    "no-loop-func": "error",
+    "no-multi-spaces": "error",
+    "no-multi-str": "error",
+    "no-native-reassign": "error",
+    "no-nested-ternary": "error",
+    "no-new-func": "error",
+    "no-new-object": "error",
+    "no-new-wrappers": "error",
+    "no-octal-escape": "error",
+    "no-process-exit": "error",
+    "no-proto": "error",
+    "no-return-assign": "error",
+    "no-script-url": "error",
+    "no-sequences": "error",
+    "no-shadow-restricted-names": "error",
+    "no-spaced-func": "error",
+    "no-trailing-spaces": "error",
+    "no-undef-init": "error",
+    "no-undefined": "error",
+    "no-unused-expressions": "error",
+    "no-unused-vars": ["error", {"vars": "all", "args": "none"}],
+    "no-with": "error",
+    "object-curly-spacing": ["error", "never"],
+    "one-var": ["error", "never"],
+    "quote-props": ["error", "consistent-as-needed"],
+    "quotes": ["error", "single", "avoid-escape"],
+    "semi": ["error", "always"],
+    "semi-spacing": ["error", {"before": false, "after": true}],
+    "space-before-blocks": ["error", "always"],
+    "space-before-function-paren": ["error", {"anonymous": "always", "named": "never"}],
+    "space-in-parens": ["error", "never"],
+    "space-infix-ops": "error",
+    "space-unary-ops": ["error", { "words": true, "nonwords": false }],
+    "spaced-comment": ["error", "always"],
+    "strict": ["error", "function"],
+    "yoda": ["error", "never"],
+    "max-nested-callbacks": ["warn", 3],
+    "valid-jsdoc": ["warn", {
+      "prefer": {
+        "returns": "return",
+        "property": "prop"
+      },
+      "requireReturn": false
+    }]
+  }
+}

+ 15 - 0
web/core/.eslintrc.passing.json

@@ -0,0 +1,15 @@
+{
+  "extends": "./.eslintrc.json",
+  "rules": {
+    "no-shadow": "off",
+    "no-new": "off",
+    "no-continue": "off",
+    "new-cap": "off",
+    "max-len": "off",
+    "default-case": "off",
+    "prefer-destructuring": "off",
+    "react/no-this-in-sfc": "off",
+    "react/destructuring-assignment": "off",
+    "import/named": "off"
+  }
+}

+ 21 - 0
web/core/.gitignore

@@ -0,0 +1,21 @@
+# Ignore node_modules folder created when installing core's JavaScript
+# dependencies.
+node_modules
+yarn-error.log
+
+# Ignore overrides of core's phpcs.xml.dist and phpunit.xml.dist.
+phpcs.xml
+phpunit.xml
+
+# Ignore package-lock.json that is automatically created when adding
+# dependencies by users of NPMv5.
+package-lock.json
+
+# Ignore test reports
+reports
+
+# Ignore local Nightwatch settings
+nightwatch.settings.json
+
+# Ignore dotenv
+.env

+ 1 - 0
web/core/.prettierignore

@@ -0,0 +1 @@
+modules/locale/tests/locale_test.es6.js

+ 6 - 0
web/core/.prettierrc.json

@@ -0,0 +1,6 @@
+{
+  "printWidth": 80,
+  "semi": true,
+  "singleQuote": true,
+  "trailingComma": "all"
+}

+ 2 - 0
web/core/.stylelintignore

@@ -0,0 +1,2 @@
+themes/claro/**/*.css
+!themes/claro/**/*.pcss.css

+ 446 - 0
web/core/.stylelintrc.json

@@ -0,0 +1,446 @@
+{
+  "extends": "stylelint-config-standard",
+  "plugins": [
+    "stylelint-no-browser-hacks/lib",
+    "stylelint-order"
+  ],
+  "rules": {
+    "comment-empty-line-before": null,
+    "function-linear-gradient-no-nonstandard-direction": null,
+    "function-whitespace-after": null,
+    "no-descending-specificity": null,
+    "no-duplicate-selectors": null,
+    "no-unknown-animations": true,
+    "media-feature-name-no-unknown": [true, {
+      "ignoreMediaFeatureNames": [
+        "prefers-reduced-motion",
+        "min--moz-device-pixel-ratio"
+      ]
+    }],
+    "number-leading-zero": "always",
+    "order/order": [
+      "custom-properties",
+      "dollar-variables",
+      {
+        "type": "at-rule",
+        "hasBlock": false
+      },
+      "declarations",
+      "rules",
+      {
+        "type": "at-rule",
+        "hasBlock": true
+      }
+    ],
+    "order/properties-order": [
+      "position",
+      "z-index",
+      "top",
+      "right",
+      "bottom",
+      "left",
+
+      "display",
+      "visibility",
+      "float",
+      "clear",
+      "overflow",
+      "overflow-x",
+      "overflow-y",
+      "-ms-overflow-x",
+      "-ms-overflow-y",
+      "-webkit-overflow-scrolling",
+      "clip",
+      "zoom",
+
+      "flex",
+      "flex-flow",
+      "flex-direction",
+      "flex-wrap",
+      "flex-basis",
+      "flex-grow",
+      "flex-shrink",
+      "flex-order",
+      "flex-pack",
+
+      "-ms-grid",
+      "grid",
+      "grid-area",
+      "grid-template",
+      "grid-template-areas",
+      "-ms-grid-rows",
+      "grid-template-rows",
+      "-ms-grid-columns",
+      "grid-template-columns",
+      "grid-row",
+      "-ms-grid-row",
+      "grid-row-start",
+      "grid-row-end",
+      "grid-column",
+      "-ms-grid-column",
+      "grid-column-start",
+      "grid-column-end",
+      "grid-auto-rows",
+      "grid-auto-columns",
+      "grid-auto-flow",
+      "grid-gap",
+      "grid-row-gap",
+      "grid-column-gap",
+      "-ms-grid-row-align",
+      "-ms-grid-column-align",
+
+      "place-content",
+      "place-items",
+      "align-content",
+      "align-items",
+      "align-self",
+      "justify-content",
+      "justify-items",
+      "justify-self",
+
+      "order",
+
+      "-webkit-box-sizing",
+      "-moz-box-sizing",
+      "box-sizing",
+      "width",
+      "min-width",
+      "max-width",
+      "height",
+      "min-height",
+      "max-height",
+      "margin",
+      "margin-top",
+      "margin-right",
+      "margin-bottom",
+      "margin-left",
+      "padding",
+      "padding-top",
+      "padding-right",
+      "padding-bottom",
+      "padding-left",
+
+      "table-layout",
+      "-webkit-columns",
+      "-moz-columns",
+      "columns",
+      "-webkit-column-span",
+      "-moz-column-span",
+      "column-span",
+      "-webkit-column-width",
+      "-moz-column-width",
+      "column-width",
+      "-webkit-column-count",
+      "-moz-column-count",
+      "column-count",
+      "-webkit-column-fill",
+      "-moz-column-fill",
+      "column-fill",
+      "-webkit-column-gap",
+      "-moz-column-gap",
+      "column-gap",
+      "-webkit-column-rule",
+      "-moz-column-rule",
+      "column-rule",
+      "-webkit-column-rule-width",
+      "-moz-column-rule-width",
+      "column-rule-width",
+      "-webkit-column-rule-style",
+      "-moz-column-rule-style",
+      "column-rule-style",
+      "-webkit-column-rule-color",
+      "-moz-column-rule-color",
+      "column-rule-color",
+      "empty-cells",
+      "caption-side",
+      "border-spacing",
+      "border-collapse",
+      "$counter-style",
+      "list-style",
+      "list-style-position",
+      "list-style-type",
+      "list-style-image",
+
+      "content",
+      "quotes",
+      "counter-reset",
+      "counter-increment",
+      "resize",
+      "cursor",
+      "-webkit-user-select",
+      "-moz-user-select",
+      "-ms-user-select",
+      "user-select",
+      "nav-index",
+      "nav-up",
+      "nav-right",
+      "nav-down",
+      "nav-left",
+      "-webkit-transition",
+      "-moz-transition",
+      "-ms-transition",
+      "-o-transition",
+      "transition",
+      "-webkit-transition-delay",
+      "-moz-transition-delay",
+      "-ms-transition-delay",
+      "-o-transition-delay",
+      "transition-delay",
+      "-webkit-transition-timing-function",
+      "-moz-transition-timing-function",
+      "-ms-transition-timing-function",
+      "-o-transition-timing-function",
+      "transition-timing-function",
+      "-webkit-transition-duration",
+      "-moz-transition-duration",
+      "-ms-transition-duration",
+      "-o-transition-duration",
+      "transition-duration",
+      "-webkit-transition-property",
+      "-moz-transition-property",
+      "-ms-transition-property",
+      "-o-transition-property",
+      "transition-property",
+      "-webkit-transform",
+      "-moz-transform",
+      "-ms-transform",
+      "-o-transform",
+      "transform",
+      "-webkit-transform-origin",
+      "-moz-transform-origin",
+      "-ms-transform-origin",
+      "-o-transform-origin",
+      "transform-origin",
+      "$keyframes",
+      "-webkit-animation",
+      "-moz-animation",
+      "-ms-animation",
+      "-o-animation",
+      "animation",
+      "-webkit-animation-name",
+      "-moz-animation-name",
+      "-ms-animation-name",
+      "-o-animation-name",
+      "animation-name",
+      "-webkit-animation-duration",
+      "-moz-animation-duration",
+      "-ms-animation-duration",
+      "-o-animation-duration",
+      "animation-duration",
+      "-webkit-animation-play-state",
+      "-moz-animation-play-state",
+      "-ms-animation-play-state",
+      "-o-animation-play-state",
+      "animation-play-state",
+      "-webkit-animation-timing-function",
+      "-moz-animation-timing-function",
+      "-ms-animation-timing-function",
+      "-o-animation-timing-function",
+      "animation-timing-function",
+      "-webkit-animation-delay",
+      "-moz-animation-delay",
+      "-ms-animation-delay",
+      "-o-animation-delay",
+      "animation-delay",
+      "-webkit-animation-iteration-count",
+      "-moz-animation-iteration-count",
+      "-ms-animation-iteration-count",
+      "-o-animation-iteration-count",
+      "animation-iteration-count",
+      "-webkit-animation-direction",
+      "-moz-animation-direction",
+      "-ms-animation-direction",
+      "-o-animation-direction",
+      "animation-direction",
+      "text-align",
+      "-webkit-text-align-last",
+      "-moz-text-align-last",
+      "-ms-text-align-last",
+      "text-align-last",
+      "vertical-align",
+      "white-space",
+      "text-decoration",
+      "text-emphasis",
+      "text-emphasis-color",
+      "text-emphasis-style",
+      "text-emphasis-position",
+      "text-indent",
+      "-ms-text-justify",
+      "text-justify",
+      "text-transform",
+      "letter-spacing",
+      "word-spacing",
+      "-ms-writing-mode",
+      "text-outline",
+      "text-transform",
+      "text-wrap",
+      "text-overflow",
+      "-ms-text-overflow",
+      "text-overflow-ellipsis",
+      "text-overflow-mode",
+      "-ms-word-wrap",
+      "word-wrap",
+      "word-break",
+      "-ms-word-break",
+      "-moz-tab-size",
+      "-o-tab-size",
+      "tab-size",
+      "-webkit-hyphens",
+      "-moz-hyphens",
+      "hyphens",
+      "pointer-events",
+      "direction",
+      "unicode-bidi",
+      "orphans",
+      "widows",
+      "object-fit",
+      "object-position",
+
+      "opacity",
+      "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity",
+      "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha",
+      "-webkit-filter",
+      "-ms-filter",
+      "filter",
+      "-ms-interpolation-mode",
+      "color",
+      "border",
+      "border-collapse",
+      "border-width",
+      "border-style",
+      "border-color",
+      "border-top",
+      "border-top-width",
+      "border-top-style",
+      "border-top-color",
+      "border-right",
+      "border-right-width",
+      "border-right-style",
+      "border-right-color",
+      "border-bottom",
+      "border-bottom-width",
+      "border-bottom-style",
+      "border-bottom-color",
+      "border-left",
+      "border-left-width",
+      "border-left-style",
+      "border-left-color",
+      "-webkit-border-radius",
+      "-moz-border-radius",
+      "border-radius",
+      "-webkit-border-top-left-radius",
+      "-moz-border-radius-topleft",
+      "border-top-left-radius",
+      "-webkit-border-top-right-radius",
+      "-moz-border-radius-topright",
+      "border-top-right-radius",
+      "-webkit-border-bottom-right-radius",
+      "-moz-border-radius-bottomright",
+      "border-bottom-right-radius",
+      "-webkit-border-bottom-left-radius",
+      "-moz-border-radius-bottomleft",
+      "border-bottom-left-radius",
+      "-webkit-border-image",
+      "-moz-border-image",
+      "-o-border-image",
+      "border-image",
+      "-webkit-border-image-source",
+      "-moz-border-image-source",
+      "-o-border-image-source",
+      "border-image-source",
+      "-webkit-border-image-slice",
+      "-moz-border-image-slice",
+      "-o-border-image-slice",
+      "border-image-slice",
+      "-webkit-border-image-width",
+      "-moz-border-image-width",
+      "-o-border-image-width",
+      "border-image-width",
+      "-webkit-border-image-outset",
+      "-moz-border-image-outset",
+      "-o-border-image-outset",
+      "border-image-outset",
+      "-webkit-border-image-repeat",
+      "-moz-border-image-repeat",
+      "-o-border-image-repeat",
+      "border-image-repeat",
+      "outline",
+      "outline-width",
+      "outline-style",
+      "outline-color",
+      "outline-offset",
+      "background",
+      "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader",
+      "background-color",
+      "background-image",
+      "background-repeat",
+      "background-attachment",
+      "background-position",
+      "background-position-x",
+      "-ms-background-position-x",
+      "background-position-y",
+      "-ms-background-position-y",
+      "-webkit-background-clip",
+      "-moz-background-clip",
+      "background-clip",
+      "background-origin",
+      "-webkit-background-size",
+      "-moz-background-size",
+      "-o-background-size",
+      "background-size",
+      "box-decoration-break",
+      "-webkit-box-shadow",
+      "-moz-box-shadow",
+      "box-shadow",
+      "filter:progid:DXImageTransform.Microsoft.gradient",
+      "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient",
+      "text-shadow",
+
+      "$font-face",
+      "font",
+      "font-family",
+      "src",
+      "$font-feature-values",
+      "$swash",
+      "$annotation",
+      "$ornaments",
+      "$stylistic",
+      "$styleset",
+      "$character-variant",
+      "font-variant-alternates",
+      "font-size",
+      "font-weight",
+      "font-style",
+      "font-variant",
+      "font-size-adjust",
+      "font-stretch",
+      "font-effect",
+      "font-emphasize",
+      "font-emphasize-position",
+      "font-emphasize-style",
+      "font-smooth",
+      "line-height"
+    ],
+    "plugin/no-browser-hacks": [true, {
+      "browsers": [
+        "ie >= 9",
+        "edge >= 13",
+        "firefox >= 5",
+        "opera >= 12",
+        "safari >= 5",
+        "chrome >= 56"
+      ]
+    }],
+    "property-no-unknown": null,
+    "rule-empty-line-before": null,
+    "selector-pseudo-element-colon-notation": null,
+    "shorthand-property-no-redundant-values": null,
+    "string-quotes": "double",
+    "unit-whitelist": ["deg", "em", "ex", "ms", "rem", "%", "s", "px", "vw", "vh"]
+  },
+  "ignoreFiles": [
+    "assets/vendor/**/*.css",
+    "tests/Drupal/Tests/Core/Asset/css_test_files/**/*.css",
+    "modules/media/css/plugins/drupalmedia/ckeditor.drupalmedia.css"
+  ]
+}

+ 8 - 0
web/core/CHANGELOG.txt

@@ -0,0 +1,8 @@
+New minor (feature) releases of Drupal are released every six months and
+patch (bugfix) releases are released every month. More information on the
+Drupal release cycle: https://www.drupal.org/core/release-cycle-overview
+
+* For a full list of fixes in the latest release, visit:
+ https://www.drupal.org/8/download
+* API change records for Drupal core:
+ https://www.drupal.org/list-changes/drupal

+ 59 - 0
web/core/COPYRIGHT.txt

@@ -0,0 +1,59 @@
+All Drupal code is Copyright 2001 - 2020 by the original authors.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at
+your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program as the file LICENSE.txt; if not, please see
+http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+
+Drupal is a registered trademark of Dries Buytaert.
+
+Drupal includes works under other copyright notices and distributed
+according to the terms of the GNU General Public License or a compatible
+license, including:
+
+Javascript
+
+  Farbtastic - Copyright (c) 2010 Matt Farina
+
+  HTML5 Shiv - Copyright (c) 2014  Alexander Farkas, Jonathan Neal, Paul Irish,
+    and John-David Dalton
+
+  jQuery - Copyright (c) 2011 John Resig
+
+  jQuery Bgiframe - Copyright (c) 2013 Brandon Aaron (http://brandonaaron.net)
+
+  jQuery BBQ - Copyright (c) 2010 "Cowboy" Ben Alman
+
+  jQuery Cookie - Copyright (c) 2014 Klaus Hartl
+
+  jQuery Form - Copyright (c) 2017 Kevin Morris
+
+  jQuery Globalize - Copyright (c) 2012 Software Freedom Conservancy, Inc.
+
+  jQuery Mousewheel - Copyright OpenJS Foundation and other contributors
+    (https://openjsf.org/)
+
+  jQuery Metadata - Copyright (c) 2006 John Resig, Yehuda Katz, Jörn Zaefferer,
+    Paul McLanahan
+
+  jQuery Once - Copyright (c) 2009 Konstantin Käfer
+
+  jQuery UI - Copyright (c) 2015 by the authors and other contributors
+    (http://jqueryui.com/about)
+
+  Sizzle.js - Copyright (c) 2016 JS Foundation and other contributors
+    (https://js.foundation)
+
+PHP
+
+  Drupal depends on numerous PHP Composer packages. All Composer packages
+    retain the copyright of the authors.

+ 45 - 0
web/core/INSTALL.mysql.txt

@@ -0,0 +1,45 @@
+
+CREATE THE MySQL DATABASE
+--------------------------
+
+This step is only necessary if you don't already have a database set up (e.g.,
+by your host). In the following examples, 'username' is an example MySQL user
+which has the CREATE and GRANT privileges. Use the appropriate user name for
+your system.
+
+First, you must create a new database for your Drupal site (here, 'databasename'
+is the name of the new database):
+
+  mysqladmin -u username -p create databasename
+
+MySQL will prompt for the 'username' database password and then create the
+initial database files. Next you must log in and set the access database rights:
+
+  mysql -u username -p
+
+Again, you will be asked for the 'username' database password. At the MySQL
+prompt, enter the following command:
+
+  GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER,
+  CREATE TEMPORARY TABLES ON databasename.*
+  TO 'username'@'localhost' IDENTIFIED BY 'password';
+
+where:
+
+ 'databasename' is the name of your database
+ 'username' is the username of your MySQL account
+ 'localhost' is the web server host where Drupal is installed
+ 'password' is the password required for that username
+
+Note: Unless the database user/host combination for your Drupal installation
+has all of the privileges listed above (except possibly CREATE TEMPORARY TABLES,
+which is currently only used by Drupal core automated tests and some
+contributed modules), you will not be able to install or run Drupal.
+
+If successful, MySQL will reply with:
+
+  Query OK, 0 rows affected
+
+If the InnoDB storage engine is available, it will be used for all database
+tables. InnoDB provides features over MyISAM such as transaction support,
+row-level locks, and consistent non-locking reads.

+ 44 - 0
web/core/INSTALL.pgsql.txt

@@ -0,0 +1,44 @@
+
+CREATE THE PostgreSQL DATABASE
+------------------------------
+
+Note that the database must be created with UTF-8 (Unicode) encoding.
+
+1. CREATE DATABASE USER
+
+   This step is only necessary if you don't already have a user set up (e.g., by
+   your host), or want to create a new user for use with Drupal only. The
+   following command creates a new user named 'username' and asks for a password
+   for that user:
+
+     createuser --pwprompt --encrypted --no-createrole --no-createdb username
+
+   If there are no errors, then the command was successful.
+
+2. CREATE DRUPAL DATABASE
+
+   This step is only necessary if you don't already have a database set up
+   (e.g., by your host) or want to create a new database for use with Drupal
+   only. The following command creates a new database named 'databasename',
+   which is owned by the previously created 'username':
+
+     createdb --encoding=UTF8 --owner=username databasename
+
+   If there are no errors, then the command was successful.
+
+3. CREATE SCHEMA OR SCHEMAS (Optional advanced step)
+
+   Drupal will run across different schemas within your database if you so wish.
+   By default, Drupal runs inside the 'public' schema but you can use $db_prefix
+   inside settings.php to define a schema for Drupal to run inside of, or
+   specify tables that are shared inside of a separate schema. Drupal will not
+   create schemas for you. In fact, the user that Drupal runs as should not be
+   allowed to do this. You'll need to execute the SQL below as a superuser,
+   replace 'username' with the username that Drupal uses to connect to
+   PostgreSQL, and replace 'schema_name' with a schema name you wish to use,
+   such as 'shared':
+
+     CREATE SCHEMA schema_name AUTHORIZATION username;
+
+   Do this for as many schemas as you need. See default.settings.php for
+   instructions on how to set which tables use which schemas.

+ 39 - 0
web/core/INSTALL.sqlite.txt

@@ -0,0 +1,39 @@
+
+SQLITE REQUIREMENTS
+-------------------
+
+PHP's PDO SQLite driver must be enabled. If you do not have pdo_sqlite
+available, depending on your system there are different ways to install it.
+
+Windows
+-------
+Read more about it on http://www.php.net/manual/en/pdo.installation.php
+
+Linux
+-----
+
+  sudo apt-get install php-sqlite3
+
+SQLITE DATABASE CREATION
+------------------------
+
+The Drupal installer will create the SQLite database for you. The only
+requirement is that the installer must have write permissions to the directory
+where the database file resides. This directory (not just the database file) also
+has to remain writeable by the web server going forward for SQLite to continue to
+be able to operate.
+
+On the "Database configuration" form in the "Database file" field, you must
+supply the exact path to where you wish your database file to reside. It is
+strongly suggested that you choose a path that is outside of the webroot, yet
+ensure that the directory is writeable by the web server.
+
+If you must place your database file in your webroot, you could try using the
+following in your "Database file" field:
+
+  sites/default/files/.ht.sqlite
+
+Note: The .ht in the name will tell Apache to prevent the database from being
+downloaded. Please check that the file is, indeed, protected by your webserver.
+If not, please consult the documentation of your webserver on how to protect a
+file from downloading.

+ 459 - 0
web/core/INSTALL.txt

@@ -0,0 +1,459 @@
+
+CONTENTS OF THIS FILE
+---------------------
+
+ * Quickstart
+ * Requirements and notes
+ * Optional server requirements
+ * Installation
+ * Reinstall
+ * Building and customizing your site
+ * Multisite configuration
+ * Multilingual configuration
+
+QUICKSTART
+----------------------
+
+Prerequisites:
+- PHP 7.0.8 (or greater) (https://php.net).
+
+In the instructions below, replace the version x.y.z with the specific version
+you wish to download. Example: 8.6.0.zip. You can find the latest stable version
+at https://www.drupal.org/project/drupal.
+
+Download and extract the Drupal package:
+- curl -sS https://ftp.drupal.org/files/projects/drupal-x.y.z.zip --output drupal-x.y.z.zip
+- unzip drupal-x.y.z.zip
+- cd /path/to/drupal-x.y.z
+- php core/scripts/drupal quick-start
+
+Wait… installation can take a minute or two. A successful installation will
+result in opening the new site in your browser.
+
+Run the following command for a list of available options that you may need to
+configure quick-start:
+- php core/scripts/drupal quick-start --help
+
+Follow the instructions in the REINSTALL section below to start over.
+
+NOTE: This quick start solution uses PHP's built-in web server and is not
+intended for production use. Read more about how to run Drupal in a production
+environment below.
+
+REQUIREMENTS AND NOTES
+----------------------
+
+Drupal requires:
+
+- A web server with PHP support, for example:
+  - Apache 2.0 (or greater) (http://httpd.apache.org/).
+  - Nginx 1.1 (or greater) (http://nginx.com/).
+- PHP 7.0.8 (or greater) (http://php.net/). For better security support it is
+  recommended to update to at least 7.2.17.
+- One of the following databases:
+  - MySQL 5.5.3 (or greater) (http://www.mysql.com/).
+  - MariaDB 5.5.20 (or greater) (https://mariadb.org/). MariaDB is a fully
+    compatible drop-in replacement for MySQL.
+  - Percona Server 5.5.8 (or greater) (http://www.percona.com/). Percona
+    Server is a backwards-compatible replacement for MySQL.
+  - PostgreSQL 9.1.2 (or greater) (http://www.postgresql.org/).
+  - SQLite 3.7.11 (or greater) (http://www.sqlite.org/).
+
+For more detailed information about Drupal requirements, including a list of
+PHP extensions and configurations that are required, see "System requirements"
+(https://www.drupal.org/requirements) in the Drupal.org online documentation.
+
+For detailed information on how to configure a test server environment using a
+variety of operating systems and web servers, see "Local server setup"
+(https://www.drupal.org/node/157602) in the Drupal.org online documentation.
+
+Note that all directories mentioned in this document are always relative to the
+directory of your Drupal installation, and commands are meant to be run from
+this directory (except for the initial commands that create that directory).
+
+OPTIONAL SERVER REQUIREMENTS
+----------------------------
+
+- If you want to use Drupal's "Clean URLs" feature on an Apache web server, you
+  will need the mod_rewrite module and the ability to use local .htaccess
+  files. For Clean URLs support on IIS, see "Clean URLs with IIS"
+  (https://www.drupal.org/node/3854) in the Drupal.org online documentation.
+
+- If you plan to use XML-based services such as RSS aggregation, you will need
+  PHP's XML extension. This extension is enabled by default on most PHP
+  installations.
+
+- To serve gzip compressed CSS and JS files on an Apache web server, you will
+  need the mod_headers module and the ability to use local .htaccess files.
+
+- Some Drupal functionality (e.g., checking whether Drupal and contributed
+  modules need updates, RSS aggregation, etc.) require that the web server be
+  able to go out to the web and download information. If you want to use this
+  functionality, you need to verify that your hosting provider or server
+  configuration allows the web server to initiate outbound connections. Most web
+  hosting setups allow this.
+
+INSTALLATION
+------------
+
+1. Download and extract Drupal.
+
+   You can obtain the latest Drupal release from https://www.drupal.org -- the
+   files are available in .tar.gz and .zip formats and can be extracted using
+   most compression tools.
+
+   To download and extract the files, on a typical Unix/Linux command line, use
+   the following commands (assuming you want version x.y.z of Drupal in .tar.gz
+   format):
+
+     wget https://www.drupal.org/files/projects/drupal-x.y.z.tar.gz
+     tar -zxvf drupal-x.y.z.tar.gz
+
+   This will create a new directory drupal-x.y.z/ containing all Drupal files
+   and directories. Then, to move the contents of that directory into a
+   directory within your web server's document root or your public HTML
+   directory, continue with this command:
+
+     mv drupal-x.y.z/* drupal-x.y.z/.htaccess drupal-x.y.z/.csslintrc drupal-x.y.z/.editorconfig drupal-x.y.z/.eslintignore drupal-x.y.z/.eslintrc.json drupal-x.y.z/.gitattributes /path/to/your/installation
+
+   You can also download the latest version of Drupal using Git on the command
+   line and set up a repository by following the instructions at
+   https://www.drupal.org/project/drupal/git-instructions for "Setting up
+   repository for the first time".
+
+   Once you have downloaded Drupal successfully, you may install Composer
+   globally using the instructions at
+   https://getcomposer.org/doc/00-intro.md#globally
+
+   With Composer installed, run the following command from the Drupal web root:
+
+     composer install
+
+2. Create the Drupal database.
+
+   Because Drupal stores all site information in a database, the Drupal
+   installer will attempt to create this database for you. If you create the
+   database manually, you must grant Drupal certain database privileges (such as
+   the ability to create tables).  For details, consult INSTALL.mysql.txt,
+   INSTALL.pgsql.txt, or INSTALL.sqlite.txt. You may also need to consult your
+   web hosting provider for instructions specific to your web host.
+
+   Take note of the username, password, database name, and hostname as you
+   create the database. You will enter this information during the install.
+
+3. Run the install script.
+
+   To run the install script, point your browser to the base URL of your
+   website (e.g., http://www.example.com).
+
+   You will be guided through several screens to set up the database, add the
+   site maintenance account (the first user, also known as user/1), and provide
+   basic web site settings.
+
+   During installation, several files and directories need to be created, which
+   the install script will try to do automatically. However, on some hosting
+   environments, manual steps are required, and the install script will tell
+   you that it cannot proceed until you fix certain issues. This is normal and
+   does not indicate a problem with your server.
+
+   The most common steps you may need to perform are:
+
+   a. Missing files directory.
+
+      The install script will attempt to create a public file storage directory
+      in the default location at sites/default/files (the location of the files
+      directory may be changed after Drupal is installed).
+
+      If auto-creation fails, you can create the directory yourself. (If you are
+      creating a multisite installation, substitute the correct sites directory
+      for sites/default; see the Multisite Configuration section of this file,
+      below.) Sample commands from a Unix/Linux command line:
+
+        mkdir sites/default/files
+        chmod a+w sites/default/files
+
+      Alternatively, you can make the install script work by changing
+      permissions on the sites/default directory. The web server can then
+      create the files directory within it for you.
+
+      For example, on a Unix/Linux command line, you can grant everyone
+      (including the web server) permission to write to the sites/default
+      directory with this command:
+
+        chmod a+w sites/default
+
+      Then re-run install.php (e.g. by clicking "try again" at the bottom of
+      the Requirements problem page. Once the files directory is created, you
+      will need to grant everyone (including the web server) permission to
+      write to it with this command:
+
+        chmod a+w sites/default/files
+
+      Be sure to set the permissions for the default directory back after the
+      installation is finished! (Leave the files directory writeable.)
+      Sample command:
+
+        chmod go-w sites/default
+
+   b. Missing settings file.
+
+      Drupal will try to automatically create a settings.php configuration file,
+      which is normally in the directory sites/default (to avoid problems when
+      upgrading, Drupal is not packaged with this file). If auto-creation fails,
+      you will need to create this file yourself, using the file
+      sites/default/default.settings.php as a template.
+
+      For example, on a Unix/Linux command line, you can make a copy of the
+      default.settings.php file with the command:
+
+        cp sites/default/default.settings.php sites/default/settings.php
+
+      Next, grant write privileges to the file to everyone (including the web
+      server) with the command:
+
+        chmod a+w sites/default/settings.php
+
+      Be sure to set the permissions back after the installation is finished!
+      Sample command:
+
+        chmod go-w sites/default/settings.php
+
+   c. Write permissions after install.
+
+      The install script will attempt to write-protect the settings.php file and
+      the sites/default directory after saving your configuration. If this
+      fails, you will be notified, and you can do it manually. Sample commands
+      from a Unix/Linux command line:
+
+        chmod go-w sites/default/settings.php
+        chmod go-w sites/default
+
+4. Verify that the site is working.
+
+   When the install script finishes, you will be logged in with the site
+   maintenance account on a "Welcome" page. If the default Drupal theme is not
+   displaying properly and links on the page result in "Page Not Found" errors,
+   you may be experiencing problems with clean URLs. Visit
+   https://www.drupal.org/docs/8/clean-urls-in-drupal-8 to troubleshoot.
+
+5. Change file system storage settings (optional).
+
+   The files directory created in step 3 is the default file system path used to
+   store all uploaded files, as well as some temporary files created by
+   Drupal. After installation, you can modify the file system path to store
+   uploaded files in a different location.
+
+   It is not necessary to modify this path, but you may wish to change it if:
+
+   - Your site runs multiple Drupal installations from a single codebase (modify
+     the file system path of each installation to a different directory so that
+     uploads do not overlap between installations).
+
+   - Your site runs on a number of web servers behind a load balancer or reverse
+     proxy (modify the file system path on each server to point to a shared file
+     repository).
+
+   - You want to restrict access to uploaded files.
+
+   To modify the file system path:
+
+   a. Ensure that the new location for the path exists and is writable by the
+      web server. For example, to create a new directory named uploads and grant
+      write permissions, use the following commands on a Unix/Linux command
+      line:
+
+        mkdir uploads
+        chmod a+w uploads
+
+   b. Open your settings.php in a plain-text editor, and uncomment (remove the #
+      at the start of line) this line:
+
+        # $settings['file_public_path'] = 'sites/default/files';
+
+      Enter the desired path and save the file.
+
+      If you want to use private file storage, you need to uncomment (remove
+      the # at the start of line) the following line in settings.php:
+
+        # $settings['file_private_path'] = '';
+
+      Enter the path for private files and save the file.
+
+   Changing the file system path after files have been uploaded may cause
+   unexpected problems on an existing site. If you modify the file system path
+   on an existing site, remember to copy all files from the original location
+   to the new location.
+
+6. Revoke documentation file permissions (optional).
+
+   Some administrators suggest making the documentation files, especially
+   CHANGELOG.txt, non-readable so that the exact version of Drupal you are
+   running is slightly more difficult to determine. If you wish to implement
+   this optional security measure, from a Unix/Linux command line you can use
+   the following command:
+
+     chmod a-r core/CHANGELOG.txt
+
+   Note that the example only affects CHANGELOG.txt. To completely hide all
+   documentation files from public view, repeat this command for each of the
+   Drupal documentation files in the installation directory, substituting the
+   name of each file for CHANGELOG.txt in the example.
+
+   For more information on setting file permissions, see "Modifying Linux,
+   Unix, and Mac file permissions" (https://www.drupal.org/node/202483) or
+   "Modifying Windows file permissions" (https://www.drupal.org/node/202491) in
+   the Drupal.org online documentation.
+
+7. Set up independent "cron" maintenance jobs.
+
+   Many Drupal modules have tasks that must be run periodically, including the
+   Search module (building and updating the index used for keyword searching),
+   the Aggregator module (retrieving feeds from other sites), and the System
+   module (performing routine maintenance and pruning of database tables). These
+   tasks are known as "cron maintenance tasks", named after the Unix/Linux
+   "cron" utility.
+
+   When you install Drupal, its built-in cron feature is enabled, which
+   automatically runs the cron tasks periodically, triggered by people visiting
+   pages of your site. You can configure the built-in cron feature by navigating
+   to Administration > Configuration > System > Cron.
+
+   It is also possible to run the cron tasks independent of site visits; this is
+   recommended for most sites. To do this, you will need to set up an automated
+   process to visit the page /cron on your site, which executes the cron
+   tasks.
+
+   The URL of the cron page requires a "cron key" to protect against
+   unauthorized access. Your site's cron key is automatically generated during
+   installation and is specific to your site. The full URL of the page, with the
+   cron key, is available in the "Cron maintenance tasks" section of the Status
+   report page at Administration > Reports > Status report.
+
+   As an example of how to set up this automated process, you can use the
+   crontab utility on Unix/Linux systems. The following crontab line uses the
+   wget command to visit the cron page, and runs each hour, on the hour:
+
+   0 * * * * wget -O - -q -t 1 http://example.com/cron/YOURKEY
+
+   Replace the text "http://example.com/cron/YOURKEY" in the example with the
+   full URL displayed under "Cron maintenance tasks" on the "Status report"
+   page.
+
+   More information about cron maintenance tasks is available at
+   https://www.drupal.org/cron, and sample cron shell scripts can be found in
+   the core/scripts/ directory. (Note that these scripts must be customized like
+   the above example, to add your site-specific cron key and domain name.)
+
+REINSTALL
+------------
+
+Drupal can be reinstalled without downloading and extracting the Drupal release.
+
+1. Drop all the tables in your database.
+
+2. Remove everything in sites/default/files.
+
+3. Remove sites/default/settings.php.
+
+4. Follow the Installation Instructions above starting from Step 3 (Run the
+   install script).
+
+BUILDING AND CUSTOMIZING YOUR SITE
+----------------------------------
+
+A new installation of Drupal defaults to a very basic configuration. To extend
+your site, you use "modules" and "themes". A module is a plugin that adds
+functionality to Drupal, while a theme changes the look of your site. The core
+of Drupal provides several optional modules and themes, and you can download
+more at https://www.drupal.org/project/project_module and
+https://www.drupal.org/project/project_theme
+
+Do not mix downloaded or custom modules and themes with Drupal's core modules
+and themes. Drupal's modules and themes are located in the /core/modules and
+/core/themes directories, while the modules and themes you add to Drupal are
+normally placed in the /modules and /themes directories. If you run a multisite
+installation, you can also place modules and themes in the site-specific
+directories -- see the Multisite Configuration section, below.
+
+Never edit Drupal's core modules and themes; instead, use the hooks available in
+the Drupal API. To modify the behavior of Drupal, develop a module as described
+at https://www.drupal.org/developing/modules. To modify the look of Drupal,
+create a subtheme as described at https://www.drupal.org/node/2165673, or a
+completely new theme as described at https://www.drupal.org/docs/8/theming
+
+MULTISITE CONFIGURATION
+-----------------------
+
+A single Drupal installation can host several Drupal-powered sites, each with
+its own individual configuration.
+
+For this to work you need the file sites/sites.php to exist. Make a copy of
+the example.sites.php file:
+
+  $ cp sites/example.sites.php sites/sites.php
+
+Additional site configurations are created in subdirectories within the 'sites'
+directory. Each subdirectory must have a 'settings.php' file, which specifies
+the configuration settings. The easiest way to create additional sites is to
+copy file 'default.settings.php' from the 'sites/default' directory into the
+new site directory with file name 'settings.php' and modify as appropriate.
+The new directory name is constructed from the site's URL. The configuration
+for www.example.com could be in 'sites/example.com/settings.php' (note that
+'www.' should be omitted if users can access your site at http://example.com/).
+
+  $ cp sites/default/default.settings.php sites/example.com/settings.php
+
+Sites do not have to have a different domain. You can also use subdomains and
+subdirectories for Drupal sites. For example, example.com, sub.example.com, and
+sub.example.com/site3 can all be defined as independent Drupal sites. The setup
+for a configuration such as this would look like the following:
+
+  sites/default/settings.php
+  sites/example.com/settings.php
+  sites/sub.example.com/settings.php
+  sites/sub.example.com.site3/settings.php
+
+When searching for a site configuration (for example www.sub.example.com/site3),
+Drupal will search for configuration files in the following order, using the
+first configuration it finds:
+
+  sites/www.sub.example.com.site3/settings.php
+  sites/sub.example.com.site3/settings.php
+  sites/example.com.site3/settings.php
+  sites/www.sub.example.com/settings.php
+  sites/sub.example.com/settings.php
+  sites/example.com/settings.php
+  sites/default/settings.php
+
+If you are installing on a non-standard port, the port number is treated as the
+deepest subdomain. For example: http://www.example.com:8080/ could be loaded
+from sites/8080.www.example.com/. The port number will be removed according to
+the pattern above if no port-specific configuration is found, just like a real
+subdomain.
+
+Each site configuration can have its own site-specific modules and themes in
+addition to those installed in the standard 'modules' and 'themes' directories.
+To use site-specific modules or themes, simply create a 'modules' or 'themes'
+directory within the site configuration directory. For example, if
+sub.example.com has a custom theme and a custom module that should not be
+accessible to other sites, the setup would look like this:
+
+  sites/sub.example.com/
+    settings.php
+    themes/custom_theme
+    modules/custom_module
+
+For more information about multiple virtual hosts or the configuration
+settings, consult https://www.drupal.org/docs/8/multisite
+
+For more information on configuring Drupal's file system path in a multisite
+configuration, see step 6 above.
+
+MULTILINGUAL CONFIGURATION
+--------------------------
+
+By default, Drupal is installed in one language, and further languages may be
+installed later.
+
+For detailed instructions, visit
+https://www.drupal.org/docs/8/multilingual

+ 339 - 0
web/core/LICENSE.txt

@@ -0,0 +1,339 @@
+        GNU GENERAL PUBLIC LICENSE
+           Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+          Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+        GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+          NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+         END OF TERMS AND CONDITIONS
+
+      How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.

+ 549 - 0
web/core/MAINTAINERS.txt

@@ -0,0 +1,549 @@
+Drupal core is built and maintained by the Drupal project community. Everyone is
+encouraged to submit issues and changes (patches) to improve Drupal, and to
+contribute in other ways -- see https://www.drupal.org/contribute to find out
+how.
+
+This file lists the active maintainers. For a list of past maintainers, see:
+https://www.drupal.org/core/maintainers/past
+
+
+Core committers
+---------------
+
+The Drupal Core committers oversee the development of Drupal as a whole. The
+core committers for Drupal are (in alphabetical order):
+
+BDFL
+- Dries Buytaert 'dries' https://www.drupal.org/u/dries
+
+Product managers
+- Dries Buytaert 'dries' https://www.drupal.org/u/dries
+- Angela Byron 'webchick' https://www.drupal.org/u/webchick
+- Gábor Hojtsy 'Gábor Hojtsy' https://www.drupal.org/u/gábor-hojtsy
+- Roy Scholten 'yoroy' https://www.drupal.org/u/yoroy
+
+Framework managers
+
+  Backend
+  - Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+  - Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
+  - Francesco Placella 'plach' https://www.drupal.org/u/plach
+  - Alex Pott 'alexpott' https://www.drupal.org/u/alexpott
+  - Lee Rowlands 'larowlan' https://www.drupal.org/u/larowlan
+
+  Frontend
+  - Lauri Eskola 'lauriii' https://www.drupal.org/u/lauriii
+
+Release managers
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
+- Jess Myrbo 'xjm' https://www.drupal.org/u/xjm
+
+Committer team facilitators
+- (provisional) Pamela Barone 'pameeela' https://www.drupal.org/u/pameeela
+
+Subsystem maintainers
+---------------------
+
+The Drupal Core subsystem maintainers oversee the development of Drupal
+subsystems. See https://www.drupal.org/contribute/core-maintainers for more
+information on their responsibilities, and to find out how to become a subsystem
+maintainer. Current subsystem maintainers for Drupal 8:
+
+Actions
+- ?
+
+Aggregator
+- ?
+
+Ajax
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
+
+Asset Library API
+- ?
+
+Authentication and Authorization
+- ?
+
+Automated Cron
+- ?
+
+Ban
+- ?
+
+Bartik
+- Emma Maria Karayiannis 'emma.maria' https://www.drupal.org/u/emma.maria
+
+Base system
+- ?
+
+Basic Auth
+- Juampy Novillo Requena 'juampy' https://www.drupal.org/u/juampy
+
+Batch API
+- John Cook 'John Cook' https://www.drupal.org/u/john-cook
+
+BigPipe
+- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
+- Fabian Franz 'Fabianx' https://www.drupal.org/u/fabianx
+
+Block
+- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
+- Ben Dougherty 'benjy' https://www.drupal.org/u/benjy
+
+Block Content
+- Lee Rowlands 'larowlan' https://www.drupal.org/u/larowlan
+
+Book
+- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
+
+Bootstrap
+- ?
+
+Breakpoint
+- Peter Droogmans 'attiks' https://www.drupal.org/u/attiks
+- Marc Drummond 'mdrummond' https://www.drupal.org/u/mdrummond
+
+Cache
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
+
+CKEditor
+- Henrik Danielsson 'TwoD' https://www.drupal.org/u/TwoD
+- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
+- Marek 'mlewand' Lewandowski https://www.drupal.org/u/mlewand
+
+Classy
+- David Hernandez 'davidhernandez' https://www.drupal.org/u/davidhernandez
+
+Color
+- ?
+
+Comment
+- Lee Rowlands 'larowlan' https://www.drupal.org/u/larowlan
+- Andrey Postnikov 'andypost' https://www.drupal.org/u/andypost
+
+Configuration API
+- Alex Pott 'alexpott' https://www.drupal.org/u/alexpott
+- Matthew Tift 'mtift' https://www.drupal.org/u/mtift
+
+Configuration Entity API
+- Alex Pott 'alexpott' https://www.drupal.org/u/alexpott
+- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
+
+Configuration UI
+- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
+
+Configuration Translation
+- Gábor Hojtsy 'Gábor Hojtsy' https://www.drupal.org/u/gábor-hojtsy
+- Tobias Zimmermann 'tstoeckler' https://www.drupal.org/u/tstoeckler
+- Vijayachandran Mani 'vijaycs85' https://www.drupal.org/u/vijaycs85
+
+Contact
+- Lee Rowlands 'larowlan' https://www.drupal.org/u/larowlan
+- Jibran Ijaz 'jibran' https://www.drupal.org/u/jibran
+- Andrey Postnikov 'andypost' https://www.drupal.org/u/andypost
+
+Content Moderation
+- Sam Becker 'Sam152' https://www.drupal.org/u/sam152
+
+Content Translation
+- Francesco Placella 'plach' https://www.drupal.org/u/plach
+
+Contextual
+- ?
+
+Cron
+- ?
+
+CSS
+- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
+
+Database API
+- ?
+
+  MySQL DB driver
+  - David Strauss 'David Strauss' https://www.drupal.org/u/david-strauss
+
+  PostgreSQL DB driver
+  - ?
+
+  Sqlite DB driver
+  - ?
+
+Database Logging
+- Mariano D'Agostino 'dagmar' https://www.drupal.org/u/dagmar
+
+Database Update API
+- ?
+
+DateTime
+- Jonathan Hedstrom 'jhedstrom' https://www.drupal.org/u/jhedstrom
+- Matthew Donadio 'mpdonadio' https://www.drupal.org/u/mpdonadio
+
+DateTime Range
+- Jonathan Hedstrom 'jhedstrom' https://www.drupal.org/u/jhedstrom
+- Matthew Donadio 'mpdonadio' https://www.drupal.org/u/mpdonadio
+
+Dynamic Page Cache
+- Fabian Franz 'Fabianx' https://www.drupal.org/u/fabianx
+- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
+
+Editor
+- Henrik Danielsson 'TwoD' https://www.drupal.org/u/TwoD
+- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
+
+Entity API
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
+- Hristo Chonov 'hchonov' https://www.drupal.org/u/hchonov
+- Sascha Grossenbacher 'Berdir' https://www.drupal.org/u/berdir
+- Francesco Placella 'plach' https://www.drupal.org/u/plach
+- Tobias Zimmermann 'tstoeckler' https://www.drupal.org/u/tstoeckler
+
+Extension API
+- ?
+
+Field API
+- Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu
+
+Field UI
+- Yves Chedemois 'yched' https://www.drupal.org/u/yched
+- Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu
+
+File
+- Kim Pepper 'kim.pepper' https://www.drupal.org/u/kimpepper
+
+Filter
+- ?
+
+Forum
+- Lee Rowlands 'larowlan' https://www.drupal.org/u/larowlan
+
+Form API
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
+
+History
+- Andrey Postnikov 'andypost' https://www.drupal.org/u/andypost
+
+Hypertext Application Language (HAL)
+- ?
+
+Help
+- ?
+
+Help Topics
+- Amber Matz 'Amber Himes Matz' https://www.drupal.org/u/amber-himes-matz
+- Andrey Postnikov 'andypost' https://www.drupal.org/u/andypost
+
+Image
+- Claudiu Cristea 'claudiu.cristea' https://www.drupal.org/u/claudiu.cristea
+
+Inline Form Errors
+- Daniël Smidt 'dmsmidt' https://www.drupal.org/u/dmsmidt
+
+Installer
+- ?
+
+Interface Translation (locale)
+- Gábor Hojtsy 'Gábor Hojtsy' https://www.drupal.org/u/gábor-hojtsy
+
+JavaScript
+- Théodore Biadala 'nod_' https://www.drupal.org/u/nod_
+- Sally Young 'justafish' https://www.drupal.org/u/justafish
+
+JSON:API
+- Gabe Sullice 'gabesullice' https://www.drupal.org/u/gabesullice
+- Mateu Aguiló Bosch 'e0ipso' https://www.drupal.org/u/e0ipso
+- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
+
+Language
+- Francesco Placella 'plach' https://www.drupal.org/u/plach
+- Daniel F. Kudwien 'sun' https://www.drupal.org/u/sun
+
+Layout Builder
+- Ted Bowman 'tedbow' https://www.drupal.org/u/tedbow
+- Emilie Nouveau 'DyanneNova' https://www.drupal.org/u/dyannenova
+- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
+
+Link Field
+- Weber Macedo 'Mac_Weber' https://www.drupal.org/u/mac_weber
+
+Lock
+- ?
+
+Mail
+- ?
+
+Markup
+- ?
+
+Media
+- Sean Blommaert 'seanB' https://www.drupal.org/u/seanb
+- Marcos Cano 'marcoscano' https://www.drupal.org/u/marcoscano
+- Christian Fritsch 'chr.fritsch' https://www.drupal.org/u/chr.fritsch
+- Adam Globus-Hoenich 'phenaproxima' https://www.drupal.org/u/phenaproxima
+
+  Media Library
+  - Sean Blommaert 'seanB' https://www.drupal.org/u/seanb
+  - Adam Globus-Hoenich 'phenaproxima' https://www.drupal.org/u/phenaproxima
+
+Menu
+- Daniel Wehner 'dawehner' https://www.drupal.org/u/dawehner
+- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
+
+Menu Link Content
+- Daniel Wehner 'dawehner' https://www.drupal.org/u/dawehner
+- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
+
+Menu UI
+- ?
+
+Migrate
+- Benji Fisher 'benjifisher' https://www.drupal.org/u/benjifisher
+- Adam Globus-Hoenich 'phenaproxima' https://www.drupal.org/u/phenaproxima
+- Lucas Hedding 'heddn' https://www.drupal.org/u/heddn
+- Michael Lutz 'mikelutz' https://www.drupal.org/u/mikelutz
+- Vicki Spagnolo 'quietone' https://www.drupal.org/u/quietone
+
+Node
+- ?
+
+Node Access
+- Ken Rickard 'agentrickard' https://www.drupal.org/u/agentrickard
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
+- Jess Myrbo 'xjm' https://www.drupal.org/u/xjm
+
+Options
+- ?
+
+Page Cache
+- Lorenz Schori 'znerol' https://www.drupal.org/u/znerol
+- Fabian Franz 'Fabianx' https://www.drupal.org/u/fabianx
+
+Path
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
+
+Path Alias
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
+
+Plugin
+- Kris Vanderwater 'EclipseGc' https://www.drupal.org/u/eclipseGc
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
+
+Queue
+- James Gilliland 'neclimdul' https://www.drupal.org/u/neclimdul
+
+Quick Edit
+- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
+- Théodore Biadala 'nod_' https://www.drupal.org/u/nod_
+
+RDF
+- ?
+
+Render API
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
+
+Request Processing
+- ?
+
+REST
+- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
+
+Responsive Image
+- Peter Droogmans 'attiks' https://www.drupal.org/u/attiks
+- Marc Drummond 'mdrummond' https://www.drupal.org/u/mdrummond
+- Jelle Sebreghts 'Jelle_S' https://www.drupal.org/u/jelle_s
+
+Routing
+- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
+
+Search
+- Peter Wolanin 'pwolanin' https://www.drupal.org/u/pwolanin
+
+Serialization
+- Damian Lee 'damiankloip' https://www.drupal.org/u/damiankloip
+
+Settings Tray
+- Ted Bowman 'tedbow' https://www.drupal.org/u/tedbow
+
+Seven
+- ?
+
+Shortcut
+- Tobias Zimmermann 'tstoeckler' https://www.drupal.org/u/tstoeckler
+- Jibran Ijaz 'jibran' https://www.drupal.org/u/jibran
+
+Stable
+- ?
+
+Stark
+- John Albin Wilkins 'JohnAlbin' https://www.drupal.org/u/johnalbin
+
+Statistics
+- ?
+
+Syslog
+- Mariano D'Agostino 'dagmar' https://www.drupal.org/u/dagmar
+
+System (module)
+- ?
+
+Taxonomy
+- Jess Myrbo 'xjm' https://www.drupal.org/u/xjm
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
+
+Telephone
+- ?
+
+Testing framework
+- Alex Pott 'alexpott' https://www.drupal.org/u/alexpott
+- Sascha Grossenbacher 'Berdir' https://www.drupal.org/u/berdir
+- Daniel Wehner 'dawehner' https://www.drupal.org/u/dawehner
+
+Text Field
+- ?
+
+Theme API
+- Alex Bronstein 'effulgentsia' https://www.drupal.org/u/effulgentsia
+- Fabian Franz 'Fabianx' https://www.drupal.org/u/fabianx
+- Joël Pittet 'joelpittet' https://www.drupal.org/u/joelpittet
+- Lauri Eskola 'lauriii' https://www.drupal.org/u/lauriii
+
+Token
+- ?
+
+Toolbar
+- Théodore Biadala 'nod_' https://www.drupal.org/u/nod_
+
+Tour
+- Nick Schuch 'nick_schuch' https://www.drupal.org/u/nick_schuch
+
+Tracker
+- ?
+
+Transliteration
+- Andrei Mateescu 'amateescu' https://www.drupal.org/u/amateescu
+
+Typed Data
+- Wolfgang Ziegler 'fago' https://www.drupal.org/u/fago
+
+Umami demo
+- Mark Conroy 'markconroy' https://www.drupal.org/u/markconroy
+- Gareth Goodwin 'smaz' https://www.drupal.org/u/smaz
+- Keith Jay 'kjay' https://www.drupal.org/u/kjay
+- Ofer Shaal 'shaal' https://www.drupal.org/u/shaal
+
+Update UI
+- ?
+
+User
+- Moshe Weitzman 'moshe weitzman' https://www.drupal.org/u/moshe-weitzman
+
+Views
+- Daniel Wehner 'dawehner' https://www.drupal.org/u/dawehner
+- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
+- Damian Lee 'damiankloip' https://www.drupal.org/u/damiankloip
+- Jess Myrbo 'xjm' https://www.drupal.org/u/xjm
+- Len Swaneveld 'Lendude' https://www.drupal.org/u/lendude
+
+Workflows
+- Sam Becker 'Sam152' https://www.drupal.org/u/sam152
+
+
+Topic maintainers
+-----------------
+
+Accessibility
+- Rain Breaw Michaels 'rainbreaw' https://www.drupal.org/u/rainbreaw
+- Mike Gifford 'mgifford' https://www.drupal.org/u/mgifford
+- Andrew Macpherson 'andrewmacpherson' https://www.drupal.org/u/andrewmacpherson
+
+Documentation
+- ?
+
+Performance
+- Nathaniel Catchpole 'catch' https://www.drupal.org/u/catch
+
+Testing
+- ?
+
+Usability
+- Cristina Chumillas 'ckrina' https://www.drupal.org/u/ckrina
+- Roy Scholten 'yoroy' https://www.drupal.org/u/yoroy
+- Bojhan Somers 'Bojhan' https://www.drupal.org/u/bojhan
+
+Provisional membership: None at this time.
+
+
+Security team
+-------------
+
+To report a security issue, see:
+https://www.drupal.org/security-team/report-issue
+
+The Drupal security team provides Security Advisories for vulnerabilities,
+assists developers in resolving security issues, and provides security
+documentation. See https://www.drupal.org/security-team for more information.
+The security team lead is:
+
+- Michael Hess 'mlhess' https://www.drupal.org/u/mlhess
+
+
+Initiative coordinators
+-----------------------
+
+The Drupal Core Initiative coordinators oversee approved projects that
+re-architect or otherwise improve large areas of Drupal core. See
+https://www.drupal.org/community-initiatives/drupal-core for more information on
+their responsibilities. The initiative coordinators for Drupal 8 are:
+
+API-first Initiative
+- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
+- Mateu Aguiló Bosch 'e0ipso' https://www.drupal.org/u/e0ipso
+- Gabe Sullice 'gabesullice' https://www.drupal.org/u/gabesullice
+
+Admin UI & JavaScript Modernisation Initiative
+- Cristina Chumillas 'ckrina' https://www.drupal.org/u/ckrina
+- Sally Young 'justafish' https://www.drupal.org/u/justafish
+
+Layout Initiative
+- Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett
+- Emilie Nouveau 'DyanneNova' https://www.drupal.org/u/dyannenova
+
+Media Initiative
+- Janez Urevc 'slashrsm' https://www.drupal.org/u/slashrsm
+
+PHPUnit Initiative
+- Daniel Wehner 'dawehner' https://www.drupal.org/u/dawehner
+- (provisional) Michiel Nugter 'michielnugter' https://www.drupal.org/u/michielnugter
+- (provisional) Len Swaneveld 'Lendude' https://www.drupal.org/u/lendude
+
+Workflow Initiative
+- Dick Olsson 'dixon_' https://www.drupal.org/u/dixon_
+
+Provisional membership: None at this time.
+
+
+Core mentoring coordinators
+---------------------------
+
+The Drupal Core mentors inspire, enable, and encourage new core contributors.
+See https://www.drupal.org/core-mentoring for more information about mentoring.
+
+Mentoring coordinators recruit and coach other mentors. They work on contributor
+tools, documentation, and processes to make it easier for new contributors to
+get involved. They organize communications and logistics, and actively
+participate in mentoring.
+
+- Mauricio Dinarte 'dinarcon' https://www.drupal.org/u/dinarcon
+- Lucas Hedding 'heddn' https://www.drupal.org/u/heddn
+- Tara King 'sparklingrobots' https://www.drupal.org/u/sparklingrobots
+- Rachel Lawson 'rachel_norfolk' https://www.drupal.org/u/rachel_norfolk
+- Valery Lourie 'valthebald' https://www.drupal.org/u/valthebald
+- Elli Ludwigson 'ekl1773' https://www.drupal.org/u/ekl1773
+- Jess Myrbo 'xjm' https://www.drupal.org/u/xjm
+- Matthew Radcliffe 'mradcliffe' https://www.drupal.org/u/mradcliffe
+
+Provisional membership: None at this time.

+ 77 - 0
web/core/UPDATE.txt

@@ -0,0 +1,77 @@
+CONTENTS OF THIS FILE
+---------------------
+
+ * Introduction
+ * Minor and patch version updates
+ * Is my site using Composer?
+ * Updating code manually
+ * Updating code with Composer
+ * Updating Drupal 8 to Drupal 9
+ * Updating Drupal 6 or 7 to Drupal 9
+
+
+INTRODUCTION
+------------
+
+This document provides links to resources on how to update or migrate your
+Drupal site.
+
+
+MINOR AND PATCH VERSION UPDATES
+-------------------------------
+
+Minor and patch updates are done either manually or with Composer, depending on
+how the site was installed. Refer to the sections below on updating with
+Composer or updating manually for tarball/zip installations.
+
+Minor and patch versions are updates such as:
+ * Patch releases (e.g. 9.1.2 to 9.1.3)
+ * Scheduled minor releases (e.g 9.3.5 to 9.4.0)
+ * Beta to final releases (e.g. 9.1.0-beta2 to 9.1.0)
+
+
+IS MY SITE USING COMPOSER?
+--------------------------
+
+Before updating, determine if your site is currently managed by Composer.
+
+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
+
+
+UPDATING CODE WITH COMPOSER
+---------------------------
+
+ * Refer to 'Update Drupal core via Composer' for details on using Composer:
+   https://www.drupal.org/docs/8/update/update-drupal-core-via-composer
+
+
+UPDATING CODE MANUALLY
+----------------------
+
+ * Refer to 'Updating the Core Software' for details on updating manually:
+   https://www.drupal.org/docs/8/update/update-core-manually
+
+
+UPDATING DRUPAL 8 TO DRUPAL 9
+-----------------------------
+
+    1. Prepare the Drupal 8 site for Drupal 9:
+    https://www.drupal.org/docs/9/how-to-prepare-your-drupal-7-or-8-site-for-drupal-9/upgrading-a-drupal-8-site-to-drupal-9
+
+    2. Update the Drupal 8 codebase to Drupal 9:
+    https://www.drupal.org/docs/8/upgrade/upgrading-between-drupal-8-major-versions-eg-from-drupal-8-to-drupal-9
+
+    3. Navigate to /update.php to initiate the update process.
+
+
+UPDATING DRUPAL 6 OR 7 TO DRUPAL 9
+----------------------------------
+
+ * Updating from a previous major version, such as Drupal 6 or 7, requires
+   importing the old site configuration and content into a new Drupal 9 site.
+   Refer to the 'Migrate Guide' for more details on this process:
+   https://www.drupal.org/upgrade/migrate

+ 11 - 0
web/core/assets/scaffold/README.txt

@@ -0,0 +1,11 @@
+Drupal Scaffold Files are files that are contained inside drupal/core, but are
+installed outside of the core directory (e.g. at the Drupal root).
+
+Scaffold files were added to drupal/core in Drupal 8.8.x. During the Drupal 8
+development cycle, the scaffold files are also being maintained in their original
+locations. This is done so that Drupal sites based on the template project
+drupal-composer/drupal-project may continue to download these files from the same
+URLs they have historically been found at.
+
+The scaffold files will be deleted from their original location in Drupal 9.
+See https://www.drupal.org/project/drupal/issues/3075954 for follow-on work.

+ 16 - 0
web/core/assets/scaffold/TESTING.txt

@@ -0,0 +1,16 @@
+HOW-TO: Test these Drupal scaffold files
+
+In order to test these scaffold files, you'll need to get the entire Drupal repo and
+run the tests there.
+
+You'll find the tests in core/tests/Drupal/Tests/ComposerIntegrationTest.php.
+
+You can get the full Drupal repo here:
+https://www.drupal.org/project/drupal/git-instructions
+
+You can find more information about running PHPUnit tests with Drupal here:
+https://www.drupal.org/node/2116263
+
+You can run a single phpunit test file like so:
+
+$ ./vendor/bin/phpunit -c core core/tests/Drupal/Tests/ComposerIntegrationTest.php

+ 40 - 0
web/core/assets/scaffold/files/csslintrc

@@ -0,0 +1,40 @@
+--errors=box-model,
+         display-property-grouping,
+         duplicate-background-images,
+         duplicate-properties,
+         empty-rules,
+         ids,
+         import,
+         important,
+         known-properties,
+         outline-none,
+         overqualified-elements,
+         qualified-headings,
+         shorthand,
+         star-property-hack,
+         text-indent,
+         underscore-property-hack,
+         unique-headings,
+         unqualified-attributes,
+         vendor-prefix,
+         zero-units
+--ignore=adjoining-classes,
+         box-sizing,
+         bulletproof-font-face,
+         compatible-vendor-prefixes,
+         errors,
+         fallback-colors,
+         floats,
+         font-faces,
+         font-sizes,
+         gradients,
+         import-ie-limit,
+         order-alphabetical,
+         regex-selectors,
+         rules-count,
+         selector-max,
+         selector-max-approaching,
+         selector-newline,
+         universal-selector
+--exclude-list=core/assets,
+               vendor

+ 174 - 0
web/core/assets/scaffold/files/default.services.yml

@@ -0,0 +1,174 @@
+parameters:
+  session.storage.options:
+    # Default ini options for sessions.
+    #
+    # Some distributions of Linux (most notably Debian) ship their PHP
+    # installations with garbage collection (gc) disabled. Since Drupal depends
+    # on PHP's garbage collection for clearing sessions, ensure that garbage
+    # collection occurs by using the most common settings.
+    # @default 1
+    gc_probability: 1
+    # @default 100
+    gc_divisor: 100
+    #
+    # Set session lifetime (in seconds), i.e. the time from the user's last
+    # visit to the active session may be deleted by the session garbage
+    # collector. When a session is deleted, authenticated users are logged out,
+    # and the contents of the user's $_SESSION variable is discarded.
+    # @default 200000
+    gc_maxlifetime: 200000
+    #
+    # Set session cookie lifetime (in seconds), i.e. the time from the session
+    # is created to the cookie expires, i.e. when the browser is expected to
+    # discard the cookie. The value 0 means "until the browser is closed".
+    # @default 2000000
+    cookie_lifetime: 2000000
+    #
+    # Drupal automatically generates a unique session cookie name based on the
+    # full domain name used to access the site. This mechanism is sufficient
+    # for most use-cases, including multi-site deployments. However, if it is
+    # desired that a session can be reused across different subdomains, the
+    # cookie domain needs to be set to the shared base domain. Doing so assures
+    # that users remain logged in as they cross between various subdomains.
+    # To maximize compatibility and normalize the behavior across user agents,
+    # the cookie domain should start with a dot.
+    #
+    # @default none
+    # cookie_domain: '.example.com'
+    #
+  twig.config:
+    # Twig debugging:
+    #
+    # When debugging is enabled:
+    # - The markup of each Twig template is surrounded by HTML comments that
+    #   contain theming information, such as template file name suggestions.
+    # - Note that this debugging markup will cause automated tests that directly
+    #   check rendered HTML to fail. When running automated tests, 'debug'
+    #   should be set to FALSE.
+    # - The dump() function can be used in Twig templates to output information
+    #   about template variables.
+    # - Twig templates are automatically recompiled whenever the source code
+    #   changes (see auto_reload below).
+    #
+    # For more information about debugging Twig templates, see
+    # https://www.drupal.org/node/1906392.
+    #
+    # Not recommended in production environments
+    # @default false
+    debug: false
+    # Twig auto-reload:
+    #
+    # Automatically recompile Twig templates whenever the source code changes.
+    # If you don't provide a value for auto_reload, it will be determined
+    # based on the value of debug.
+    #
+    # Not recommended in production environments
+    # @default null
+    auto_reload: null
+    # Twig cache:
+    #
+    # By default, Twig templates will be compiled and stored in the filesystem
+    # to increase performance. Disabling the Twig cache will recompile the
+    # templates from source each time they are used. In most cases the
+    # auto_reload setting above should be enabled rather than disabling the
+    # Twig cache.
+    #
+    # Not recommended in production environments
+    # @default true
+    cache: true
+  renderer.config:
+    # Renderer required cache contexts:
+    #
+    # The Renderer will automatically associate these cache contexts with every
+    # render array, hence varying every render array by these cache contexts.
+    #
+    # @default ['languages:language_interface', 'theme', 'user.permissions']
+    required_cache_contexts: ['languages:language_interface', 'theme', 'user.permissions']
+    # Renderer automatic placeholdering conditions:
+    #
+    # Drupal allows portions of the page to be automatically deferred when
+    # rendering to improve cache performance. That is especially helpful for
+    # cache contexts that vary widely, such as the active user. On some sites
+    # those may be different, however, such as sites with only a handful of
+    # users. If you know what the high-cardinality cache contexts are for your
+    # site, specify those here. If you're not sure, the defaults are fairly safe
+    # in general.
+    #
+    # For more information about rendering optimizations see
+    # https://www.drupal.org/developing/api/8/render/arrays/cacheability#optimizing
+    auto_placeholder_conditions:
+      # Max-age at or below which caching is not considered worthwhile.
+      #
+      # Disable by setting to -1.
+      #
+      # @default 0
+      max-age: 0
+      # Cache contexts with a high cardinality.
+      #
+      # Disable by setting to [].
+      #
+      # @default ['session', 'user']
+      contexts: ['session', 'user']
+      # Tags with a high invalidation frequency.
+      #
+      # Disable by setting to [].
+      #
+      # @default []
+      tags: []
+  # Cacheability debugging:
+  #
+  # Responses with cacheability metadata (CacheableResponseInterface instances)
+  # get X-Drupal-Cache-Tags and X-Drupal-Cache-Contexts headers.
+  #
+  # For more information about debugging cacheable responses, see
+  # https://www.drupal.org/developing/api/8/response/cacheable-response-interface
+  #
+  # Not recommended in production environments
+  # @default false
+  http.response.debug_cacheability_headers: false
+  factory.keyvalue:
+    {}
+    # Default key/value storage service to use.
+    # @default keyvalue.database
+    # default: keyvalue.database
+    # Collection-specific overrides.
+    # state: keyvalue.database
+  factory.keyvalue.expirable:
+    {}
+    # Default key/value expirable storage service to use.
+    # @default keyvalue.database.expirable
+    # default: keyvalue.database.expirable
+  # Allowed protocols for URL generation.
+  filter_protocols:
+    - http
+    - https
+    - ftp
+    - news
+    - nntp
+    - tel
+    - telnet
+    - mailto
+    - irc
+    - ssh
+    - sftp
+    - webcal
+    - rtsp
+
+   # Configure Cross-Site HTTP requests (CORS).
+   # Read https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
+   # for more information about the topic in general.
+   # Note: By default the configuration is disabled.
+  cors.config:
+    enabled: false
+    # Specify allowed headers, like 'x-allowed-header'.
+    allowedHeaders: []
+    # Specify allowed request methods, specify ['*'] to allow all possible ones.
+    allowedMethods: []
+    # Configure requests allowed from specific origins.
+    allowedOrigins: ['*']
+    # Sets the Access-Control-Expose-Headers header.
+    exposedHeaders: false
+    # Sets the Access-Control-Max-Age header.
+    maxAge: false
+    # Sets the Access-Control-Allow-Credentials header.
+    supportsCredentials: false

+ 794 - 0
web/core/assets/scaffold/files/default.settings.php

@@ -0,0 +1,794 @@
+<?php
+
+// @codingStandardsIgnoreFile
+
+/**
+ * @file
+ * Drupal site-specific configuration file.
+ *
+ * IMPORTANT NOTE:
+ * This file may have been set to read-only by the Drupal installation program.
+ * If you make changes to this file, be sure to protect it again after making
+ * your modifications. Failure to remove write permissions to this file is a
+ * security risk.
+ *
+ * In order to use the selection rules below the multisite aliasing file named
+ * sites/sites.php must be present. Its optional settings will be loaded, and
+ * the aliases in the array $sites will override the default directory rules
+ * below. See sites/example.sites.php for more information about aliases.
+ *
+ * The configuration directory will be discovered by stripping the website's
+ * hostname from left to right and pathname from right to left. The first
+ * configuration file found will be used and any others will be ignored. If no
+ * other configuration file is found then the default configuration file at
+ * 'sites/default' will be used.
+ *
+ * For example, for a fictitious site installed at
+ * https://www.drupal.org:8080/mysite/test/, the 'settings.php' file is searched
+ * for in the following directories:
+ *
+ * - sites/8080.www.drupal.org.mysite.test
+ * - sites/www.drupal.org.mysite.test
+ * - sites/drupal.org.mysite.test
+ * - sites/org.mysite.test
+ *
+ * - sites/8080.www.drupal.org.mysite
+ * - sites/www.drupal.org.mysite
+ * - sites/drupal.org.mysite
+ * - sites/org.mysite
+ *
+ * - sites/8080.www.drupal.org
+ * - sites/www.drupal.org
+ * - sites/drupal.org
+ * - sites/org
+ *
+ * - sites/default
+ *
+ * Note that if you are installing on a non-standard port number, prefix the
+ * hostname with that number. For example,
+ * https://www.drupal.org:8080/mysite/test/ could be loaded from
+ * sites/8080.www.drupal.org.mysite.test/.
+ *
+ * @see example.sites.php
+ * @see \Drupal\Core\DrupalKernel::getSitePath()
+ *
+ * In addition to customizing application settings through variables in
+ * settings.php, you can create a services.yml file in the same directory to
+ * register custom, site-specific service definitions and/or swap out default
+ * implementations with custom ones.
+ */
+
+/**
+ * Database settings:
+ *
+ * The $databases array specifies the database connection or
+ * connections that Drupal may use.  Drupal is able to connect
+ * to multiple databases, including multiple types of databases,
+ * during the same request.
+ *
+ * One example of the simplest connection array is shown below. To use the
+ * sample settings, copy and uncomment the code below between the @code and
+ * @endcode lines and paste it after the $databases declaration. You will need
+ * to replace the database username and password and possibly the host and port
+ * with the appropriate credentials for your database system.
+ *
+ * The next section describes how to customize the $databases array for more
+ * specific needs.
+ *
+ * @code
+ * $databases['default']['default'] = [
+ *   'database' => 'databasename',
+ *   'username' => 'sqlusername',
+ *   'password' => 'sqlpassword',
+ *   'host' => 'localhost',
+ *   'port' => '3306',
+ *   'driver' => 'mysql',
+ *   'prefix' => '',
+ *   'collation' => 'utf8mb4_general_ci',
+ * ];
+ * @endcode
+ */
+$databases = [];
+
+/**
+ * Customizing database settings.
+ *
+ * Many of the values of the $databases array can be customized for your
+ * particular database system. Refer to the sample in the section above as a
+ * starting point.
+ *
+ * The "driver" property indicates what Drupal database driver the
+ * connection should use.  This is usually the same as the name of the
+ * database type, such as mysql or sqlite, but not always.  The other
+ * properties will vary depending on the driver.  For SQLite, you must
+ * specify a database file name in a directory that is writable by the
+ * webserver.  For most other drivers, you must specify a
+ * username, password, host, and database name.
+ *
+ * Drupal core implements drivers for mysql, pgsql, and sqlite. Other drivers
+ * can be provided by contributed or custom modules. To use a contributed or
+ * custom driver, the "namespace" property must be set to the namespace of the
+ * driver. The code in this namespace must be autoloadable prior to connecting
+ * to the database, and therefore, prior to when module root namespaces are
+ * added to the autoloader. To add the driver's namespace to the autoloader,
+ * set the "autoload" property to the PSR-4 base directory of the driver's
+ * namespace. This is optional for projects managed with Composer if the
+ * driver's namespace is in Composer's autoloader.
+ *
+ * Transaction support is enabled by default for all drivers that support it,
+ * including MySQL. To explicitly disable it, set the 'transactions' key to
+ * FALSE.
+ * Note that some configurations of MySQL, such as the MyISAM engine, don't
+ * support it and will proceed silently even if enabled. If you experience
+ * transaction related crashes with such configuration, set the 'transactions'
+ * key to FALSE.
+ *
+ * For each database, you may optionally specify multiple "target" databases.
+ * A target database allows Drupal to try to send certain queries to a
+ * different database if it can but fall back to the default connection if not.
+ * That is useful for primary/replica replication, as Drupal may try to connect
+ * to a replica server when appropriate and if one is not available will simply
+ * fall back to the single primary server (The terms primary/replica are
+ * traditionally referred to as master/slave in database server documentation).
+ *
+ * The general format for the $databases array is as follows:
+ * @code
+ * $databases['default']['default'] = $info_array;
+ * $databases['default']['replica'][] = $info_array;
+ * $databases['default']['replica'][] = $info_array;
+ * $databases['extra']['default'] = $info_array;
+ * @endcode
+ *
+ * In the above example, $info_array is an array of settings described above.
+ * The first line sets a "default" database that has one primary database
+ * (the second level default).  The second and third lines create an array
+ * of potential replica databases.  Drupal will select one at random for a given
+ * request as needed.  The fourth line creates a new database with a name of
+ * "extra".
+ *
+ * You can optionally set prefixes for some or all database table names
+ * by using the 'prefix' setting. If a prefix is specified, the table
+ * name will be prepended with its value. Be sure to use valid database
+ * characters only, usually alphanumeric and underscore. If no prefixes
+ * are desired, leave it as an empty string ''.
+ *
+ * To have all database names prefixed, set 'prefix' as a string:
+ * @code
+ *   'prefix' => 'main_',
+ * @endcode
+ *
+ * Per-table prefixes are deprecated as of Drupal 8.2, and will be removed in
+ * Drupal 9.0. After that, only a single prefix for all tables will be
+ * supported.
+ *
+ * To provide prefixes for specific tables, set 'prefix' as an array.
+ * The array's keys are the table names and the values are the prefixes.
+ * The 'default' element is mandatory and holds the prefix for any tables
+ * not specified elsewhere in the array. Example:
+ * @code
+ *   'prefix' => [
+ *     'default'   => 'main_',
+ *     'users'     => 'shared_',
+ *     'sessions'  => 'shared_',
+ *     'role'      => 'shared_',
+ *     'authmap'   => 'shared_',
+ *   ],
+ * @endcode
+ * You can also use a reference to a schema/database as a prefix. This may be
+ * useful if your Drupal installation exists in a schema that is not the default
+ * or you want to access several databases from the same code base at the same
+ * time.
+ * Example:
+ * @code
+ *   'prefix' => [
+ *     'default'   => 'main.',
+ *     'users'     => 'shared.',
+ *     'sessions'  => 'shared.',
+ *     'role'      => 'shared.',
+ *     'authmap'   => 'shared.',
+ *   ];
+ * @endcode
+ * NOTE: MySQL and SQLite's definition of a schema is a database.
+ *
+ * Advanced users can add or override initial commands to execute when
+ * connecting to the database server, as well as PDO connection settings. For
+ * example, to enable MySQL SELECT queries to exceed the max_join_size system
+ * variable, and to reduce the database connection timeout to 5 seconds:
+ * @code
+ * $databases['default']['default'] = [
+ *   'init_commands' => [
+ *     'big_selects' => 'SET SQL_BIG_SELECTS=1',
+ *   ],
+ *   'pdo' => [
+ *     PDO::ATTR_TIMEOUT => 5,
+ *   ],
+ * ];
+ * @endcode
+ *
+ * WARNING: The above defaults are designed for database portability. Changing
+ * them may cause unexpected behavior, including potential data loss. See
+ * https://www.drupal.org/developing/api/database/configuration for more
+ * information on these defaults and the potential issues.
+ *
+ * More details can be found in the constructor methods for each driver:
+ * - \Drupal\Core\Database\Driver\mysql\Connection::__construct()
+ * - \Drupal\Core\Database\Driver\pgsql\Connection::__construct()
+ * - \Drupal\Core\Database\Driver\sqlite\Connection::__construct()
+ *
+ * Sample Database configuration format for PostgreSQL (pgsql):
+ * @code
+ *   $databases['default']['default'] = [
+ *     'driver' => 'pgsql',
+ *     'database' => 'databasename',
+ *     'username' => 'sqlusername',
+ *     'password' => 'sqlpassword',
+ *     'host' => 'localhost',
+ *     'prefix' => '',
+ *   ];
+ * @endcode
+ *
+ * Sample Database configuration format for SQLite (sqlite):
+ * @code
+ *   $databases['default']['default'] = [
+ *     'driver' => 'sqlite',
+ *     'database' => '/path/to/databasefilename',
+ *   ];
+ * @endcode
+ *
+ * Sample Database configuration format for a driver in a contributed module:
+ * @code
+ *   $databases['default']['default'] = [
+ *     'driver' => 'mydriver',
+ *     'namespace' => 'Drupal\mymodule\Driver\Database\mydriver',
+ *     'autoload' => 'modules/mymodule/src/Driver/Database/mydriver/',
+ *     'database' => 'databasename',
+ *     'username' => 'sqlusername',
+ *     'password' => 'sqlpassword',
+ *     'host' => 'localhost',
+ *     'prefix' => '',
+ *   ];
+ * @endcode
+ */
+
+/**
+ * Location of the site configuration files.
+ *
+ * The $settings['config_sync_directory'] specifies the location of file system
+ * directory used for syncing configuration data. On install, the directory is
+ * created. This is used for configuration imports.
+ *
+ * The default location for this directory is inside a randomly-named
+ * directory in the public files path. The setting below allows you to set
+ * its location.
+ */
+# $settings['config_sync_directory'] = '/directory/outside/webroot';
+
+/**
+ * Settings:
+ *
+ * $settings contains environment-specific configuration, such as the files
+ * directory and reverse proxy address, and temporary configuration, such as
+ * security overrides.
+ *
+ * @see \Drupal\Core\Site\Settings::get()
+ */
+
+/**
+ * Salt for one-time login links, cancel links, form tokens, etc.
+ *
+ * This variable will be set to a random value by the installer. All one-time
+ * login links will be invalidated if the value is changed. Note that if your
+ * site is deployed on a cluster of web servers, you must ensure that this
+ * variable has the same value on each server.
+ *
+ * For enhanced security, you may set this variable to the contents of a file
+ * outside your document root; you should also ensure that this file is not
+ * stored with backups of your database.
+ *
+ * Example:
+ * @code
+ *   $settings['hash_salt'] = file_get_contents('/home/example/salt.txt');
+ * @endcode
+ */
+$settings['hash_salt'] = '';
+
+/**
+ * Deployment identifier.
+ *
+ * Drupal's dependency injection container will be automatically invalidated and
+ * rebuilt when the Drupal core version changes. When updating contributed or
+ * custom code that changes the container, changing this identifier will also
+ * allow the container to be invalidated as soon as code is deployed.
+ */
+# $settings['deployment_identifier'] = \Drupal::VERSION;
+
+/**
+ * Access control for update.php script.
+ *
+ * If you are updating your Drupal installation using the update.php script but
+ * are not logged in using either an account with the "Administer software
+ * updates" permission or the site maintenance account (the account that was
+ * created during installation), you will need to modify the access check
+ * statement below. Change the FALSE to a TRUE to disable the access check.
+ * After finishing the upgrade, be sure to open this file again and change the
+ * TRUE back to a FALSE!
+ */
+$settings['update_free_access'] = FALSE;
+
+/**
+ * External access proxy settings:
+ *
+ * If your site must access the Internet via a web proxy then you can enter the
+ * proxy settings here. Set the full URL of the proxy, including the port, in
+ * variables:
+ * - $settings['http_client_config']['proxy']['http']: The proxy URL for HTTP
+ *   requests.
+ * - $settings['http_client_config']['proxy']['https']: The proxy URL for HTTPS
+ *   requests.
+ * You can pass in the user name and password for basic authentication in the
+ * URLs in these settings.
+ *
+ * You can also define an array of host names that can be accessed directly,
+ * bypassing the proxy, in $settings['http_client_config']['proxy']['no'].
+ */
+# $settings['http_client_config']['proxy']['http'] = 'http://proxy_user:proxy_pass@example.com:8080';
+# $settings['http_client_config']['proxy']['https'] = 'http://proxy_user:proxy_pass@example.com:8080';
+# $settings['http_client_config']['proxy']['no'] = ['127.0.0.1', 'localhost'];
+
+/**
+ * Reverse Proxy Configuration:
+ *
+ * Reverse proxy servers are often used to enhance the performance
+ * of heavily visited sites and may also provide other site caching,
+ * security, or encryption benefits. In an environment where Drupal
+ * is behind a reverse proxy, the real IP address of the client should
+ * be determined such that the correct client IP address is available
+ * to Drupal's logging, statistics, and access management systems. In
+ * the most simple scenario, the proxy server will add an
+ * X-Forwarded-For header to the request that contains the client IP
+ * address. However, HTTP headers are vulnerable to spoofing, where a
+ * malicious client could bypass restrictions by setting the
+ * X-Forwarded-For header directly. Therefore, Drupal's proxy
+ * configuration requires the IP addresses of all remote proxies to be
+ * specified in $settings['reverse_proxy_addresses'] to work correctly.
+ *
+ * Enable this setting to get Drupal to determine the client IP from the
+ * X-Forwarded-For header. If you are unsure about this setting, do not have a
+ * reverse proxy, or Drupal operates in a shared hosting environment, this
+ * setting should remain commented out.
+ *
+ * In order for this setting to be used you must specify every possible
+ * reverse proxy IP address in $settings['reverse_proxy_addresses'].
+ * If a complete list of reverse proxies is not available in your
+ * environment (for example, if you use a CDN) you may set the
+ * $_SERVER['REMOTE_ADDR'] variable directly in settings.php.
+ * Be aware, however, that it is likely that this would allow IP
+ * address spoofing unless more advanced precautions are taken.
+ */
+# $settings['reverse_proxy'] = TRUE;
+
+/**
+ * Specify every reverse proxy IP address in your environment.
+ * This setting is required if $settings['reverse_proxy'] is TRUE.
+ */
+# $settings['reverse_proxy_addresses'] = ['a.b.c.d', ...];
+
+/**
+ * Reverse proxy trusted headers.
+ *
+ * Sets which headers to trust from your reverse proxy.
+ *
+ * Common values are:
+ * - \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_ALL
+ * - \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED
+ *
+ * Note the default value of
+ * @code
+ * \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_ALL | \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED
+ * @endcode
+ * is not secure by default. The value should be set to only the specific
+ * headers the reverse proxy uses. For example:
+ * @code
+ * \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_ALL
+ * @endcode
+ * This would trust the following headers:
+ * - X_FORWARDED_FOR
+ * - X_FORWARDED_HOST
+ * - X_FORWARDED_PROTO
+ * - X_FORWARDED_PORT
+ *
+ * @see \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_ALL
+ * @see \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED
+ * @see \Symfony\Component\HttpFoundation\Request::setTrustedProxies
+ */
+# $settings['reverse_proxy_trusted_headers'] = \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_ALL | \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED;
+
+
+/**
+ * Page caching:
+ *
+ * By default, Drupal sends a "Vary: Cookie" HTTP header for anonymous page
+ * views. This tells a HTTP proxy that it may return a page from its local
+ * cache without contacting the web server, if the user sends the same Cookie
+ * header as the user who originally requested the cached page. Without "Vary:
+ * Cookie", authenticated users would also be served the anonymous page from
+ * the cache. If the site has mostly anonymous users except a few known
+ * editors/administrators, the Vary header can be omitted. This allows for
+ * better caching in HTTP proxies (including reverse proxies), i.e. even if
+ * clients send different cookies, they still get content served from the cache.
+ * However, authenticated users should access the site directly (i.e. not use an
+ * HTTP proxy, and bypass the reverse proxy if one is used) in order to avoid
+ * getting cached pages from the proxy.
+ */
+# $settings['omit_vary_cookie'] = TRUE;
+
+
+/**
+ * Cache TTL for client error (4xx) responses.
+ *
+ * Items cached per-URL tend to result in a large number of cache items, and
+ * this can be problematic on 404 pages which by their nature are unbounded. A
+ * fixed TTL can be set for these items, defaulting to one hour, so that cache
+ * backends which do not support LRU can purge older entries. To disable caching
+ * of client error responses set the value to 0. Currently applies only to
+ * page_cache module.
+ */
+# $settings['cache_ttl_4xx'] = 3600;
+
+/**
+ * Expiration of cached forms.
+ *
+ * Drupal's Form API stores details of forms in a cache and these entries are
+ * kept for at least 6 hours by default. Expired entries are cleared by cron.
+ *
+ * @see \Drupal\Core\Form\FormCache::setCache()
+ */
+# $settings['form_cache_expiration'] = 21600;
+
+/**
+ * Class Loader.
+ *
+ * If the APC extension is detected, the Symfony APC class loader is used for
+ * performance reasons. Detection can be prevented by setting
+ * class_loader_auto_detect to false, as in the example below.
+ */
+# $settings['class_loader_auto_detect'] = FALSE;
+
+/*
+ * If the APC extension is not detected, either because APC is missing or
+ * because auto-detection has been disabled, auto-loading falls back to
+ * Composer's ClassLoader, which is good for development as it does not break
+ * when code is moved in the file system. You can also decorate the base class
+ * loader with another cached solution than the Symfony APC class loader, as
+ * all production sites should have a cached class loader of some sort enabled.
+ *
+ * To do so, you may decorate and replace the local $class_loader variable. For
+ * example, to use Symfony's APC class loader without automatic detection,
+ * uncomment the code below.
+ */
+/*
+if ($settings['hash_salt']) {
+  $prefix = 'drupal.' . hash('sha256', 'drupal.' . $settings['hash_salt']);
+  $apc_loader = new \Symfony\Component\ClassLoader\ApcClassLoader($prefix, $class_loader);
+  unset($prefix);
+  $class_loader->unregister();
+  $apc_loader->register();
+  $class_loader = $apc_loader;
+}
+*/
+
+/**
+ * Authorized file system operations:
+ *
+ * The Update Manager module included with Drupal provides a mechanism for
+ * site administrators to securely install missing updates for the site
+ * directly through the web user interface. On securely-configured servers,
+ * the Update manager will require the administrator to provide SSH or FTP
+ * credentials before allowing the installation to proceed; this allows the
+ * site to update the new files as the user who owns all the Drupal files,
+ * instead of as the user the webserver is running as. On servers where the
+ * webserver user is itself the owner of the Drupal files, the administrator
+ * will not be prompted for SSH or FTP credentials (note that these server
+ * setups are common on shared hosting, but are inherently insecure).
+ *
+ * Some sites might wish to disable the above functionality, and only update
+ * the code directly via SSH or FTP themselves. This setting completely
+ * disables all functionality related to these authorized file operations.
+ *
+ * @see https://www.drupal.org/node/244924
+ *
+ * Remove the leading hash signs to disable.
+ */
+# $settings['allow_authorize_operations'] = FALSE;
+
+/**
+ * Default mode for directories and files written by Drupal.
+ *
+ * Value should be in PHP Octal Notation, with leading zero.
+ */
+# $settings['file_chmod_directory'] = 0775;
+# $settings['file_chmod_file'] = 0664;
+
+/**
+ * Public file base URL:
+ *
+ * An alternative base URL to be used for serving public files. This must
+ * include any leading directory path.
+ *
+ * A different value from the domain used by Drupal to be used for accessing
+ * public files. This can be used for a simple CDN integration, or to improve
+ * security by serving user-uploaded files from a different domain or subdomain
+ * pointing to the same server. Do not include a trailing slash.
+ */
+# $settings['file_public_base_url'] = 'http://downloads.example.com/files';
+
+/**
+ * Public file path:
+ *
+ * A local file system path where public files will be stored. This directory
+ * must exist and be writable by Drupal. This directory must be relative to
+ * the Drupal installation directory and be accessible over the web.
+ */
+# $settings['file_public_path'] = 'sites/default/files';
+
+/**
+ * Private file path:
+ *
+ * A local file system path where private files will be stored. This directory
+ * must be absolute, outside of the Drupal installation directory and not
+ * accessible over the web.
+ *
+ * Note: Caches need to be cleared when this value is changed to make the
+ * private:// stream wrapper available to the system.
+ *
+ * See https://www.drupal.org/documentation/modules/file for more information
+ * about securing private files.
+ */
+# $settings['file_private_path'] = '';
+
+/**
+ * Temporary file path:
+ *
+ * A local file system path where temporary files will be stored. This directory
+ * must be absolute, outside of the Drupal installation directory and not
+ * accessible over the web.
+ *
+ * If this is not set, the default for the operating system will be used.
+ *
+ * @see \Drupal\Component\FileSystem\FileSystem::getOsTemporaryDirectory()
+ */
+# $settings['file_temp_path'] = '/tmp';
+
+/**
+ * Session write interval:
+ *
+ * Set the minimum interval between each session write to database.
+ * For performance reasons it defaults to 180.
+ */
+# $settings['session_write_interval'] = 180;
+
+/**
+ * String overrides:
+ *
+ * To override specific strings on your site with or without enabling the Locale
+ * module, add an entry to this list. This functionality allows you to change
+ * a small number of your site's default English language interface strings.
+ *
+ * Remove the leading hash signs to enable.
+ *
+ * The "en" part of the variable name, is dynamic and can be any langcode of
+ * any added language. (eg locale_custom_strings_de for german).
+ */
+# $settings['locale_custom_strings_en'][''] = [
+#   'forum'      => 'Discussion board',
+#   '@count min' => '@count minutes',
+# ];
+
+/**
+ * A custom theme for the offline page:
+ *
+ * This applies when the site is explicitly set to maintenance mode through the
+ * administration page or when the database is inactive due to an error.
+ * The template file should also be copied into the theme. It is located inside
+ * 'core/modules/system/templates/maintenance-page.html.twig'.
+ *
+ * Note: This setting does not apply to installation and update pages.
+ */
+# $settings['maintenance_theme'] = 'bartik';
+
+/**
+ * PHP settings:
+ *
+ * To see what PHP settings are possible, including whether they can be set at
+ * runtime (by using ini_set()), read the PHP documentation:
+ * http://php.net/manual/ini.list.php
+ * See \Drupal\Core\DrupalKernel::bootEnvironment() for required runtime
+ * settings and the .htaccess file for non-runtime settings.
+ * Settings defined there should not be duplicated here so as to avoid conflict
+ * issues.
+ */
+
+/**
+ * If you encounter a situation where users post a large amount of text, and
+ * the result is stripped out upon viewing but can still be edited, Drupal's
+ * output filter may not have sufficient memory to process it.  If you
+ * experience this issue, you may wish to uncomment the following two lines
+ * and increase the limits of these variables.  For more information, see
+ * http://php.net/manual/pcre.configuration.php.
+ */
+# ini_set('pcre.backtrack_limit', 200000);
+# ini_set('pcre.recursion_limit', 200000);
+
+/**
+ * Configuration overrides.
+ *
+ * To globally override specific configuration values for this site,
+ * set them here. You usually don't need to use this feature. This is
+ * useful in a configuration file for a vhost or directory, rather than
+ * the default settings.php.
+ *
+ * Note that any values you provide in these variable overrides will not be
+ * viewable from the Drupal administration interface. The administration
+ * interface displays the values stored in configuration so that you can stage
+ * changes to other environments that don't have the overrides.
+ *
+ * There are particular configuration values that are risky to override. For
+ * example, overriding the list of installed modules in 'core.extension' is not
+ * supported as module install or uninstall has not occurred. Other examples
+ * include field storage configuration, because it has effects on database
+ * structure, and 'core.menu.static_menu_link_overrides' since this is cached in
+ * a way that is not config override aware. Also, note that changing
+ * configuration values in settings.php will not fire any of the configuration
+ * change events.
+ */
+# $config['system.site']['name'] = 'My Drupal site';
+# $config['user.settings']['anonymous'] = 'Visitor';
+
+/**
+ * Fast 404 pages:
+ *
+ * Drupal can generate fully themed 404 pages. However, some of these responses
+ * are for images or other resource files that are not displayed to the user.
+ * This can waste bandwidth, and also generate server load.
+ *
+ * The options below return a simple, fast 404 page for URLs matching a
+ * specific pattern:
+ * - $config['system.performance']['fast_404']['exclude_paths']: A regular
+ *   expression to match paths to exclude, such as images generated by image
+ *   styles, or dynamically-resized images. The default pattern provided below
+ *   also excludes the private file system. If you need to add more paths, you
+ *   can add '|path' to the expression.
+ * - $config['system.performance']['fast_404']['paths']: A regular expression to
+ *   match paths that should return a simple 404 page, rather than the fully
+ *   themed 404 page. If you don't have any aliases ending in htm or html you
+ *   can add '|s?html?' to the expression.
+ * - $config['system.performance']['fast_404']['html']: The html to return for
+ *   simple 404 pages.
+ *
+ * Remove the leading hash signs if you would like to alter this functionality.
+ */
+# $config['system.performance']['fast_404']['exclude_paths'] = '/\/(?:styles)|(?:system\/files)\//';
+# $config['system.performance']['fast_404']['paths'] = '/\.(?:txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i';
+# $config['system.performance']['fast_404']['html'] = '<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>';
+
+/**
+ * Load services definition file.
+ */
+$settings['container_yamls'][] = $app_root . '/' . $site_path . '/services.yml';
+
+/**
+ * Override the default service container class.
+ *
+ * This is useful for example to trace the service container for performance
+ * tracking purposes, for testing a service container with an error condition or
+ * to test a service container that throws an exception.
+ */
+# $settings['container_base_class'] = '\Drupal\Core\DependencyInjection\Container';
+
+/**
+ * Override the default yaml parser class.
+ *
+ * Provide a fully qualified class name here if you would like to provide an
+ * alternate implementation YAML parser. The class must implement the
+ * \Drupal\Component\Serialization\SerializationInterface interface.
+ */
+# $settings['yaml_parser_class'] = NULL;
+
+/**
+ * Trusted host configuration.
+ *
+ * Drupal core can use the Symfony trusted host mechanism to prevent HTTP Host
+ * header spoofing.
+ *
+ * To enable the trusted host mechanism, you enable your allowable hosts
+ * in $settings['trusted_host_patterns']. This should be an array of regular
+ * expression patterns, without delimiters, representing the hosts you would
+ * like to allow.
+ *
+ * For example:
+ * @code
+ * $settings['trusted_host_patterns'] = [
+ *   '^www\.example\.com$',
+ * ];
+ * @endcode
+ * will allow the site to only run from www.example.com.
+ *
+ * If you are running multisite, or if you are running your site from
+ * different domain names (eg, you don't redirect http://www.example.com to
+ * http://example.com), you should specify all of the host patterns that are
+ * allowed by your site.
+ *
+ * For example:
+ * @code
+ * $settings['trusted_host_patterns'] = [
+ *   '^example\.com$',
+ *   '^.+\.example\.com$',
+ *   '^example\.org$',
+ *   '^.+\.example\.org$',
+ * ];
+ * @endcode
+ * will allow the site to run off of all variants of example.com and
+ * example.org, with all subdomains included.
+ */
+
+/**
+ * The default list of directories that will be ignored by Drupal's file API.
+ *
+ * By default ignore node_modules and bower_components folders to avoid issues
+ * with common frontend tools and recursive scanning of directories looking for
+ * extensions.
+ *
+ * @see \Drupal\Core\File\FileSystemInterface::scanDirectory()
+ * @see \Drupal\Core\Extension\ExtensionDiscovery::scanDirectory()
+ */
+$settings['file_scan_ignore_directories'] = [
+  'node_modules',
+  'bower_components',
+];
+
+/**
+ * The default number of entities to update in a batch process.
+ *
+ * This is used by update and post-update functions that need to go through and
+ * change all the entities on a site, so it is useful to increase this number
+ * if your hosting configuration (i.e. RAM allocation, CPU speed) allows for a
+ * larger number of entities to be processed in a single batch run.
+ */
+$settings['entity_update_batch_size'] = 50;
+
+/**
+ * Entity update backup.
+ *
+ * This is used to inform the entity storage handler that the backup tables as
+ * well as the original entity type and field storage definitions should be
+ * retained after a successful entity update process.
+ */
+$settings['entity_update_backup'] = TRUE;
+
+/**
+ * Node migration type.
+ *
+ * This is used to force the migration system to use the classic node migrations
+ * instead of the default complete node migrations. The migration system will
+ * use the classic node migration only if there are existing migrate_map tables
+ * for the classic node migrations and they contain data. These tables may not
+ * exist if you are developing custom migrations and do not want to use the
+ * complete node migrations. Set this to TRUE to force the use of the classic
+ * node migrations.
+ */
+$settings['migrate_node_migrate_type_classic'] = FALSE;
+
+/**
+ * Load local development override configuration, if available.
+ *
+ * Use settings.local.php to override variables on secondary (staging,
+ * development, etc) installations of this site. Typically used to disable
+ * caching, JavaScript/CSS compression, re-routing of outgoing emails, and
+ * other things that should not happen on development and testing sites.
+ *
+ * Keep this code block at the end of this file to take full effect.
+ */
+#
+# if (file_exists($app_root . '/' . $site_path . '/settings.local.php')) {
+#   include $app_root . '/' . $site_path . '/settings.local.php';
+# }

+ 9 - 0
web/core/assets/scaffold/files/development.services.yml

@@ -0,0 +1,9 @@
+# Local development services.
+#
+# To activate this feature, follow the instructions at the top of the
+# 'example.settings.local.php' file, which sits next to this file.
+parameters:
+  http.response.debug_cacheability_headers: true
+services:
+  cache.backend.null:
+    class: Drupal\Core\Cache\NullBackendFactory

+ 3 - 0
web/core/assets/scaffold/files/drupal.INSTALL.txt

@@ -0,0 +1,3 @@
+
+Please read core/INSTALL.txt for detailed installation instructions for your
+Drupal web site.

+ 143 - 0
web/core/assets/scaffold/files/drupal.README.txt

@@ -0,0 +1,143 @@
+
+CONTENTS OF THIS FILE
+---------------------
+
+ * About Drupal
+ * Configuration and features
+ * Installation profiles
+ * Appearance
+ * Developing for Drupal
+ * More information
+
+
+ABOUT DRUPAL
+------------
+
+Drupal is an open source content management platform supporting a variety of
+websites ranging from personal weblogs to large community-driven websites. For
+more information, see the Drupal website at https://www.drupal.org, and join
+the Drupal community at https://www.drupal.org/community.
+
+Legal information about Drupal:
+ * Know your rights when using Drupal:
+   See LICENSE.txt in the "core" directory.
+ * Learn about the Drupal trademark and logo policy:
+   https://www.drupal.com/trademark
+
+
+CONFIGURATION AND FEATURES
+--------------------------
+
+Drupal core (what you get when you download and extract a drupal-x.y.tar.gz or
+drupal-x.y.zip file from https://www.drupal.org/project/drupal) has what you
+need to get started with your website. It includes several modules (extensions
+that add functionality) for common website features, such as managing content,
+user accounts, image uploading, and search. Core comes with many options that
+allow site-specific configuration. In addition to the core modules, there are
+thousands of contributed modules (for functionality not included with Drupal
+core) available for download.
+
+More about configuration:
+ * Install, update, and maintain Drupal:
+   See INSTALL.txt and UPDATE.txt in the "core" directory.
+ * Learn about how to use Drupal to create your site:
+   https://www.drupal.org/documentation
+ * Follow best practices:
+   https://www.drupal.org/best-practices
+ * Download contributed modules to /modules to extend Drupal's functionality:
+   https://www.drupal.org/project/modules
+ * See also: "Developing for Drupal" for writing your own modules, below.
+
+
+INSTALLATION PROFILES
+---------------------
+
+Installation profiles define additional steps (such as enabling modules,
+defining content types, etc.) that run after the base installation provided
+by core when Drupal is first installed. There are two basic installation
+profiles provided with Drupal core.
+
+Installation profiles from the Drupal community modify the installation process
+to provide a website for a specific use case, such as a CMS for media
+publishers, a web-based project tracking tool, or a full-fledged CRM for
+non-profit organizations raising money and accepting donations. They can be
+distributed as bare installation profiles or as "distributions". Distributions
+include Drupal core, the installation profile, and all other required
+extensions, such as contributed and custom modules, themes, and third-party
+libraries. Bare installation profiles require you to download Drupal Core and
+the required extensions separately; place the downloaded profile in the
+/profiles directory before you start the installation process.
+
+More about installation profiles and distributions:
+ * Read about the difference between installation profiles and distributions:
+   https://www.drupal.org/docs/8/distributions/creating-distributions
+ * Download contributed installation profiles and distributions:
+   https://www.drupal.org/project/distributions
+ * Develop your own installation profile or distribution:
+   https://www.drupal.org/docs/8/creating-distributions
+
+
+APPEARANCE
+----------
+
+In Drupal, the appearance of your site is set by the theme (themes are
+extensions that set fonts, colors, and layout). Drupal core comes with several
+themes. More themes are available for download, and you can also create your own
+custom theme.
+
+More about themes:
+ * Download contributed themes to /themes to modify Drupal's appearance:
+   https://www.drupal.org/project/themes
+ * Develop your own theme:
+   https://www.drupal.org/docs/8/theming
+
+
+DEVELOPING FOR DRUPAL
+---------------------
+
+Drupal contains an extensive API that allows you to add to and modify the
+functionality of your site. The API consists of "hooks", which allow modules to
+react to system events and customize Drupal's behavior, and functions that
+standardize common operations such as database queries and form generation. The
+flexible hook architecture means that you should never need to directly modify
+the files that come with Drupal core to achieve the functionality you want;
+instead, functionality modifications take the form of modules.
+
+When you need new functionality for your Drupal site, search for existing
+contributed modules. If you find a module that matches except for a bug or an
+additional needed feature, change the module and contribute your improvements
+back to the project in the form of a "patch". Create new custom modules only
+when nothing existing comes close to what you need.
+
+More about developing:
+ * Search for existing contributed modules:
+   https://www.drupal.org/project/modules
+ * Contribute a patch:
+   https://www.drupal.org/patch/submit
+ * Develop your own module:
+   https://www.drupal.org/developing/modules
+ * Follow programming best practices:
+   https://www.drupal.org/developing/best-practices
+ * Refer to the API documentation:
+   https://api.drupal.org/api/drupal/8
+ * Learn from documented Drupal API examples:
+   https://www.drupal.org/project/examples
+
+
+MORE INFORMATION
+----------------
+
+ * See the Drupal.org online documentation:
+   https://www.drupal.org/documentation
+
+ * For a list of security announcements, see the "Security advisories" page at
+   https://www.drupal.org/security (available as an RSS feed). This page also
+   describes how to subscribe to these announcements via email.
+
+ * For information about the Drupal security process, or to find out how to
+   report a potential security issue to the Drupal security team, see the
+   "Security team" page at https://www.drupal.org/security-team
+
+ * For information about the wide range of available support options, visit
+   https://www.drupal.org and click on Community and Support in the top or
+   bottom navigation.

+ 17 - 0
web/core/assets/scaffold/files/editorconfig

@@ -0,0 +1,17 @@
+# Drupal editor configuration normalization
+# @see http://editorconfig.org/
+
+# This is the top-most .editorconfig file; do not search in parent directories.
+root = true
+
+# All files.
+[*]
+end_of_line = LF
+indent_style = space
+indent_size = 2
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[composer.{json,lock}]
+indent_size = 4

+ 8 - 0
web/core/assets/scaffold/files/eslintignore

@@ -0,0 +1,8 @@
+core/**/*
+vendor/**/*
+sites/**/files/**/*
+libraries/**/*
+sites/**/libraries/**/*
+profiles/**/libraries/**/*
+**/js_test_files/**/*
+**/node_modules/**/*

+ 3 - 0
web/core/assets/scaffold/files/eslintrc.json

@@ -0,0 +1,3 @@
+{
+  "extends": "./core/.eslintrc.json"
+}

+ 42 - 0
web/core/assets/scaffold/files/example.gitignore

@@ -0,0 +1,42 @@
+# This file contains default .gitignore rules. To use it, copy it to .gitignore,
+# and it will cause files like your settings.php and user-uploaded files to be
+# excluded from Git version control. This is a common strategy to avoid
+# accidentally including private information in public repositories and patch
+# files.
+#
+# Because .gitignore can be specific to your site, this file has a different
+# name; updating Drupal core will not override your custom .gitignore file.
+
+# Ignore core when managing all of a project's dependencies with Composer
+# including Drupal core.
+# core
+
+# Ignore dependencies that are managed with Composer.
+# Generally you should only ignore the root vendor directory. It's important
+# that core/assets/vendor and any other vendor directories within contrib or
+# custom module, theme, etc., are not ignored unless you purposely do so.
+/vendor/
+
+# Ignore configuration files that may contain sensitive information.
+sites/*/settings*.php
+sites/*/services*.yml
+
+# Ignore paths that contain user-generated content.
+sites/*/files
+sites/*/private
+
+# Ignore SimpleTest multi-site environment.
+sites/simpletest
+
+# If you prefer to store your .gitignore file in the sites/ folder, comment
+# or delete the previous settings and uncomment the following ones, instead.
+
+# Ignore configuration files that may contain sensitive information.
+# */settings*.php
+
+# Ignore paths that contain user-generated content.
+# */files
+# */private
+
+# Ignore SimpleTest multi-site environment.
+# simpletest

+ 155 - 0
web/core/assets/scaffold/files/example.settings.local.php

@@ -0,0 +1,155 @@
+<?php
+
+// @codingStandardsIgnoreFile
+
+/**
+ * @file
+ * Local development override configuration feature.
+ *
+ * To activate this feature, copy and rename it such that its path plus
+ * filename is 'sites/default/settings.local.php'. Then, go to the bottom of
+ * 'sites/default/settings.php' and uncomment the commented lines that mention
+ * 'settings.local.php'.
+ *
+ * If you are using a site name in the path, such as 'sites/example.com', copy
+ * this file to 'sites/example.com/settings.local.php', and uncomment the lines
+ * at the bottom of 'sites/example.com/settings.php'.
+ */
+
+/**
+ * Assertions.
+ *
+ * The Drupal project primarily uses runtime assertions to enforce the
+ * expectations of the API by failing when incorrect calls are made by code
+ * under development.
+ *
+ * @see http://php.net/assert
+ * @see https://www.drupal.org/node/2492225
+ *
+ * If you are using PHP 7.0 it is strongly recommended that you set
+ * zend.assertions=1 in the PHP.ini file (It cannot be changed from .htaccess
+ * or runtime) on development machines and to 0 in production.
+ *
+ * @see https://wiki.php.net/rfc/expectations
+ */
+assert_options(ASSERT_ACTIVE, TRUE);
+\Drupal\Component\Assertion\Handle::register();
+
+/**
+ * Enable local development services.
+ */
+$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/development.services.yml';
+
+/**
+ * Show all error messages, with backtrace information.
+ *
+ * In case the error level could not be fetched from the database, as for
+ * example the database connection failed, we rely only on this value.
+ */
+$config['system.logging']['error_level'] = 'verbose';
+
+/**
+ * Disable CSS and JS aggregation.
+ */
+$config['system.performance']['css']['preprocess'] = FALSE;
+$config['system.performance']['js']['preprocess'] = FALSE;
+
+/**
+ * Disable the render cache.
+ *
+ * Note: you should test with the render cache enabled, to ensure the correct
+ * cacheability metadata is present. However, in the early stages of
+ * development, you may want to disable it.
+ *
+ * This setting disables the render cache by using the Null cache back-end
+ * defined by the development.services.yml file above.
+ *
+ * Only use this setting once the site has been installed.
+ */
+# $settings['cache']['bins']['render'] = 'cache.backend.null';
+
+/**
+ * Disable caching for migrations.
+ *
+ * Uncomment the code below to only store migrations in memory and not in the
+ * database. This makes it easier to develop custom migrations.
+ */
+# $settings['cache']['bins']['discovery_migration'] = 'cache.backend.memory';
+
+/**
+ * Disable Internal Page Cache.
+ *
+ * Note: you should test with Internal Page Cache enabled, to ensure the correct
+ * cacheability metadata is present. However, in the early stages of
+ * development, you may want to disable it.
+ *
+ * This setting disables the page cache by using the Null cache back-end
+ * defined by the development.services.yml file above.
+ *
+ * Only use this setting once the site has been installed.
+ */
+# $settings['cache']['bins']['page'] = 'cache.backend.null';
+
+/**
+ * Disable Dynamic Page Cache.
+ *
+ * Note: you should test with Dynamic Page Cache enabled, to ensure the correct
+ * cacheability metadata is present (and hence the expected behavior). However,
+ * in the early stages of development, you may want to disable it.
+ */
+# $settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';
+
+/**
+ * Allow test modules and themes to be installed.
+ *
+ * Drupal ignores test modules and themes by default for performance reasons.
+ * During development it can be useful to install test extensions for debugging
+ * purposes.
+ */
+# $settings['extension_discovery_scan_tests'] = TRUE;
+
+/**
+ * Enable access to rebuild.php.
+ *
+ * This setting can be enabled to allow Drupal's php and database cached
+ * storage to be cleared via the rebuild.php page. Access to this page can also
+ * be gained by generating a query string from rebuild_token_calculator.sh and
+ * using these parameters in a request to rebuild.php.
+ */
+$settings['rebuild_access'] = TRUE;
+
+/**
+ * Skip file system permissions hardening.
+ *
+ * The system module will periodically check the permissions of your site's
+ * site directory to ensure that it is not writable by the website user. For
+ * sites that are managed with a version control system, this can cause problems
+ * when files in that directory such as settings.php are updated, because the
+ * user pulling in the changes won't have permissions to modify files in the
+ * directory.
+ */
+$settings['skip_permissions_hardening'] = TRUE;
+
+/**
+ * Exclude modules from configuration synchronization.
+ *
+ * On config export sync, no config or dependent config of any excluded module
+ * is exported. On config import sync, any config of any installed excluded
+ * module is ignored. In the exported configuration, it will be as if the
+ * excluded module had never been installed. When syncing configuration, if an
+ * excluded module is already installed, it will not be uninstalled by the
+ * configuration synchronization, and dependent configuration will remain
+ * intact. This affects only configuration synchronization; single import and
+ * export of configuration are not affected.
+ *
+ * Drupal does not validate or sanity check the list of excluded modules. For
+ * instance, it is your own responsibility to never exclude required modules,
+ * because it would mean that the exported configuration can not be imported
+ * anymore.
+ *
+ * This is an advanced feature and using it means opting out of some of the
+ * guarantees the configuration synchronization provides. It is not recommended
+ * to use this feature with modules that affect Drupal in a major way such as
+ * the language or field module.
+ */
+# $settings['config_exclude_modules'] = ['devel', 'stage_file_proxy'];

+ 57 - 0
web/core/assets/scaffold/files/example.sites.php

@@ -0,0 +1,57 @@
+<?php
+
+// @codingStandardsIgnoreFile
+
+/**
+ * @file
+ * Configuration file for multi-site support and directory aliasing feature.
+ *
+ * This file is required for multi-site support and also allows you to define a
+ * set of aliases that map hostnames, ports, and pathnames to configuration
+ * directories in the sites directory. These aliases are loaded prior to
+ * scanning for directories, and they are exempt from the normal discovery
+ * rules. See default.settings.php to view how Drupal discovers the
+ * configuration directory when no alias is found.
+ *
+ * Aliases are useful on development servers, where the domain name may not be
+ * the same as the domain of the live server. Since Drupal stores file paths in
+ * the database (files, system table, etc.) this will ensure the paths are
+ * correct when the site is deployed to a live server.
+ *
+ * To activate this feature, copy and rename it such that its path plus
+ * filename is 'sites/sites.php'.
+ *
+ * Aliases are defined in an associative array named $sites. The array is
+ * written in the format: '<port>.<domain>.<path>' => 'directory'. As an
+ * example, to map https://www.drupal.org:8080/mysite/test to the configuration
+ * directory sites/example.com, the array should be defined as:
+ * @code
+ * $sites = [
+ *   '8080.www.drupal.org.mysite.test' => 'example.com',
+ * ];
+ * @endcode
+ * The URL, https://www.drupal.org:8080/mysite/test/, could be a symbolic link
+ * or an Apache Alias directive that points to the Drupal root containing
+ * index.php. An alias could also be created for a subdomain. See the
+ * @link https://www.drupal.org/documentation/install online Drupal installation guide @endlink
+ * for more information on setting up domains, subdomains, and subdirectories.
+ *
+ * The following examples look for a site configuration in sites/example.com:
+ * @code
+ * URL: http://dev.drupal.org
+ * $sites['dev.drupal.org'] = 'example.com';
+ *
+ * URL: http://localhost/example
+ * $sites['localhost.example'] = 'example.com';
+ *
+ * URL: http://localhost:8080/example
+ * $sites['8080.localhost.example'] = 'example.com';
+ *
+ * URL: https://www.drupal.org:8080/mysite/test/
+ * $sites['8080.www.drupal.org.mysite.test'] = 'example.com';
+ * @endcode
+ *
+ * @see default.settings.php
+ * @see \Drupal\Core\DrupalKernel::getSitePath()
+ * @see https://www.drupal.org/documentation/install/multi-site
+ */

+ 61 - 0
web/core/assets/scaffold/files/gitattributes

@@ -0,0 +1,61 @@
+# Drupal git normalization
+# @see https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
+# @see https://www.drupal.org/node/1542048
+
+# Normally these settings would be done with macro attributes for improved
+# readability and easier maintenance. However macros can only be defined at the
+# repository root directory. Drupal avoids making any assumptions about where it
+# is installed.
+
+# Define text file attributes.
+# - Treat them as text.
+# - Ensure no CRLF line-endings, neither on checkout nor on checkin.
+# - Detect whitespace errors.
+#   - Exposed by default in `git diff --color` on the CLI.
+#   - Validate with `git diff --check`.
+#   - Deny applying with `git apply --whitespace=error-all`.
+#   - Fix automatically with `git apply --whitespace=fix`.
+
+*.config  text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.css     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.dist    text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.engine  text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.html    text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=html
+*.inc     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.install text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.js      text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.json    text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.lock    text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.map     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.md      text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.module  text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.php     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.po      text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.profile text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.script  text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.sh      text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.sql     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.svg     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.theme   text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php
+*.twig    text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.txt     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.xml     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+*.yml     text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2
+
+# Define binary file attributes.
+# - Do not treat them as text.
+# - Include binary diff in patches instead of "binary files differ."
+*.eot     -text diff
+*.exe     -text diff
+*.gif     -text diff
+*.gz      -text diff
+*.ico     -text diff
+*.jpeg    -text diff
+*.jpg     -text diff
+*.otf     -text diff
+*.phar    -text diff
+*.png     -text diff
+*.svgz    -text diff
+*.ttf     -text diff
+*.woff    -text diff
+*.woff2   -text diff

+ 65 - 0
web/core/assets/scaffold/files/ht.router.php

@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ * Router script for the built-in PHP web server.
+ *
+ * The built-in web server should only be used for development and testing as it
+ * has a number of limitations that makes running Drupal on it highly insecure
+ * and somewhat limited.
+ *
+ * Note that:
+ * - The server is single-threaded, any requests made during the execution of
+ *   the main request will hang until the main request has been completed.
+ * - The web server does not enforce any of the settings in .htaccess in
+ *   particular a remote user will be able to download files that normally would
+ *   be protected from direct access such as .module files.
+ *
+ * The router script is needed to work around a bug in PHP, see
+ * https://bugs.php.net/bug.php?id=61286.
+ *
+ * Usage:
+ * php -S localhost:8888 .ht.router.php
+ *
+ * @see http://php.net/manual/en/features.commandline.webserver.php
+ */
+
+$url = parse_url($_SERVER['REQUEST_URI']);
+if (file_exists(__DIR__ . $url['path'])) {
+  // Serve the requested resource as-is.
+  return FALSE;
+}
+
+// Work around the PHP bug.
+$path = $url['path'];
+$script = 'index.php';
+if (strpos($path, '.php') !== FALSE) {
+  // Work backwards through the path to check if a script exists. Otherwise
+  // fallback to index.php.
+  do {
+    $path = dirname($path);
+    if (preg_match('/\.php$/', $path) && is_file(__DIR__ . $path)) {
+      // Discovered that the path contains an existing PHP file. Use that as the
+      // script to include.
+      $script = ltrim($path, '/');
+      break;
+    }
+  } while ($path !== '/' && $path !== '.');
+}
+
+// Update $_SERVER variables to point to the correct index-file.
+$index_file_absolute = $_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $script;
+$index_file_relative = DIRECTORY_SEPARATOR . $script;
+
+// SCRIPT_FILENAME will point to the router script itself, it should point to
+// the full path of index.php.
+$_SERVER['SCRIPT_FILENAME'] = $index_file_absolute;
+
+// SCRIPT_NAME and PHP_SELF will either point to index.php or contain the full
+// virtual path being requested depending on the URL being requested. They
+// should always point to index.php relative to document root.
+$_SERVER['SCRIPT_NAME'] = $index_file_relative;
+$_SERVER['PHP_SELF'] = $index_file_relative;
+
+// Require the script and let core take over.
+require $_SERVER['SCRIPT_FILENAME'];

+ 182 - 0
web/core/assets/scaffold/files/htaccess

@@ -0,0 +1,182 @@
+#
+# Apache/PHP/Drupal settings:
+#
+
+# Protect files and directories from prying eyes.
+<FilesMatch "\.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\.(?!well-known).*|Entries.*|Repository|Root|Tag|Template|composer\.(json|lock)|web\.config)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$">
+  <IfModule mod_authz_core.c>
+    Require all denied
+  </IfModule>
+  <IfModule !mod_authz_core.c>
+    Order allow,deny
+  </IfModule>
+</FilesMatch>
+
+# Don't show directory listings for URLs which map to a directory.
+Options -Indexes
+
+# Set the default handler.
+DirectoryIndex index.php index.html index.htm
+
+# Add correct encoding for SVGZ.
+AddType image/svg+xml svg svgz
+AddEncoding gzip svgz
+
+# Most of the following PHP settings cannot be changed at runtime. See
+# sites/default/default.settings.php and
+# Drupal\Core\DrupalKernel::bootEnvironment() for settings that can be
+# changed at runtime.
+
+# PHP 7, Apache 1 and 2.
+<IfModule mod_php7.c>
+  php_value assert.active                   0
+</IfModule>
+
+# Requires mod_expires to be enabled.
+<IfModule mod_expires.c>
+  # Enable expirations.
+  ExpiresActive On
+
+  # Cache all files for 2 weeks after access (A).
+  ExpiresDefault A1209600
+
+  <FilesMatch \.php$>
+    # Do not allow PHP scripts to be cached unless they explicitly send cache
+    # headers themselves. Otherwise all scripts would have to overwrite the
+    # headers set by mod_expires if they want another caching behavior. This may
+    # fail if an error occurs early in the bootstrap process, and it may cause
+    # problems if a non-Drupal PHP file is installed in a subdirectory.
+    ExpiresActive Off
+  </FilesMatch>
+</IfModule>
+
+# Set a fallback resource if mod_rewrite is not enabled. This allows Drupal to
+# work without clean URLs. This requires Apache version >= 2.2.16. If Drupal is
+# not accessed by the top level URL (i.e.: http://example.com/drupal/ instead of
+# http://example.com/), the path to index.php will need to be adjusted.
+<IfModule !mod_rewrite.c>
+  FallbackResource /index.php
+</IfModule>
+
+# Various rewrite rules.
+<IfModule mod_rewrite.c>
+  RewriteEngine on
+
+  # Set "protossl" to "s" if we were accessed via https://.  This is used later
+  # if you enable "www." stripping or enforcement, in order to ensure that
+  # you don't bounce between http and https.
+  RewriteRule ^ - [E=protossl]
+  RewriteCond %{HTTPS} on
+  RewriteRule ^ - [E=protossl:s]
+
+  # Make sure Authorization HTTP header is available to PHP
+  # even when running as CGI or FastCGI.
+  RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+
+  # Block access to "hidden" directories whose names begin with a period. This
+  # includes directories used by version control systems such as Subversion or
+  # Git to store control files. Files whose names begin with a period, as well
+  # as the control files used by CVS, are protected by the FilesMatch directive
+  # above.
+  #
+  # NOTE: This only works when mod_rewrite is loaded. Without mod_rewrite, it is
+  # not possible to block access to entire directories from .htaccess because
+  # <DirectoryMatch> is not allowed here.
+  #
+  # If you do not have mod_rewrite installed, you should remove these
+  # directories from your webroot or otherwise protect them from being
+  # downloaded.
+  RewriteRule "/\.|^\.(?!well-known/)" - [F]
+
+  # If your site can be accessed both with and without the 'www.' prefix, you
+  # can use one of the following settings to redirect users to your preferred
+  # URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
+  #
+  # To redirect all users to access the site WITH the 'www.' prefix,
+  # (http://example.com/foo will be redirected to http://www.example.com/foo)
+  # uncomment the following:
+  # RewriteCond %{HTTP_HOST} .
+  # RewriteCond %{HTTP_HOST} !^www\. [NC]
+  # RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
+  #
+  # To redirect all users to access the site WITHOUT the 'www.' prefix,
+  # (http://www.example.com/foo will be redirected to http://example.com/foo)
+  # uncomment the following:
+  # RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
+  # RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301]
+
+  # Modify the RewriteBase if you are using Drupal in a subdirectory or in a
+  # VirtualDocumentRoot and the rewrite rules are not working properly.
+  # For example if your site is at http://example.com/drupal uncomment and
+  # modify the following line:
+  # RewriteBase /drupal
+  #
+  # If your site is running in a VirtualDocumentRoot at http://example.com/,
+  # uncomment the following line:
+  # RewriteBase /
+
+  # Redirect common PHP files to their new locations.
+  RewriteCond %{REQUEST_URI} ^(.*)?/(install.php) [OR]
+  RewriteCond %{REQUEST_URI} ^(.*)?/(rebuild.php)
+  RewriteCond %{REQUEST_URI} !core
+  RewriteRule ^ %1/core/%2 [L,QSA,R=301]
+
+  # Rewrite install.php during installation to see if mod_rewrite is working
+  RewriteRule ^core/install.php core/install.php?rewrite=ok [QSA,L]
+
+  # Pass all requests not referring directly to files in the filesystem to
+  # index.php.
+  RewriteCond %{REQUEST_FILENAME} !-f
+  RewriteCond %{REQUEST_FILENAME} !-d
+  RewriteCond %{REQUEST_URI} !=/favicon.ico
+  RewriteRule ^ index.php [L]
+
+  # For security reasons, deny access to other PHP files on public sites.
+  # Note: The following URI conditions are not anchored at the start (^),
+  # because Drupal may be located in a subdirectory. To further improve
+  # security, you can replace '!/' with '!^/'.
+  # Allow access to PHP files in /core (like authorize.php or install.php):
+  RewriteCond %{REQUEST_URI} !/core/[^/]*\.php$
+  # Allow access to test-specific PHP files:
+  RewriteCond %{REQUEST_URI} !/core/modules/system/tests/https?.php
+  # Allow access to Statistics module's custom front controller.
+  # Copy and adapt this rule to directly execute PHP files in contributed or
+  # custom modules or to run another PHP application in the same directory.
+  RewriteCond %{REQUEST_URI} !/core/modules/statistics/statistics.php$
+  # Deny access to any other PHP files that do not match the rules above.
+  # Specifically, disallow autoload.php from being served directly.
+  RewriteRule "^(.+/.*|autoload)\.php($|/)" - [F]
+
+  # Rules to correctly serve gzip compressed CSS and JS files.
+  # Requires both mod_rewrite and mod_headers to be enabled.
+  <IfModule mod_headers.c>
+    # Serve gzip compressed CSS files if they exist and the client accepts gzip.
+    RewriteCond %{HTTP:Accept-encoding} gzip
+    RewriteCond %{REQUEST_FILENAME}\.gz -s
+    RewriteRule ^(.*)\.css $1\.css\.gz [QSA]
+
+    # Serve gzip compressed JS files if they exist and the client accepts gzip.
+    RewriteCond %{HTTP:Accept-encoding} gzip
+    RewriteCond %{REQUEST_FILENAME}\.gz -s
+    RewriteRule ^(.*)\.js $1\.js\.gz [QSA]
+
+    # Serve correct content types, and prevent double compression.
+    RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1,E=no-brotli:1]
+    RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1,E=no-brotli:1]
+
+    <FilesMatch "(\.js\.gz|\.css\.gz)$">
+      # Serve correct encoding type.
+      Header set Content-Encoding gzip
+      # Force proxies to cache gzipped & non-gzipped css/js files separately.
+      Header append Vary Accept-Encoding
+    </FilesMatch>
+  </IfModule>
+</IfModule>
+
+# Various header fixes.
+<IfModule mod_headers.c>
+  # Disable content sniffing, since it's an attack vector.
+  Header always set X-Content-Type-Options nosniff
+  # Disable Proxy header, since it's an attack vector.
+  RequestHeader unset Proxy
+</IfModule>

+ 22 - 0
web/core/assets/scaffold/files/index.php

@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * @file
+ * The PHP page that serves all page requests on a Drupal installation.
+ *
+ * All Drupal code is released under the GNU General Public License.
+ * See COPYRIGHT.txt and LICENSE.txt files in the "core" directory.
+ */
+
+use Drupal\Core\DrupalKernel;
+use Symfony\Component\HttpFoundation\Request;
+
+$autoloader = require_once 'autoload.php';
+
+$kernel = new DrupalKernel('prod', $autoloader);
+
+$request = Request::createFromGlobals();
+$response = $kernel->handle($request);
+$response->send();
+
+$kernel->terminate($request, $response);

+ 42 - 0
web/core/assets/scaffold/files/modules.README.txt

@@ -0,0 +1,42 @@
+Modules extend your site functionality beyond Drupal core.
+
+WHAT TO PLACE IN THIS DIRECTORY?
+--------------------------------
+
+Placing downloaded and custom modules in this directory separates downloaded and
+custom modules from Drupal core's modules. This allows Drupal core to be updated
+without overwriting these files.
+
+DOWNLOAD ADDITIONAL MODULES
+---------------------------
+
+Contributed modules from the Drupal community may be downloaded at
+https://www.drupal.org/project/project_module.
+
+ORGANIZING MODULES IN THIS DIRECTORY
+------------------------------------
+
+You may create subdirectories in this directory, to organize your added modules,
+without breaking the site. Some common subdirectories include "contrib" for
+contributed modules, and "custom" for custom modules. Note that if you move a
+module to a subdirectory after it has been enabled, you may need to clear the
+Drupal cache so it can be found.
+
+There are number of directories that are ignored when looking for modules. These
+are 'src', 'lib', 'vendor', 'assets', 'css', 'files', 'images', 'js', 'misc',
+'templates', 'includes', 'fixtures' and 'Drupal'.
+
+MULTISITE CONFIGURATION
+-----------------------
+
+In multisite configurations, modules found in this directory are available to
+all sites. You may also put modules in the sites/all/modules directory, and the
+versions in sites/all/modules will take precedence over versions of the same
+module that are here. Alternatively, the sites/your_site_name/modules directory
+pattern may be used to restrict modules to a specific site instance.
+
+MORE INFORMATION
+----------------
+
+Refer to the “Developing for Drupal” section of the README.txt in the Drupal
+root directory for further information on extending Drupal with custom modules.

+ 28 - 0
web/core/assets/scaffold/files/profiles.README.txt

@@ -0,0 +1,28 @@
+Installation profiles define additional steps that run after the base
+installation of Drupal is completed. They may also offer additional
+functionality and change the behavior of the site.
+
+WHAT TO PLACE IN THIS DIRECTORY?
+--------------------------------
+
+Place downloaded and custom installation profiles in this directory.
+Note that installation profiles are generally provided as part of a Drupal
+distribution.
+
+DOWNLOAD ADDITIONAL DISTRIBUTIONS
+---------------------------------
+
+Contributed distributions from the Drupal community may be downloaded at
+https://www.drupal.org/project/project_distribution.
+
+MULTISITE CONFIGURATION
+-----------------------
+
+In multisite configurations, installation profiles found in this directory are
+available to all sites during their initial site installation.
+
+MORE INFORMATION
+----------------
+
+Refer to the "Installation profiles" section of the README.txt in the Drupal
+root directory for further information on extending Drupal with custom profiles.

+ 61 - 0
web/core/assets/scaffold/files/robots.txt

@@ -0,0 +1,61 @@
+#
+# robots.txt
+#
+# This file is to prevent the crawling and indexing of certain parts
+# of your site by web crawlers and spiders run by sites like Yahoo!
+# and Google. By telling these "robots" where not to go on your site,
+# you save bandwidth and server resources.
+#
+# This file will be ignored unless it is at the root of your host:
+# Used:    http://example.com/robots.txt
+# Ignored: http://example.com/site/robots.txt
+#
+# For more information about the robots.txt standard, see:
+# http://www.robotstxt.org/robotstxt.html
+
+User-agent: *
+# CSS, JS, Images
+Allow: /core/*.css$
+Allow: /core/*.css?
+Allow: /core/*.js$
+Allow: /core/*.js?
+Allow: /core/*.gif
+Allow: /core/*.jpg
+Allow: /core/*.jpeg
+Allow: /core/*.png
+Allow: /core/*.svg
+Allow: /profiles/*.css$
+Allow: /profiles/*.css?
+Allow: /profiles/*.js$
+Allow: /profiles/*.js?
+Allow: /profiles/*.gif
+Allow: /profiles/*.jpg
+Allow: /profiles/*.jpeg
+Allow: /profiles/*.png
+Allow: /profiles/*.svg
+# Directories
+Disallow: /core/
+Disallow: /profiles/
+# Files
+Disallow: /README.txt
+Disallow: /web.config
+# Paths (clean URLs)
+Disallow: /admin/
+Disallow: /comment/reply/
+Disallow: /filter/tips
+Disallow: /node/add/
+Disallow: /search/
+Disallow: /user/register/
+Disallow: /user/password/
+Disallow: /user/login/
+Disallow: /user/logout/
+# Paths (no clean URLs)
+Disallow: /index.php/admin/
+Disallow: /index.php/comment/reply/
+Disallow: /index.php/filter/tips
+Disallow: /index.php/node/add/
+Disallow: /index.php/search/
+Disallow: /index.php/user/password/
+Disallow: /index.php/user/register/
+Disallow: /index.php/user/login/
+Disallow: /index.php/user/logout/

+ 10 - 0
web/core/assets/scaffold/files/sites.README.txt

@@ -0,0 +1,10 @@
+This directory structure contains the settings and configuration files specific
+to your site or sites and is an integral part of multisite configurations.
+
+It is now recommended to place your custom and downloaded extensions in the
+/modules, /themes, and /profiles directories located in the Drupal root. The
+sites/all/ subdirectory structure, which was recommended in previous versions
+of Drupal, is still supported.
+
+See core/INSTALL.txt for information about single-site installation or
+multisite configuration.

+ 31 - 0
web/core/assets/scaffold/files/themes.README.txt

@@ -0,0 +1,31 @@
+Themes allow you to change the look and feel of your Drupal site. You can use
+themes contributed by others or create your own.
+
+WHAT TO PLACE IN THIS DIRECTORY?
+--------------------------------
+
+Placing downloaded and custom themes in this directory separates downloaded and
+custom themes from Drupal core's themes. This allows Drupal core to be updated
+without overwriting these files.
+
+DOWNLOAD ADDITIONAL THEMES
+--------------------------
+
+Contributed themes from the Drupal community may be downloaded at
+https://www.drupal.org/project/project_theme.
+
+MULTISITE CONFIGURATION
+-----------------------
+
+In multisite configurations, themes found in this directory are available to
+all sites. You may also put themes in the sites/all/themes directory, and the
+versions in sites/all/themes will take precedence over versions of the same
+themes that are here. Alternatively, the sites/your_site_name/themes directory
+pattern may be used to restrict themes to a specific site instance.
+
+MORE INFORMATION
+-----------------
+
+Refer to the "Appearance" section of the README.txt in the Drupal root directory
+for further information on customizing the appearance of Drupal with custom
+themes.

+ 31 - 0
web/core/assets/scaffold/files/update.php

@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * The PHP page that handles updating the Drupal installation.
+ *
+ * All Drupal code is released under the GNU General Public License.
+ * See COPYRIGHT.txt and LICENSE.txt files in the "core" directory.
+ */
+
+use Drupal\Core\Update\UpdateKernel;
+use Symfony\Component\HttpFoundation\Request;
+
+$autoloader = require_once 'autoload.php';
+
+// Disable garbage collection during test runs. Under certain circumstances the
+// update path will create so many objects that garbage collection causes
+// segmentation faults.
+require_once 'core/includes/bootstrap.inc';
+if (drupal_valid_test_ua()) {
+  gc_collect_cycles();
+  gc_disable();
+}
+
+$kernel = new UpdateKernel('prod', $autoloader, FALSE);
+$request = Request::createFromGlobals();
+
+$response = $kernel->handle($request);
+$response->send();
+
+$kernel->terminate($request, $response);

+ 103 - 0
web/core/assets/scaffold/files/web.config

@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+  <system.webServer>
+    <!-- Don't show directory listings for URLs which map to a directory. -->
+    <directoryBrowse enabled="false" />
+
+    <!--
+       Caching configuration was not delegated by default. Some hosters may not
+       delegate the caching configuration to site owners by default and that
+       may cause errors when users install. Uncomment this if you want to and
+       are allowed to enable caching.
+     -->
+    <!--
+    <caching>
+      <profiles>
+        <add extension=".php" policy="DisableCache" kernelCachePolicy="DisableCache" />
+        <add extension=".html" policy="CacheForTimePeriod" kernelCachePolicy="CacheForTimePeriod" duration="14:00:00" />
+      </profiles>
+    </caching>
+     -->
+
+    <rewrite>
+      <rules>
+        <rule name="Protect files and directories from prying eyes" stopProcessing="true">
+          <match url="\.(engine|inc|install|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|yml|svn-base)$|^(code-style\.pl|Entries.*|Repository|Root|Tag|Template|all-wcprops|entries|format|composer\.(json|lock)|\.htaccess)$" />
+          <action type="CustomResponse" statusCode="403" subStatusCode="0" statusReason="Forbidden" statusDescription="Access is forbidden." />
+        </rule>
+
+        <rule name="Force simple error message for requests for non-existent favicon.ico" stopProcessing="true">
+          <match url="favicon\.ico" />
+          <action type="CustomResponse" statusCode="404" subStatusCode="1" statusReason="File Not Found" statusDescription="The requested file favicon.ico was not found" />
+          <conditions>
+            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
+          </conditions>
+        </rule>
+     <!-- If running on a PHP version affected by httpoxy vulnerability
+      uncomment the following rule to mitigate it's impact. To make this
+      rule work, you will also need to add HTTP_PROXY to the allowed server
+      variables manually in IIS. See https://www.drupal.org/node/2783079.
+        <rule name="Erase HTTP_PROXY" patternSyntax="Wildcard">
+          <match url="*.*" />
+          <serverVariables>
+            <set name="HTTP_PROXY" value="" />
+          </serverVariables>
+          <action type="None" />
+        </rule>
+    -->
+    <!-- To redirect all users to access the site WITH the 'www.' prefix,
+     http://example.com/foo will be redirected to http://www.example.com/foo)
+     adapt and uncomment the following:   -->
+    <!--
+        <rule name="Redirect to add www" stopProcessing="true">
+          <match url="^(.*)$" ignoreCase="false" />
+          <conditions>
+            <add input="{HTTP_HOST}" pattern="^example\.com$" />
+          </conditions>
+          <action type="Redirect" redirectType="Permanent" url="http://www.example.com/{R:1}" />
+        </rule>
+    -->
+
+    <!-- To redirect all users to access the site WITHOUT the 'www.' prefix,
+     http://www.example.com/foo will be redirected to http://example.com/foo)
+     adapt and uncomment the following:   -->
+    <!--
+        <rule name="Redirect to remove www" stopProcessing="true">
+          <match url="^(.*)$" ignoreCase="false" />
+          <conditions>
+            <add input="{HTTP_HOST}" pattern="^www\.example\.com$" />
+          </conditions>
+          <action type="Redirect" redirectType="Permanent" url="http://example.com/{R:1}" />
+        </rule>
+    -->
+
+        <!-- Pass all requests not referring directly to files in the filesystem
+         to index.php. -->
+        <rule name="Short URLS" stopProcessing="true">
+          <match url="^(.*)$" ignoreCase="false" />
+          <conditions>
+            <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
+            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
+            <add input="{URL}" pattern="^/favicon.ico$" ignoreCase="false" negate="true" />
+          </conditions>
+          <action type="Rewrite" url="index.php" />
+        </rule>
+      </rules>
+    </rewrite>
+
+  <!-- If running Windows Server 2008 R2 this can be commented out -->
+    <!-- httpErrors>
+      <remove statusCode="404" subStatusCode="-1" />
+      <error statusCode="404" prefixLanguageFilePath="" path="/index.php" responseMode="ExecuteURL" />
+    </httpErrors -->
+
+    <defaultDocument>
+     <!-- Set the default document -->
+      <files>
+         <clear />
+        <add value="index.php" />
+      </files>
+    </defaultDocument>
+
+  </system.webServer>
+</configuration>

+ 202 - 0
web/core/authorize.php

@@ -0,0 +1,202 @@
+<?php
+
+/**
+ * @file
+ * Administrative script for running authorized file operations.
+ *
+ * Using this script, the site owner (the user actually owning the files on the
+ * webserver) can authorize certain file-related operations to proceed with
+ * elevated privileges, for example to deploy and upgrade modules or themes.
+ * Users should not visit this page directly, but instead use an administrative
+ * user interface which knows how to redirect the user to this script as part of
+ * a multistep process. This script actually performs the selected operations
+ * without loading all of Drupal, to be able to more gracefully recover from
+ * errors. Access to the script is controlled by a global killswitch in
+ * settings.php ('allow_authorize_operations') and via the 'administer software
+ * updates' permission.
+ *
+ * There are helper functions for setting up an operation to run via this
+ * system in modules/system/system.module. For more information, see:
+ * @link authorize Authorized operation helper functions @endlink
+ */
+
+use Drupal\Core\DrupalKernel;
+use Drupal\Core\Form\EnforcedResponseException;
+use Drupal\Core\Url;
+use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Drupal\Core\Site\Settings;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\Routing\Route;
+
+// Change the directory to the Drupal root.
+chdir('..');
+
+$autoloader = require_once 'autoload.php';
+
+/**
+ * Global flag to identify update.php and authorize.php runs.
+ *
+ * Identifies update.php and authorize.php runs, avoiding unwanted operations
+ * such as css/js preprocessing and translation, and solves some theming issues.
+ * The flag is checked in other places in Drupal code (not just authorize.php).
+ */
+const MAINTENANCE_MODE = 'update';
+
+/**
+ * Determines if the current user is allowed to run authorize.php.
+ *
+ * The killswitch in settings.php overrides all else, otherwise, the user must
+ * have access to the 'administer software updates' permission.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ *   The incoming request.
+ *
+ * @return bool
+ *   TRUE if the current user can run authorize.php, and FALSE if not.
+ */
+function authorize_access_allowed(Request $request) {
+  $account = \Drupal::service('authentication')->authenticate($request);
+  if ($account) {
+    \Drupal::currentUser()->setAccount($account);
+  }
+  return Settings::get('allow_authorize_operations', TRUE) && \Drupal::currentUser()->hasPermission('administer software updates');
+}
+
+try {
+  $request = Request::createFromGlobals();
+  $kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
+  $kernel->boot();
+  // A route is required for route matching.
+  $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('<none>'));
+  $request->attributes->set(RouteObjectInterface::ROUTE_NAME, '<none>');
+  $kernel->preHandle($request);
+  // Ensure our request includes the session if appropriate.
+  if (PHP_SAPI !== 'cli') {
+    $request->setSession($kernel->getContainer()->get('session'));
+  }
+}
+catch (HttpExceptionInterface $e) {
+  $response = new Response('', $e->getStatusCode());
+  $response->prepare($request)->send();
+  exit;
+}
+
+// We have to enable the user and system modules, even to check access and
+// display errors via the maintenance theme.
+\Drupal::moduleHandler()->addModule('system', 'core/modules/system');
+\Drupal::moduleHandler()->addModule('user', 'core/modules/user');
+\Drupal::moduleHandler()->load('system');
+\Drupal::moduleHandler()->load('user');
+
+// Initialize the maintenance theme for this administrative script.
+drupal_maintenance_theme();
+
+$content = [];
+$show_messages = TRUE;
+
+$is_allowed = authorize_access_allowed($request);
+
+// Build content.
+if ($is_allowed) {
+  // Load both the Form API and Batch API.
+  require_once __DIR__ . '/includes/form.inc';
+  require_once __DIR__ . '/includes/batch.inc';
+
+  $page_title = $request->getSession()->get('authorize_page_title', t('Authorize file system changes'));
+
+  // See if we've run the operation and need to display a report.
+  if ($results = $request->getSession()->remove('authorize_results')) {
+
+    // Clear the session out.
+    $request->getSession()->remove('authorize_operation');
+    $request->getSession()->remove('authorize_filetransfer_info');
+
+    if (!empty($results['page_title'])) {
+      $page_title = $results['page_title'];
+    }
+    if (!empty($results['page_message'])) {
+      \Drupal::messenger()->addMessage($results['page_message']['message'], $results['page_message']['type']);
+    }
+
+    $content['authorize_report'] = [
+      '#theme' => 'authorize_report',
+      '#messages' => $results['messages'],
+    ];
+
+    if (is_array($results['tasks'])) {
+      $links = $results['tasks'];
+    }
+    else {
+      // Since this is being called outside of the primary front controller,
+      // the base_url needs to be set explicitly to ensure that links are
+      // relative to the site root.
+      // @todo Simplify with https://www.drupal.org/node/2548095
+      $default_options = [
+        '#type' => 'link',
+        '#options' => [
+          'absolute' => TRUE,
+          'base_url' => $GLOBALS['base_url'],
+        ],
+      ];
+      $links = [
+        $default_options + [
+          '#url' => Url::fromRoute('system.admin'),
+          '#title' => t('Administration pages'),
+        ],
+        $default_options + [
+          '#url' => Url::fromRoute('<front>'),
+          '#title' => t('Front page'),
+        ],
+      ];
+    }
+
+    $content['next_steps'] = [
+      '#theme' => 'item_list',
+      '#items' => $links,
+      '#title' => t('Next steps'),
+    ];
+  }
+  // If a batch is running, let it run.
+  elseif ($request->query->has('batch')) {
+    $content = _batch_page($request);
+    // If _batch_page() returns a response object (likely a JsonResponse for
+    // JavaScript-based batch processing), send it immediately.
+    if ($content instanceof Response) {
+      $content->send();
+      exit;
+    }
+  }
+  else {
+    if (!$request->getSession()->has('authorize_operation') || !$request->getSession()->has('authorize_filetransfer_info')) {
+      $content = ['#markup' => t('It appears you have reached this page in error.')];
+    }
+    elseif (!$batch = batch_get()) {
+      // We have a batch to process, show the filetransfer form.
+      try {
+        $content = \Drupal::formBuilder()->getForm('Drupal\Core\FileTransfer\Form\FileTransferAuthorizeForm');
+      }
+      catch (EnforcedResponseException $e) {
+        $e->getResponse()->send();
+        exit;
+      }
+    }
+  }
+  // We defer the display of messages until all operations are done.
+  $show_messages = !(($batch = batch_get()) && isset($batch['running']));
+}
+else {
+  \Drupal::logger('access denied')->warning('authorize.php');
+  $page_title = t('Access denied');
+  $content = ['#markup' => t('You are not allowed to access this page.')];
+}
+
+$bare_html_page_renderer = \Drupal::service('bare_html_page_renderer');
+$response = $bare_html_page_renderer->renderBarePage($content, $page_title, 'maintenance_page', [
+  '#show_messages' => $show_messages,
+]);
+if (!$is_allowed) {
+  $response->setStatusCode(403);
+}
+$response->send();

+ 227 - 0
web/core/composer.json

@@ -0,0 +1,227 @@
+{
+    "name": "drupal/core",
+    "description": "Drupal is an open source content management platform powering millions of websites and applications.",
+    "type": "drupal-core",
+    "license": "GPL-2.0-or-later",
+    "require": {
+        "ext-date": "*",
+        "ext-dom": "*",
+        "ext-filter": "*",
+        "ext-gd": "*",
+        "ext-hash": "*",
+        "ext-json": "*",
+        "ext-pcre": "*",
+        "ext-PDO": "*",
+        "ext-session": "*",
+        "ext-SimpleXML": "*",
+        "ext-SPL": "*",
+        "ext-tokenizer": "*",
+        "ext-xml": "*",
+        "php": ">=7.0.8",
+        "symfony/class-loader": "~3.4.0",
+        "symfony/console": "~3.4.0",
+        "symfony/dependency-injection": "~3.4.26",
+        "symfony/event-dispatcher": "~3.4.0",
+        "symfony/http-foundation": "~3.4.35",
+        "symfony/http-kernel": "~3.4.14",
+        "symfony/routing": "~3.4.0",
+        "symfony/serializer": "~3.4.0",
+        "symfony/translation": "~3.4.0",
+        "symfony/validator": "~3.4.0",
+        "symfony/process": "~3.4.0",
+        "symfony/polyfill-iconv": "^1.0",
+        "symfony/yaml": "~3.4.5",
+        "typo3/phar-stream-wrapper": "^3.1.3",
+        "twig/twig": "^1.38.2",
+        "doctrine/common": "^2.7",
+        "doctrine/annotations": "^1.4",
+        "guzzlehttp/guzzle": "^6.3",
+        "symfony-cmf/routing": "^1.4",
+        "easyrdf/easyrdf": "^0.9",
+        "laminas/laminas-feed": "^2.12",
+        "stack/builder": "^1.0",
+        "egulias/email-validator": "^2.0",
+        "masterminds/html5": "^2.1",
+        "symfony/psr-http-message-bridge": "^1.1.2",
+        "laminas/laminas-diactoros": "^1.8",
+        "composer/semver": "^1.0",
+        "asm89/stack-cors": "^1.1",
+        "pear/archive_tar": "^1.4.9",
+        "psr/log": "^1.0"
+    },
+    "conflict": {
+        "drupal/pathauto": "<1.6",
+        "drush/drush": "<8.1.10"
+    },
+    "replace": {
+        "drupal/action": "self.version",
+        "drupal/aggregator": "self.version",
+        "drupal/automated_cron": "self.version",
+        "drupal/bartik": "self.version",
+        "drupal/ban": "self.version",
+        "drupal/basic_auth": "self.version",
+        "drupal/big_pipe": "self.version",
+        "drupal/block": "self.version",
+        "drupal/block_content": "self.version",
+        "drupal/block_place": "self.version",
+        "drupal/book": "self.version",
+        "drupal/breakpoint": "self.version",
+        "drupal/ckeditor": "self.version",
+        "drupal/claro": "self.version",
+        "drupal/classy": "self.version",
+        "drupal/color": "self.version",
+        "drupal/comment": "self.version",
+        "drupal/config": "self.version",
+        "drupal/config_translation": "self.version",
+        "drupal/contact": "self.version",
+        "drupal/content_moderation": "self.version",
+        "drupal/content_translation": "self.version",
+        "drupal/contextual": "self.version",
+        "drupal/core-annotation": "self.version",
+        "drupal/core-assertion": "self.version",
+        "drupal/core-bridge": "self.version",
+        "drupal/core-class-finder": "self.version",
+        "drupal/core-datetime": "self.version",
+        "drupal/core-dependency-injection": "self.version",
+        "drupal/core-diff": "self.version",
+        "drupal/core-discovery": "self.version",
+        "drupal/core-event-dispatcher": "self.version",
+        "drupal/core-file-cache": "self.version",
+        "drupal/core-file-security": "self.version",
+        "drupal/core-filesystem": "self.version",
+        "drupal/core-gettext": "self.version",
+        "drupal/core-graph": "self.version",
+        "drupal/core-http-foundation": "self.version",
+        "drupal/core-php-storage": "self.version",
+        "drupal/core-plugin": "self.version",
+        "drupal/core-proxy-builder": "self.version",
+        "drupal/core-render": "self.version",
+        "drupal/core-serialization": "self.version",
+        "drupal/core-transliteration": "self.version",
+        "drupal/core-utility": "self.version",
+        "drupal/core-uuid": "self.version",
+        "drupal/core-version": "self.version",
+        "drupal/datetime": "self.version",
+        "drupal/datetime_range": "self.version",
+        "drupal/dblog": "self.version",
+        "drupal/dynamic_page_cache": "self.version",
+        "drupal/editor": "self.version",
+        "drupal/entity_reference": "self.version",
+        "drupal/field": "self.version",
+        "drupal/field_layout": "self.version",
+        "drupal/field_ui": "self.version",
+        "drupal/file": "self.version",
+        "drupal/filter": "self.version",
+        "drupal/forum": "self.version",
+        "drupal/hal": "self.version",
+        "drupal/help": "self.version",
+        "drupal/help_topics": "self.version",
+        "drupal/history": "self.version",
+        "drupal/image": "self.version",
+        "drupal/inline_form_errors": "self.version",
+        "drupal/jsonapi": "self.version",
+        "drupal/language": "self.version",
+        "drupal/layout_builder": "self.version",
+        "drupal/layout_discovery": "self.version",
+        "drupal/link": "self.version",
+        "drupal/locale": "self.version",
+        "drupal/minimal": "self.version",
+        "drupal/media": "self.version",
+        "drupal/media_library": "self.version",
+        "drupal/menu_link_content": "self.version",
+        "drupal/menu_ui": "self.version",
+        "drupal/migrate": "self.version",
+        "drupal/migrate_drupal": "self.version",
+        "drupal/migrate_drupal_multilingual": "self.version",
+        "drupal/migrate_drupal_ui": "self.version",
+        "drupal/node": "self.version",
+        "drupal/options": "self.version",
+        "drupal/page_cache": "self.version",
+        "drupal/path": "self.version",
+        "drupal/path_alias": "self.version",
+        "drupal/quickedit": "self.version",
+        "drupal/rdf": "self.version",
+        "drupal/responsive_image": "self.version",
+        "drupal/rest": "self.version",
+        "drupal/search": "self.version",
+        "drupal/serialization": "self.version",
+        "drupal/settings_tray": "self.version",
+        "drupal/seven": "self.version",
+        "drupal/shortcut": "self.version",
+        "drupal/simpletest": "self.version",
+        "drupal/standard": "self.version",
+        "drupal/stark": "self.version",
+        "drupal/statistics": "self.version",
+        "drupal/syslog": "self.version",
+        "drupal/system": "self.version",
+        "drupal/taxonomy": "self.version",
+        "drupal/telephone": "self.version",
+        "drupal/text": "self.version",
+        "drupal/toolbar": "self.version",
+        "drupal/tour": "self.version",
+        "drupal/tracker": "self.version",
+        "drupal/update": "self.version",
+        "drupal/user": "self.version",
+        "drupal/views": "self.version",
+        "drupal/views_ui": "self.version",
+        "drupal/workflows": "self.version",
+        "drupal/workspaces": "self.version"
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true,
+    "autoload": {
+        "psr-4": {
+            "Drupal\\Core\\": "lib/Drupal/Core",
+            "Drupal\\Component\\": "lib/Drupal/Component",
+            "Drupal\\Driver\\": "../drivers/lib/Drupal/Driver"
+        },
+        "classmap": [
+            "lib/Drupal.php",
+            "lib/Drupal/Component/Utility/Timer.php",
+            "lib/Drupal/Component/Utility/Unicode.php",
+            "lib/Drupal/Core/Database/Database.php",
+            "lib/Drupal/Core/DrupalKernel.php",
+            "lib/Drupal/Core/DrupalKernelInterface.php",
+            "lib/Drupal/Core/Site/Settings.php"
+        ]
+    },
+    "config": {
+        "preferred-install": "dist",
+        "autoloader-suffix": "Drupal8"
+    },
+    "extra": {
+        "drupal-scaffold": {
+            "file-mapping": {
+                "[project-root]/.editorconfig": "assets/scaffold/files/editorconfig",
+                "[project-root]/.gitattributes": "assets/scaffold/files/gitattributes",
+                "[web-root]/.csslintrc": "assets/scaffold/files/csslintrc",
+                "[web-root]/.eslintignore": "assets/scaffold/files/eslintignore",
+                "[web-root]/.eslintrc.json": "assets/scaffold/files/eslintrc.json",
+                "[web-root]/.ht.router.php": "assets/scaffold/files/ht.router.php",
+                "[web-root]/.htaccess": "assets/scaffold/files/htaccess",
+                "[web-root]/example.gitignore": "assets/scaffold/files/example.gitignore",
+                "[web-root]/index.php": "assets/scaffold/files/index.php",
+                "[web-root]/INSTALL.txt": "assets/scaffold/files/drupal.INSTALL.txt",
+                "[web-root]/README.txt": "assets/scaffold/files/drupal.README.txt",
+                "[web-root]/robots.txt": "assets/scaffold/files/robots.txt",
+                "[web-root]/update.php": "assets/scaffold/files/update.php",
+                "[web-root]/web.config": "assets/scaffold/files/web.config",
+                "[web-root]/sites/README.txt": "assets/scaffold/files/sites.README.txt",
+                "[web-root]/sites/development.services.yml": "assets/scaffold/files/development.services.yml",
+                "[web-root]/sites/example.settings.local.php": "assets/scaffold/files/example.settings.local.php",
+                "[web-root]/sites/example.sites.php": "assets/scaffold/files/example.sites.php",
+                "[web-root]/sites/default/default.services.yml": "assets/scaffold/files/default.services.yml",
+                "[web-root]/sites/default/default.settings.php": "assets/scaffold/files/default.settings.php",
+                "[web-root]/modules/README.txt": "assets/scaffold/files/modules.README.txt",
+                "[web-root]/profiles/README.txt": "assets/scaffold/files/profiles.README.txt",
+                "[web-root]/themes/README.txt": "assets/scaffold/files/themes.README.txt"
+            }
+        }
+    },
+    "scripts": {
+        "pre-autoload-dump": "Drupal\\Core\\Composer\\Composer::preAutoloadDump",
+        "post-autoload-dump": [
+          "Drupal\\Core\\Composer\\Composer::ensureHtaccess"
+        ]
+    }
+}

+ 3 - 0
web/core/config/install/core.extension.yml

@@ -0,0 +1,3 @@
+module: {}
+theme: {}
+profile: ''

+ 1 - 0
web/core/config/install/core.menu.static_menu_link_overrides.yml

@@ -0,0 +1 @@
+definitions: []

+ 832 - 0
web/core/config/schema/core.data_types.schema.yml

@@ -0,0 +1,832 @@
+# Base types provided by Drupal core.
+
+# Read https://www.drupal.org/node/1905070 for more details about configuration
+# schema, types and type resolution.
+
+# Undefined type used by the system to assign to elements at any level where
+# configuration schema is not defined. Using explicitly has the same effect as
+# not defining schema, so there is no point in doing that.
+undefined:
+  label: 'Undefined'
+  class: '\Drupal\Core\Config\Schema\Undefined'
+
+# Explicit type to use when no data typing is possible. Instead of using this
+# type, we strongly suggest you use configuration structures that can be
+# described with other structural elements of schema, and describe your schema
+# with those elements.
+ignore:
+  label: 'Ignore'
+  class: '\Drupal\Core\Config\Schema\Ignore'
+
+# Basic scalar data types from typed data.
+boolean:
+  label: 'Boolean'
+  class: '\Drupal\Core\TypedData\Plugin\DataType\BooleanData'
+email:
+  label: 'Email'
+  class: '\Drupal\Core\TypedData\Plugin\DataType\Email'
+integer:
+  label: 'Integer'
+  class: '\Drupal\Core\TypedData\Plugin\DataType\IntegerData'
+timestamp:
+  label: 'Timestamp'
+  class: '\Drupal\Core\TypedData\Plugin\DataType\Timestamp'
+float:
+  label: 'Float'
+  class: '\Drupal\Core\TypedData\Plugin\DataType\FloatData'
+string:
+  label: 'String'
+  class: '\Drupal\Core\TypedData\Plugin\DataType\StringData'
+uri:
+  label: 'Uri'
+  class: '\Drupal\Core\TypedData\Plugin\DataType\Uri'
+
+# Container data types for lists with known and unknown keys.
+mapping:
+  label: Mapping
+  class: '\Drupal\Core\Config\Schema\Mapping'
+  definition_class: '\Drupal\Core\TypedData\MapDataDefinition'
+sequence:
+  label: Sequence
+  class: '\Drupal\Core\Config\Schema\Sequence'
+  definition_class: '\Drupal\Core\Config\Schema\SequenceDataDefinition'
+
+# Simple extended data types:
+
+# Human readable string that must be plain text and editable with a text field.
+label:
+  type: string
+  label: 'Label'
+  translatable: true
+
+# String containing plural variants, separated by EXT.
+plural_label:
+  type: label
+  label: 'Plural variants'
+
+# Internal Drupal path
+path:
+  type: string
+  label: 'Path'
+
+# Human readable string that can contain multiple lines of text or HTML.
+text:
+  type: string
+  label: 'Text'
+  translatable: true
+
+# A UUID.
+uuid:
+  type: string
+  label: 'UUID'
+  constraints:
+    Uuid: {}
+
+# PHP Date format string that is translatable.
+date_format:
+  type: string
+  label: 'Date format'
+  translatable: true
+  translation context: 'PHP date format'
+
+# HTML color value.
+color_hex:
+  type: string
+  label: 'Color'
+
+# Complex extended data types:
+
+# Root of a configuration object.
+
+_core_config_info:
+  type: mapping
+  mapping:
+    default_config_hash:
+      type: string
+      label: 'Default configuration hash'
+
+config_object:
+  type: mapping
+  mapping:
+    langcode:
+      type: string
+      label: 'Language code'
+    _core:
+      type: _core_config_info
+
+# Mail text with subject and body parts.
+mail:
+  type: mapping
+  label: 'Mail'
+  mapping:
+    subject:
+      type: label
+      label: 'Subject'
+    body:
+      type: text
+      label: 'Body'
+
+# Filter with module and status.
+filter:
+  type: mapping
+  label: 'Filter'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    provider:
+      type: string
+      label: 'Provider'
+    status:
+      type: boolean
+      label: 'Status'
+    weight:
+      type: integer
+      label: 'Weight'
+    settings:
+      type: filter_settings.[%parent.id]
+
+# System action configuration base.
+action_configuration_default:
+  type: sequence
+  label: 'Action configuration'
+  sequence:
+    type: string
+
+theme_settings:
+  type: config_object
+  mapping:
+    favicon:
+      type: mapping
+      label: 'Shortcut icon settings'
+      mapping:
+        mimetype:
+          type: string
+          label: 'MIME type'
+        path:
+          type: string
+          label: 'Path'
+        url:
+          type: string
+          label: 'URL'
+        use_default:
+          type: boolean
+          label: 'Use the default shortcut icon supplied by the theme'
+    features:
+      type: mapping
+      label: 'Optional features'
+      mapping:
+        comment_user_picture:
+          type: boolean
+          label: 'User pictures in comments'
+        comment_user_verification:
+          type: boolean
+          label: 'User verification status in comments'
+        favicon:
+          type: boolean
+          label: 'Shortcut icon'
+        logo:
+          type: boolean
+          label: 'Logo'
+        name:
+          type: boolean
+          label: 'Site name'
+        node_user_picture:
+          type: boolean
+          label: 'User pictures in posts'
+        slogan:
+          type: boolean
+          label: 'Site slogan'
+    logo:
+      type: mapping
+      label: 'Logo settings'
+      mapping:
+        path:
+          type: string
+          label: 'Logo path'
+        url:
+          type: uri
+          label: 'URL'
+        use_default:
+          type: boolean
+          label: 'Use default'
+    third_party_settings:
+      type: sequence
+      label: 'Third party settings'
+      sequence:
+        type: theme_settings.third_party.[%key]
+
+# Array of routes with route_name and route_params keys.
+route:
+  type: mapping
+  label: 'Route'
+  mapping:
+    route_name:
+      type: string
+      label: 'Route Name'
+    route_params:
+      type: sequence
+      label: 'Route Params'
+      sequence:
+        type: string
+        label: 'Param'
+
+# Config dependencies.
+config_dependencies_base:
+  type: mapping
+  mapping:
+    config:
+      type: sequence
+      label: 'Configuration entity dependencies'
+      sequence:
+        type: string
+    content:
+      type: sequence
+      label: 'Content entity dependencies'
+      sequence:
+        type: string
+    module:
+      type: sequence
+      label: 'Module dependencies'
+      sequence:
+        type: string
+    theme:
+      type: sequence
+      label: 'Theme dependencies'
+      sequence:
+        type: string
+
+config_dependencies:
+  type: config_dependencies_base
+  label: 'Configuration dependencies'
+  mapping:
+    enforced:
+      type: config_dependencies_base
+      label: 'Enforced configuration dependencies'
+
+config_entity:
+  type: mapping
+  mapping:
+    uuid:
+      type: uuid
+      label: 'UUID'
+    langcode:
+      type: string
+      label: 'Language code'
+    status:
+      type: boolean
+      label: 'Status'
+    dependencies:
+      type: config_dependencies
+      label: 'Dependencies'
+    third_party_settings:
+      type: sequence
+      label: 'Third party settings'
+      sequence:
+        type: '[%parent.%parent.%type].third_party.[%key]'
+    _core:
+      type: _core_config_info
+
+block.settings.*:
+  type: block_settings
+
+block_settings:
+  type: mapping
+  label: 'Block settings'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    label:
+      type: label
+      label: 'Description'
+    label_display:
+      type: string
+      label: 'Display title'
+    status:
+      type: boolean
+      label: 'Status'
+    info:
+      type: label
+      label: 'Admin info'
+    view_mode:
+      type: string
+      label: 'View mode'
+    provider:
+      type: string
+      label: 'Provider'
+    context_mapping:
+      type: sequence
+      label: 'Context assignments'
+      sequence:
+        type: string
+
+condition.plugin:
+  type: mapping
+  label: 'Condition'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    negate:
+      type: boolean
+      label: 'Negate'
+    uuid:
+      type: uuid
+    context_mapping:
+      type: sequence
+      label: 'Context assignments'
+      sequence:
+        type: string
+
+display_variant.plugin:
+  type: mapping
+  label: 'Display variant'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    label:
+      type: label
+      label: 'Label'
+    weight:
+      type: integer
+      label: 'Weight'
+    uuid:
+      type: uuid
+
+layout_plugin.settings:
+  type: mapping
+  label: 'Layout settings'
+  mapping:
+    label:
+      type: label
+      label: 'Label'
+
+layout_plugin.settings.*:
+  type: layout_plugin.settings
+
+base_entity_reference_field_settings:
+  type: mapping
+  mapping:
+    target_type:
+      type: string
+      label: 'Type of item to reference'
+
+field_config_base:
+  type: config_entity
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    field_name:
+      type: string
+      label: 'Field name'
+    entity_type:
+      type: string
+      label: 'Entity type'
+    bundle:
+      type: string
+      label: 'Bundle'
+    label:
+      type: label
+      label: 'Label'
+    description:
+      type: text
+      label: 'Help text'
+    required:
+      type: boolean
+      label: 'Required field'
+    translatable:
+      type: boolean
+      label: 'Translatable'
+    default_value:
+      type: sequence
+      label: 'Default values'
+      sequence:
+        type: field.value.[%parent.%parent.field_type]
+        label: 'Default value'
+    default_value_callback:
+      type: string
+      label: 'Default value callback'
+    settings:
+      type: field.field_settings.[%parent.field_type]
+    field_type:
+      type: string
+      label: 'Field type'
+
+core.base_field_override.*.*.*:
+  type: field_config_base
+  label: 'Base field bundle override'
+
+core.date_format.*:
+  type: config_entity
+  label: 'Date format'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    label:
+      type: label
+      label: 'Label'
+    locked:
+      type: boolean
+      label: 'Locked'
+    pattern:
+      type: core_date_format_pattern.[%parent.locked]
+      label: 'PHP date format'
+
+# Unlocked date formats should use the translatable type.
+core_date_format_pattern.0:
+  type: date_format
+  label: 'Date format'
+
+# Locked date formats are just used to transport the value.
+core_date_format_pattern.1:
+  type: string
+  label: 'Date format'
+
+# Generic field settings schemas.
+
+field.storage_settings.*:
+  type: mapping
+  label: 'Settings'
+
+field.field_settings.*:
+  type: mapping
+  label: 'Settings'
+
+field.value.*:
+  type: mapping
+  label: 'Default value'
+
+# Schema for the configuration of the String field type.
+
+field.storage_settings.string:
+  type: mapping
+  label: 'String settings'
+  mapping:
+    max_length:
+      type: integer
+      label: 'Maximum length'
+    case_sensitive:
+      type: boolean
+      label: 'Case sensitive'
+    is_ascii:
+      type: boolean
+      label: 'Contains US ASCII characters only'
+
+field.field_settings.string:
+  type: mapping
+  label: 'String settings'
+
+field.value.string:
+  type: mapping
+  label: 'Default value'
+  mapping:
+    value:
+      type: label
+      label: 'Value'
+
+# Schema for the configuration of the  String (long) field type.
+
+field.storage_settings.string_long:
+  type: mapping
+  label: 'String (long) settings'
+  mapping:
+    case_sensitive:
+      type: boolean
+      label: 'Case sensitive'
+
+field.field_settings.string_long:
+  type: mapping
+  label: 'String (long) settings'
+
+field.value.string_long:
+  type: mapping
+  label: 'Default value'
+  mapping:
+    value:
+      type: text
+      label: 'Value'
+
+# Schema for the configuration of the URI field type.
+
+field.storage_settings.uri:
+  type: field.storage_settings.string
+  label: 'URI settings'
+  mapping:
+    max_length:
+      type: integer
+      label: 'Maximum length'
+    case_sensitive:
+      type: boolean
+      label: 'Case sensitive'
+
+field.field_settings.uri:
+  type: mapping
+  label: 'URI settings'
+
+field.value.uri:
+  type: mapping
+  label: 'Default value'
+  mapping:
+    value:
+      type: string
+      label: 'Value'
+
+# Schema for the configuration of the Created field type.
+
+field.storage_settings.created:
+  type: mapping
+  label: 'Created timestamp settings'
+
+field.field_settings.created:
+  type: mapping
+  label: 'Created timestamp settings'
+
+field.value.created:
+  type: mapping
+  label: 'Default value'
+  mapping:
+    value:
+      type: integer
+      label: 'Value'
+
+# Schema for the configuration of the Changed field type.
+
+field.storage_settings.changed:
+  type: mapping
+  label: 'Changed timestamp settings'
+
+field.field_settings.changed:
+  type: mapping
+  label: 'Changed timestamp settings'
+
+field.value.changed:
+  type: mapping
+  label: 'Default value'
+  mapping:
+    value:
+      type: integer
+      label: 'Value'
+
+# Schema for the configuration of the Entity reference field type.
+
+field.storage_settings.entity_reference:
+  type: mapping
+  label: 'Entity reference field storage settings'
+  mapping:
+    target_type:
+      type: string
+      label: 'Type of item to reference'
+
+field.field_settings.entity_reference:
+  type: mapping
+  label: 'Entity reference field settings'
+  mapping:
+    handler:
+      type: string
+      label: 'Reference method'
+    handler_settings:
+      type: entity_reference_selection.[%parent.handler]
+      label: 'Entity reference selection plugin settings'
+
+field.value.entity_reference:
+  type: mapping
+  label: 'Default value'
+  mapping:
+    target_id:
+      type: string
+      label: 'Value'
+    target_uuid:
+      type: uuid
+
+# Schema for the configuration of the Boolean field type.
+
+field.field_settings.boolean:
+  label: 'Boolean settings'
+  type: mapping
+  mapping:
+    on_label:
+      type: label
+      label: 'On label'
+    off_label:
+      type: label
+      label: 'Off label'
+
+field.value.boolean:
+  type: mapping
+  mapping:
+    value:
+      type: integer
+      label: 'Value'
+
+# Schema for the configuration of the Email field type.
+
+field.storage_settings.email:
+  type: mapping
+  label: 'Email settings'
+
+field.field_settings.email:
+  type: mapping
+  label: 'Email settings'
+  sequence:
+    type: string
+    label: 'Setting'
+
+field.value.email:
+  type: mapping
+  label: 'Default value'
+  mapping:
+    value:
+      type: email
+      label: 'Value'
+
+# Schema for the configuration of the Integer field type.
+
+field.storage_settings.integer:
+  type: mapping
+  label: 'Integer settings'
+  mapping:
+    unsigned:
+      type: boolean
+      label: 'Unsigned'
+    size:
+      type: string
+      label: 'Database storage size'
+
+field.field_settings.integer:
+  type: mapping
+  label: 'Integer'
+  mapping:
+    min:
+      type: integer
+      label: 'Minimum'
+    max:
+      type: integer
+      label: 'Maximum'
+    prefix:
+      type: label
+      label: 'Prefix'
+    suffix:
+      type: label
+      label: 'Suffix'
+
+field.value.integer:
+  type: mapping
+  label: 'Default value'
+  mapping:
+    value:
+      type: integer
+      label: 'Value'
+
+# Schema for the configuration of the Decimal field type.
+
+field.storage_settings.decimal:
+  type: mapping
+  label: 'Decimal settings'
+  mapping:
+    precision:
+      type: integer
+      label: 'Precision'
+    scale:
+      type: integer
+      label: 'Scale'
+
+field.field_settings.decimal:
+  type: mapping
+  label: 'Decimal settings'
+  mapping:
+    min:
+      type: float
+      label: 'Minimum'
+    max:
+      type: float
+      label: 'Maximum'
+    prefix:
+      type: label
+      label: 'Prefix'
+    suffix:
+      type: label
+      label: 'Suffix'
+
+field.value.decimal:
+   type: mapping
+   label: 'Default value'
+   mapping:
+     value:
+       type: float
+       label: 'Value'
+
+# Schema for the configuration of the Float field type.
+
+field.storage_settings.float:
+  type: mapping
+  label: 'Float settings'
+
+field.field_settings.float:
+  type: mapping
+  label: 'Float settings'
+  mapping:
+    min:
+      type: float
+      label: 'Minimum'
+    max:
+      type: float
+      label: 'Maximum'
+    prefix:
+      type: label
+      label: 'Prefix'
+    suffix:
+      type: label
+      label: 'Suffix'
+
+field.value.float:
+  type: mapping
+  label: 'Default value'
+  mapping:
+    value:
+      type: float
+      label: 'Value'
+
+# Schema for the configuration of the Timestamp field type.
+
+field.value.timestamp:
+  type: mapping
+  label: 'Timestamp value'
+  mapping:
+    value:
+      type: timestamp
+      label: 'Value'
+
+# Text with a text format.
+text_format:
+  type: mapping
+  label: 'Text with text format'
+  # We declare the entire mapping of text and text format as translatable. This
+  # causes the entire mapping to be saved to the language overrides of the
+  # configuration. Storing only the (to be formatted) text could result in
+  # security problems in case the text format of the source text is changed.
+  translatable: true
+  mapping:
+    value:
+      type: text
+      label: 'Text'
+      # Mark the actual text as translatable (in addition to the entire mapping
+      # being marked as translatable) so that shipped configuration with
+      # formatted text can participate in the string translation system.
+      translatable: true
+    format:
+      type: string
+      label: 'Text format'
+      # The text format should not be translated as part of the string
+      # translation system, so this is not marked as translatable.
+
+# Base schema for all entity reference selection handler schemas.
+entity_reference_selection:
+  type: mapping
+  label: 'Entity reference selection handler settings'
+  mapping:
+    target_type:
+      type: string
+      label: 'Type of item to reference'
+
+# Schema for all entity reference selection handlers that are not providing a
+# specific schema.
+entity_reference_selection.*:
+  type: entity_reference_selection
+
+# Schema for the entity reference 'default' selection handler settings.
+entity_reference_selection.default:
+  type: entity_reference_selection
+  label: 'Default selection handler settings'
+  mapping:
+    target_bundles:
+      type: sequence
+      label: 'types'
+      nullable: true
+      sequence:
+        type: string
+        label: 'Bundle'
+    sort:
+      type: mapping
+      label: 'Sort settings'
+      mapping:
+        field:
+          type: string
+          label: 'Sort by'
+        direction:
+          type: string
+          label: 'Sort direction'
+    auto_create:
+      type: boolean
+      label: 'Create referenced entities if they don''t already exist'
+    auto_create_bundle:
+      type: string
+      label: 'Bundle assigned to the auto-created entities.'
+
+# Schema for all entity reference 'default:*' selection handlers that are not
+# providing a specific schema.
+entity_reference_selection.default:*:
+  type: entity_reference_selection.default

+ 417 - 0
web/core/config/schema/core.entity.schema.yml

@@ -0,0 +1,417 @@
+# Schema for Configuration files of the entity module.
+
+core.entity_view_mode.*.*:
+  type: config_entity
+  label: 'Entity view mode settings'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    label:
+      type: label
+      label: 'The human-readable name of the view mode'
+    targetEntityType:
+      type: string
+      label: 'Target entity type'
+    cache:
+      type: boolean
+      label: 'Cached'
+
+core.entity_form_mode.*.*:
+  type: config_entity
+  label: 'Entity form mode settings'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    label:
+      type: label
+      label: 'Label'
+    targetEntityType:
+      type: string
+      label: 'Target entity type'
+    cache:
+      type: boolean
+      label: 'Cache'
+
+# Overview configuration information for view mode or form mode displays.
+core.entity_view_display.*.*.*:
+  type: config_entity
+  label: 'Entity display'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    targetEntityType:
+      type: string
+      label: 'Target entity type'
+    bundle:
+      type: string
+      label: 'Bundle'
+    mode:
+      type: string
+      label: 'View or form mode machine name'
+    content:
+      type: sequence
+      label: 'Field formatters'
+      sequence:
+        type: field_formatter.entity_view_display
+    hidden:
+      type: sequence
+      label: 'Field display setting'
+      sequence:
+        type: boolean
+        label: 'Value'
+
+field_formatter:
+  type: mapping
+  label: 'Field formatter'
+  mapping:
+    type:
+      type: string
+      label: 'Format type machine name'
+    label:
+       type: string
+       label: 'Label setting machine name'
+    settings:
+      type: field.formatter.settings.[%parent.type]
+      label: 'Settings'
+    third_party_settings:
+       type: sequence
+       label: 'Third party settings'
+       sequence:
+         type: field.formatter.third_party.[%key]
+
+field_formatter.entity_view_display:
+  type: field_formatter
+  mapping:
+    weight:
+      type: integer
+      label: 'Weight'
+    region:
+      type: string
+      label: 'Region'
+
+# Overview configuration information for form mode displays.
+core.entity_form_display.*.*.*:
+  type: config_entity
+  label: 'Entity form display'
+  mapping:
+    id:
+      type: string
+      label: 'ID'
+    targetEntityType:
+      type: string
+      label: 'Target entity type'
+    bundle:
+      type: string
+      label: 'Bundle'
+    mode:
+      type: string
+      label: 'View or form mode machine name'
+    status:
+      type: boolean
+      label: 'Enabled'
+    content:
+      type: sequence
+      label: 'Field widgets'
+      sequence:
+        type: mapping
+        label: 'Field widget'
+        mapping:
+          type:
+            type: string
+            label: 'Widget type machine name'
+          weight:
+            type: integer
+            label: 'Weight'
+          region:
+            type: string
+            label: 'Region'
+          settings:
+            type: field.widget.settings.[%parent.type]
+            label: 'Settings'
+          third_party_settings:
+            type: sequence
+            label: 'Third party settings'
+            sequence:
+              type: field.widget.third_party.[%key]
+    hidden:
+      type: sequence
+      label: 'Hidden'
+      sequence:
+        type: boolean
+        label: 'Component'
+
+# Default schema for entity display field with undefined type.
+field.formatter.settings.*:
+  type: mapping
+
+# Default schema for entity form display field with undefined type.
+field.widget.settings.*:
+  type: mapping
+
+field.widget.settings.string_textfield:
+  type: mapping
+  label: 'Text field display format settings'
+  mapping:
+    size:
+      type: integer
+      label: 'Size of textfield'
+    placeholder:
+      type: label
+      label: 'Placeholder'
+
+field.widget.settings.string_textarea:
+  type: mapping
+  label: 'Textarea display format settings'
+  mapping:
+    rows:
+      type: integer
+      label: 'Rows'
+    placeholder:
+      type: label
+      label: 'Placeholder'
+
+field.widget.settings.uri:
+  type: mapping
+  label: 'URI field'
+  mapping:
+    size:
+      type: integer
+      label: 'Size of URI field'
+    placeholder:
+      type: label
+      label: 'Placeholder'
+
+field.widget.settings.email_default:
+  type: mapping
+  label: 'Email field display format settings'
+  mapping:
+    placeholder:
+      type: label
+      label: 'Placeholder'
+    size:
+      type: integer
+      label: 'Size of email field'
+
+field.widget.settings.datetime_timestamp:
+  type: mapping
+  label: 'Datetime timestamp display format settings'
+
+field.widget.settings.boolean_checkbox:
+  type: mapping
+  label: 'Boolean checkbox display format settings'
+  mapping:
+    display_label:
+      type: boolean
+      label: 'Display label'
+
+field.widget.settings.hidden:
+  type: mapping
+  label: '- Hidden - format settings'
+
+field.widget.settings.number:
+  type: mapping
+  label: 'Number default display format settings'
+  mapping:
+    placeholder:
+      type: label
+      label: 'Placeholder'
+
+field.widget.settings.checkbox:
+  type: mapping
+  label: 'Single on/off checkbox format settings'
+  mapping:
+    display_label:
+      type: boolean
+      label: 'Use field label instead of the "On value" as label'
+
+field.widget.settings.entity_reference_autocomplete_tags:
+  type: mapping
+  label: 'Entity reference autocomplete (Tags style) display format settings'
+  mapping:
+    match_operator:
+      type: string
+      label: 'Autocomplete matching'
+    match_limit:
+      type: integer
+      label: 'Maximum number of autocomplete suggestions.'
+    size:
+      type: integer
+      label: 'Size of textfield'
+    placeholder:
+      type: label
+      label: 'Placeholder'
+
+field.widget.settings.entity_reference_autocomplete:
+  type: mapping
+  label: 'Entity reference autocomplete display format settings'
+  mapping:
+    match_operator:
+      type: string
+      label: 'Autocomplete matching'
+    match_limit:
+      type: integer
+      label: 'Maximum number of autocomplete suggestions.'
+    size:
+      type: integer
+      label: 'Size of textfield'
+    placeholder:
+      type: label
+      label: 'Placeholder'
+
+field.formatter.settings.boolean:
+  type: mapping
+  mapping:
+    format:
+      type: string
+      label: 'Output format'
+    format_custom_false:
+      type: label
+      label: 'Custom output for FALSE'
+    format_custom_true:
+      type: label
+      label: 'Custom output for TRUE'
+
+field.formatter.settings.string:
+  type: mapping
+  mapping:
+    link_to_entity:
+      type: boolean
+      label: 'Link to the entity'
+
+field.formatter.settings.language:
+  type: field.formatter.settings.string
+  mapping:
+    native_language:
+      type: boolean
+      label: 'Display in native language'
+
+field.formatter.settings.number_decimal:
+  type: mapping
+  label: 'Number decimal display format settings'
+  mapping:
+    thousand_separator:
+      type: string
+      label: 'Thousand marker'
+    decimal_separator:
+      type: string
+      label: 'Decimal marker'
+    scale:
+      type: integer
+      label: 'Scale'
+    prefix_suffix:
+      type: boolean
+      label: 'Display prefix and suffix.'
+
+field.formatter.settings.number_integer:
+  type: mapping
+  label: 'Number integer display format settings'
+  mapping:
+    thousand_separator:
+      type: string
+      label: 'Thousand marker'
+    prefix_suffix:
+      type: boolean
+      label: 'Display prefix and suffix.'
+
+field.formatter.settings.number_unformatted:
+  type: mapping
+  label: 'Number unformatted display format settings'
+
+field.formatter.settings.uri_link:
+  type: mapping
+  label: 'URI as link display format settings'
+
+field.formatter.settings.timestamp:
+  type: mapping
+  label: 'Timestamp display format settings'
+  mapping:
+    date_format:
+      type: string
+      label: 'Date format'
+    custom_date_format:
+      type: string
+      label: 'Custom date format'
+    timezone:
+      type: string
+      label: 'Time zone'
+
+field.formatter.settings.timestamp_ago:
+  type: mapping
+  label: 'Timestamp ago display format settings'
+  mapping:
+    future_format:
+      type: string
+      label: 'Future format'
+    past_format:
+      type: string
+      label: 'Past format'
+    granularity:
+      type: integer
+      label: 'Granularity'
+
+field.formatter.settings.entity_reference_entity_view:
+  type: mapping
+  label: 'Entity reference rendered entity display format settings'
+  mapping:
+    view_mode:
+      type: string
+      label: 'View mode'
+    link:
+      type: boolean
+      label: 'Show links'
+
+field.formatter.settings.entity_reference_entity_id:
+  type: mapping
+  label: 'Entity reference entity ID display format settings'
+
+field.formatter.settings.entity_reference_label:
+  type: mapping
+  label: 'Entity reference label display format settings'
+  mapping:
+    link:
+      type: boolean
+      label: 'Link label to the referenced entity'
+
+block.settings.field_block:*:*:*:
+  type: block_settings
+  mapping:
+    formatter:
+      type: field_formatter
+
+# Schema for entity actions.
+action.configuration.entity:*:*:
+  type: action_configuration_default
+  label: 'Entity action'
+
+action.configuration.action_send_email_action:
+  type: mapping
+  label: 'Send email configuration'
+  mapping:
+    recipient:
+      type: string
+      label: 'Recipient'
+    subject:
+      type: label
+      label: 'Subject'
+    message:
+      type: text
+      label: 'Message'
+
+action.configuration.action_goto_action:
+  type: mapping
+  label: 'Redirect to URL configuration'
+  mapping:
+    url:
+      type: string
+      label: 'URL'
+
+action.configuration.action_message_action:
+  type: mapping
+  label: 'Display a message to the user configuration'
+  mapping:
+    message:
+      type: text
+      label: 'Message'

+ 19 - 0
web/core/config/schema/core.extension.schema.yml

@@ -0,0 +1,19 @@
+core.extension:
+  type: config_object
+  label: 'Extension settings'
+  mapping:
+    module:
+      type: sequence
+      label: 'Enabled modules'
+      sequence:
+        type: integer
+        label: 'Weight'
+    theme:
+      type: sequence
+      label: 'Installed themes'
+      sequence:
+        type: integer
+        label: 'Weight'
+    profile:
+      type: string
+      label: 'Install profile'

+ 26 - 0
web/core/config/schema/core.menu.schema.yml

@@ -0,0 +1,26 @@
+core.menu.static_menu_link_overrides:
+  type: config_object
+  label: 'Static menu link overrides'
+  mapping:
+    definitions:
+      type: sequence
+      label: Definitions
+      sequence:
+        type: mapping
+        label: Definition
+        mapping:
+          menu_name:
+            type: string
+            label: 'Menu name'
+          parent:
+            type: string
+            label: 'Parent'
+          weight:
+            type: integer
+            label: 'Weight'
+          expanded:
+            type: boolean
+            label: 'Expanded'
+          enabled:
+            type: boolean
+            label: 'Enabled'

+ 2633 - 0
web/core/core.api.php

@@ -0,0 +1,2633 @@
+<?php
+
+/**
+ * @file
+ * Documentation landing page and topics, plus core library hooks.
+ */
+
+/**
+ * @mainpage
+ * Welcome to the Drupal API Documentation!
+ *
+ * This site is an API reference for Drupal, generated from comments embedded
+ * in the source code. More in-depth documentation can be found at
+ * https://www.drupal.org/developing/api.
+ *
+ * Here are some topics to help you get started developing with Drupal.
+ *
+ * @section essentials Essential background concepts
+ *
+ * - @link oo_conventions Object-oriented conventions used in Drupal @endlink
+ * - @link extending Extending and altering Drupal @endlink
+ * - @link best_practices Security and best practices @endlink
+ * - @link info_types Types of information in Drupal @endlink
+ *
+ * @section interface User interface
+ *
+ * - @link menu Menu entries, local tasks, and other links @endlink
+ * - @link routing Routing API and page controllers @endlink
+ * - @link form_api Forms @endlink
+ * - @link block_api Blocks @endlink
+ * - @link ajax Ajax @endlink
+ *
+ * @section store_retrieve Storing and retrieving data
+ *
+ * - @link entity_api Entities @endlink
+ * - @link field Fields @endlink
+ * - @link config_api Configuration API @endlink
+ * - @link state_api State API @endlink
+ * - @link views_overview Views @endlink
+ * - @link database Database abstraction layer @endlink
+ *
+ * @section other_essentials Other essential APIs
+ *
+ * - @link plugin_api Plugins @endlink
+ * - @link container Services and the Dependency Injection Container @endlink
+ * - @link events Events @endlink
+ * - @link i18n Internationalization @endlink
+ * - @link cache Caching @endlink
+ * - @link utility Utility classes and functions @endlink
+ * - @link user_api User accounts, permissions, and roles @endlink
+ * - @link theme_render Render API @endlink
+ * - @link themeable Theme system @endlink
+ * - @link update_api Update API @endlink
+ * - @link migration Migration @endlink
+ *
+ * @section additional Additional topics
+ *
+ * - @link batch Batch API @endlink
+ * - @link queue Queue API @endlink
+ * - @link typed_data Typed Data @endlink
+ * - @link testing Automated tests @endlink
+ * - @link php_assert PHP Runtime Assert Statements @endlink
+ * - @link third_party Integrating third-party applications @endlink
+ *
+ * @section more_info Further information
+ *
+ * - @link https://api.drupal.org/api/drupal/groups/8 All topics @endlink
+ * - @link https://www.drupal.org/project/examples Examples project (sample modules) @endlink
+ * - @link https://www.drupal.org/list-changes API change notices @endlink
+ * - @link https://www.drupal.org/developing/api/8 Drupal 8 API longer references @endlink
+ */
+
+/**
+ * @defgroup third_party REST and Application Integration
+ * @{
+ * Integrating third-party applications using REST and related operations.
+ *
+ * @section sec_overview Overview of web services
+ * Web services make it possible for applications and web sites to read and
+ * update information from other web sites. There are several standard
+ * techniques for providing web services, including:
+ * - SOAP: http://wikipedia.org/wiki/SOAP
+ * - XML-RPC: http://wikipedia.org/wiki/XML-RPC
+ * - REST: http://wikipedia.org/wiki/Representational_state_transfer
+ * Drupal sites can both provide web services and integrate third-party web
+ * services.
+ *
+ * @section sec_rest_overview Overview of REST
+ * The REST technique uses basic HTTP requests to obtain and update data, where
+ * each web service defines a specific API (HTTP GET and/or POST parameters and
+ * returned response) for its HTTP requests. REST requests are separated into
+ * several types, known as methods, including:
+ * - GET: Requests to obtain data.
+ * - POST: Requests to update or create data.
+ * - PUT: Requests to update or create data (limited support, currently unused
+ *   by entity resources).
+ * - PATCH: Requests to update a subset of data, such as one field.
+ * - DELETE: Requests to delete data.
+ * The Drupal Core REST module provides support for GET, POST, PATCH, and DELETE
+ * quests on entities, GET requests on the database log from the Database
+ * Logging module, and a plugin framework for providing REST support for other
+ * data and other methods.
+ *
+ * REST requests can be authenticated. The Drupal Core Basic Auth module
+ * provides authentication using the HTTP Basic protocol; the contributed module
+ * OAuth (https://www.drupal.org/project/oauth) implements the OAuth
+ * authentication protocol. You can also use cookie-based authentication, which
+ * would require users to be logged into the Drupal site while using the
+ * application on the third-party site that is using the REST service.
+ *
+ * @section sec_rest Enabling REST for entities and the log
+ * Here are the steps to take to use the REST operations provided by Drupal
+ * Core:
+ * - Enable the REST module, plus Basic Auth (or another authentication method)
+ *   and HAL.
+ * - Node entity support is configured by default. If you would like to support
+ *   other types of entities, you can copy
+ *   core/modules/rest/config/install/rest.settings.yml to your sync
+ *   configuration directory, appropriately modified for other entity types,
+ *   and import it. Support for GET on the log from the Database Logging module
+ *   can also be enabled in this way; in this case, the 'entity:node' line
+ *   in the configuration would be replaced by the appropriate plugin ID,
+ *   'dblog'.
+ * - Set up permissions to allow the desired REST operations for a role, and set
+ *   up one or more user accounts to perform the operations.
+ * - To perform a REST operation, send a request to either the canonical URL
+ *   for an entity (such as node/12345 for a node), or if the entity does not
+ *   have a canonical URL, a URL like entity/(type)/(ID). The URL for a log
+ *   entry is dblog/(ID). The request must have the following properties:
+ *   - The request method must be set to the REST method you are using (POST,
+ *     GET, PATCH, etc.).
+ *   - The content type for the data you send, or the accept type for the
+ *     data you are receiving, must be set to 'application/hal+json'.
+ *   - If you are sending data, it must be JSON-encoded.
+ *   - You'll also need to make sure the authentication information is sent
+ *     with the request, unless you have allowed access to anonymous users.
+ *
+ * For more detailed information on setting up REST, see
+ * https://www.drupal.org/documentation/modules/rest.
+ *
+ * @section sec_plugins Defining new REST plugins
+ * The REST framework in the REST module has support built in for entities, but
+ * it is also an extensible plugin-based system. REST plugins implement
+ * interface \Drupal\rest\Plugin\ResourceInterface, and generally extend base
+ * class \Drupal\rest\Plugin\ResourceBase. They are annotated with
+ * \Drupal\rest\Annotation\RestResource annotation, and must be in plugin
+ * namespace subdirectory Plugin\rest\resource. For more information on how to
+ * create plugins, see the @link plugin_api Plugin API topic. @endlink
+ *
+ * If you create a new REST plugin, you will also need to enable it by
+ * providing default configuration or configuration import, as outlined in
+ * @ref sec_rest above.
+ *
+ * @section sec_integrate Integrating data from other sites into Drupal
+ * If you want to integrate data from other web sites into Drupal, here are
+ * some notes:
+ * - There are contributed modules available for integrating many third-party
+ *   sites into Drupal. Search on https://www.drupal.org/project/project_module
+ * - If there is not an existing module, you will need to find documentation on
+ *   the specific web services API for the site you are trying to integrate.
+ * - There are several classes and functions that are useful for interacting
+ *   with web services:
+ *   - You should make requests using the 'http_client' service, which
+ *     implements \GuzzleHttp\ClientInterface. See the
+ *     @link container Services topic @endlink for more information on
+ *     services. If you cannot use dependency injection to retrieve this
+ *     service, the \Drupal::httpClient() method is available. A good example
+ *     of how to use this service can be found in
+ *     \Drupal\aggregator\Plugin\aggregator\fetcher\DefaultFetcher
+ *   - \Drupal\Component\Serialization\Json (JSON encoding and decoding).
+ *   - PHP has functions and classes for parsing XML; see
+ *     http://php.net/manual/refs.xml.php
+ * @}
+ */
+
+/**
+ * @defgroup state_api State API
+ * @{
+ * Information about the State API.
+ *
+ * The State API is one of several methods in Drupal for storing information.
+ * See the @link info_types Information types topic @endlink for an
+ * overview of the different types of information.
+ *
+ * The basic entry point into the State API is \Drupal::state(), which returns
+ * an object of class \Drupal\Core\State\StateInterface. This class has
+ * methods for storing and retrieving state information; each piece of state
+ * information is associated with a string-valued key. Example:
+ * @code
+ * // Get the state class.
+ * $state = \Drupal::state();
+ * // Find out when cron was last run; the key is 'system.cron_last'.
+ * $time = $state->get('system.cron_last');
+ * // Set the cron run time to the current request time.
+ * $state->set('system.cron_last', REQUEST_TIME);
+ * @endcode
+ *
+ * For more on the State API, see https://www.drupal.org/developing/api/8/state
+ * @}
+ */
+
+/**
+ * @defgroup config_api Configuration API
+ * @{
+ * Information about the Configuration API.
+ *
+ * The Configuration API is one of several methods in Drupal for storing
+ * information. See the @link info_types Information types topic @endlink for
+ * an overview of the different types of information. The sections below have
+ * more information about the configuration API; see
+ * https://www.drupal.org/developing/api/8/configuration for more details.
+ *
+ * @section sec_storage Configuration storage
+ * In Drupal, there is a concept of the "active" configuration, which is the
+ * configuration that is currently in use for a site. The storage used for the
+ * active configuration is configurable: it could be in the database, in files
+ * in a particular directory, or in other storage backends; the default storage
+ * is in the database. Module developers must use the configuration API to
+ * access the active configuration, rather than being concerned about the
+ * details of where and how it is stored.
+ *
+ * Configuration is divided into individual objects, each of which has a
+ * unique name or key. Some modules will have only one configuration object,
+ * typically called 'mymodule.settings'; some modules will have many. Within
+ * a configuration object, configuration settings have data types (integer,
+ * string, Boolean, etc.) and settings can also exist in a nested hierarchy,
+ * known as a "mapping".
+ *
+ * Configuration can also be overridden on a global, per-language, or
+ * per-module basis. See https://www.drupal.org/node/1928898 for more
+ * information.
+ *
+ * @section sec_yaml Configuration YAML files
+ * Whether or not configuration files are being used for the active
+ * configuration storage on a particular site, configuration files are always
+ * used for:
+ * - Defining the default configuration for an extension (module, theme, or
+ *   profile), which is imported to the active storage when the extension is
+ *   enabled. These configuration items are located in the config/install
+ *   sub-directory of the extension. Note that changes to this configuration
+ *   after a module or theme is already enabled have no effect; to make a
+ *   configuration change after a module or theme is enabled, you would need to
+ *   uninstall/reinstall or use a hook_update_N() function.
+ * - Defining optional configuration for a module or theme. Optional
+ *   configuration items are located in the config/optional sub-directory of the
+ *   extension. These configuration items have dependencies that are not
+ *   explicit dependencies of the extension, so they are only installed if all
+ *   dependencies are met. For example, in the scenario that module A defines a
+ *   dependency which requires module B, but module A is installed first and
+ *   module B some time later, then module A's config/optional directory will be
+ *   scanned at that time for newly met dependencies, and the configuration will
+ *   be installed then. If module B is never installed, the configuration item
+ *   will not be installed either.
+ * - Exporting and importing configuration.
+ *
+ * The file storage format for configuration information in Drupal is
+ * @link http://wikipedia.org/wiki/YAML YAML files. @endlink Configuration is
+ * divided into files, each containing one configuration object. The file name
+ * for a configuration object is equal to the unique name of the configuration,
+ * with a '.yml' extension. The default configuration files for each module are
+ * placed in the config/install directory under the top-level module directory,
+ * so look there in most Core modules for examples.
+ *
+ * @section sec_schema Configuration schema and translation
+ * Each configuration file has a specific structure, which is expressed as a
+ * YAML-based configuration schema. The configuration schema details the
+ * structure of the configuration, its data types, and which of its values need
+ * to be translatable. Each module needs to define its configuration schema in
+ * files in the config/schema directory under the top-level module directory, so
+ * look there in most Core modules for examples.
+ *
+ * Configuration can be internationalized; see the
+ * @link i18n Internationalization topic @endlink for more information. Data
+ * types label, text, and date_format in configuration schema are translatable;
+ * string is non-translatable text (the 'translatable' property on a schema
+ * data type definition indicates that it is translatable).
+ *
+ * @section sec_simple Simple configuration
+ * The simple configuration API should be used for information that will always
+ * have exactly one copy or version. For instance, if your module has a
+ * setting that is either on or off, then this is only defined once, and it
+ * would be a Boolean-valued simple configuration setting.
+ *
+ * The first task in using the simple configuration API is to define the
+ * configuration file structure, file name, and schema of your settings (see
+ * @ref sec_yaml above). Once you have done that, you can retrieve the active
+ * configuration object that corresponds to configuration file mymodule.foo.yml
+ * with a call to:
+ * @code
+ * $config = \Drupal::config('mymodule.foo');
+ * @endcode
+ *
+ * This will be an object of class \Drupal\Core\Config\Config, which has methods
+ * for getting configuration information. For instance, if your YAML file
+ * structure looks like this:
+ * @code
+ * enabled: '0'
+ * bar:
+ *   baz: 'string1'
+ *   boo: 34
+ * @endcode
+ * you can make calls such as:
+ * @code
+ * // Get a single value.
+ * $enabled = $config->get('enabled');
+ * // Get an associative array.
+ * $bar = $config->get('bar');
+ * // Get one element of the array.
+ * $bar_baz = $config->get('bar.baz');
+ * @endcode
+ *
+ * The Config object that was obtained and used in the previous examples does
+ * not allow you to change configuration. If you want to change configuration,
+ * you will instead need to get the Config object by making a call to
+ * getEditable() on the config factory:
+ * @code
+ * $config =\Drupal::service('config.factory')->getEditable('mymodule.foo');
+ * @endcode
+ *
+ * Individual configuration values can be changed or added using the set()
+ * method and saved using the save() method:
+ * @code
+ * // Set a scalar value.
+ * $config->set('enabled', 1);
+ * // Save the configuration.
+ * $config->save();
+ * @endcode
+ *
+ * Configuration values can also be unset using the clear() method, which is
+ * also chainable:
+ * @code
+ * $config->clear('bar.boo')->save();
+ * $config_data = $config->get('bar');
+ * @endcode
+ * In this example $config_data would return an array with one key - 'baz' -
+ * because 'boo' was unset.
+ *
+ * @section sec_entity Configuration entities
+ * In contrast to the simple configuration settings described in the previous
+ * section, if your module allows users to create zero or more items (where
+ * "items" are things like content type definitions, view definitions, and the
+ * like), then you need to define a configuration entity type to store your
+ * configuration. Creating an entity type, loading entities, and querying them
+ * are outlined in the @link entity_api Entity API topic. @endlink Here are a
+ * few additional steps and notes specific to configuration entities:
+ * - For examples, look for classes that implement
+ *   \Drupal\Core\Config\Entity\ConfigEntityInterface -- one good example is
+ *   the \Drupal\user\Entity\Role entity type.
+ * - In the entity type annotation, you will need to define a 'config_prefix'
+ *   string. When Drupal stores a configuration item, it will be given a name
+ *   composed of your module name, your chosen config prefix, and the ID of
+ *   the individual item, separated by '.'. For example, in the Role entity,
+ *   the config prefix is 'role', so one configuration item might be named
+ *   user.role.anonymous, with configuration file user.role.anonymous.yml.
+ * - You will need to define the schema for your configuration in your
+ *   modulename.schema.yml file, with an entry for 'modulename.config_prefix.*'.
+ *   For example, for the Role entity, the file user.schema.yml has an entry
+ *   user.role.*; see @ref sec_yaml above for more information.
+ * - Your module can provide default/optional configuration entities in YAML
+ *   files; see @ref sec_yaml above for more information.
+ * - Some configuration entities have dependencies on other configuration
+ *   entities, and module developers need to consider this so that configuration
+ *   can be imported, uninstalled, and synchronized in the right order. For
+ *   example, a field display configuration entity would need to depend on
+ *   field configuration, which depends on field and bundle configuration.
+ *   Configuration entity classes expose dependencies by overriding the
+ *   \Drupal\Core\Config\Entity\ConfigEntityInterface::calculateDependencies()
+ *   method.
+ * - On routes for paths starting with '/admin' or otherwise designated as
+ *   administration paths (such as node editing when it is set as an admin
+ *   operation), if they have configuration entity placeholders, configuration
+ *   entities are normally loaded in their original language, without
+ *   translations or other overrides. This is usually desirable, because most
+ *   admin paths are for editing configuration, and you need that to be in the
+ *   source language and to lack possibly dynamic overrides. If for some reason
+ *   you need to have your configuration entity loaded in the currently-selected
+ *   language on an admin path (for instance, if you go to
+ *   example.com/es/admin/your_path and you need the entity to be in Spanish),
+ *   then you can add a 'with_config_overrides' parameter option to your route.
+ *   The same applies if you need to load the entity with overrides (or
+ *   translated) on an admin path like '/node/add/article' (when configured to
+ *   be an admin path). Here's an example using the configurable_language config
+ *   entity:
+ *   @code
+ *   mymodule.myroute:
+ *     path: '/admin/mypath/{configurable_language}'
+ *     defaults:
+ *       _controller: '\Drupal\mymodule\MyController::myMethod'
+ *     options:
+ *       parameters:
+ *         configurable_language:
+ *           type: entity:configurable_language
+ *           with_config_overrides: TRUE
+ *   @endcode
+ *   With the route defined this way, the $configurable_language parameter to
+ *   your controller method will come in translated to the current language.
+ *   Without the parameter options section, it would be in the original
+ *   language, untranslated.
+ *
+ * @see i18n
+ *
+ * @}
+ */
+
+/**
+ * @defgroup cache Cache API
+ * @{
+ * Information about the Drupal Cache API
+ *
+ * @section basics Basics
+ *
+ * Note: If not specified, all of the methods mentioned here belong to
+ * \Drupal\Core\Cache\CacheBackendInterface.
+ *
+ * The Cache API is used to store data that takes a long time to compute.
+ * Caching can either be permanent or valid only for a certain time span, and
+ * the cache can contain any type of data.
+ *
+ * To use the Cache API:
+ * - Request a cache object through \Drupal::cache() or by injecting a cache
+ *   service.
+ * - Define a Cache ID (cid) value for your data. A cid is a string, which must
+ *   contain enough information to uniquely identify the data. For example, if
+ *   your data contains translated strings, then your cid value must include the
+ *   interface text language selected for page.
+ * - Call the get() method to attempt a cache read, to see if the cache already
+ *   contains your data.
+ * - If your data is not already in the cache, compute it and add it to the
+ *   cache using the set() method. The third argument of set() can be used to
+ *   control the lifetime of your cache item.
+ *
+ * Example:
+ * @code
+ * $cid = 'mymodule_example:' . \Drupal::languageManager()->getCurrentLanguage()->getId();
+ *
+ * $data = NULL;
+ * if ($cache = \Drupal::cache()->get($cid)) {
+ *   $data = $cache->data;
+ * }
+ * else {
+ *   $data = my_module_complicated_calculation();
+ *   \Drupal::cache()->set($cid, $data);
+ * }
+ * @endcode
+ *
+ * Note the use of $data and $cache->data in the above example. Calls to
+ * \Drupal::cache()->get() return a record that contains the information stored
+ * by \Drupal::cache()->set() in the data property as well as additional meta
+ * information about the cached data. In order to make use of the cached data
+ * you can access it via $cache->data.
+ *
+ * @section bins Cache bins
+ *
+ * Cache storage is separated into "bins", each containing various cache items.
+ * Each bin can be configured separately; see @ref configuration.
+ *
+ * When you request a cache object, you can specify the bin name in your call to
+ * \Drupal::cache(). Alternatively, you can request a bin by getting service
+ * "cache.nameofbin" from the container. The default bin is called "default", with
+ * service name "cache.default", it is used to store common and frequently used
+ * caches.
+ *
+ * Other common cache bins are the following:
+ *   - bootstrap: Data needed from the beginning to the end of most requests,
+ *     that has a very strict limit on variations and is invalidated rarely.
+ *   - render: Contains cached HTML strings like cached pages and blocks, can
+ *     grow to large size.
+ *   - data: Contains data that can vary by path or similar context.
+ *   - discovery: Contains cached discovery data for things such as plugins,
+ *     views_data, or YAML discovered data such as library info.
+ *
+ * A module can define a cache bin by defining a service in its
+ * modulename.services.yml file as follows (substituting the desired name for
+ * "nameofbin"):
+ * @code
+ * cache.nameofbin:
+ *   class: Drupal\Core\Cache\CacheBackendInterface
+ *   tags:
+ *     - { name: cache.bin }
+ *   factory: cache_factory:get
+ *   arguments: [nameofbin]
+ * @endcode
+ * See the @link container Services topic @endlink for more on defining
+ * services.
+ *
+ * @section delete Deletion
+ *
+ * There are two ways to remove an item from the cache:
+ * - Deletion (using delete(), deleteMultiple() or deleteAll()) permanently
+ *   removes the item from the cache.
+ * - Invalidation (using invalidate(), invalidateMultiple() or invalidateAll())
+ *   is a "soft" delete that only marks items as "invalid", meaning "not fresh"
+ *   or "not fresh enough". Invalid items are not usually returned from the
+ *   cache, so in most ways they behave as if they have been deleted. However,
+ *   it is possible to retrieve invalid items, if they have not yet been
+ *   permanently removed by the garbage collector, by passing TRUE as the second
+ *   argument for get($cid, $allow_invalid).
+ *
+ * Use deletion if a cache item is no longer useful; for instance, if the item
+ * contains references to data that has been deleted. Use invalidation if the
+ * cached item may still be useful to some callers until it has been updated
+ * with fresh data. The fact that it was fresh a short while ago may often be
+ * sufficient.
+ *
+ * Invalidation is particularly useful to protect against stampedes. Rather than
+ * having multiple concurrent requests updating the same cache item when it
+ * expires or is deleted, there can be one request updating the cache, while the
+ * other requests can proceed using the stale value. As soon as the cache item
+ * has been updated, all future requests will use the updated value.
+ *
+ * @section tags Cache Tags
+ *
+ * The fourth argument of the set() method can be used to specify cache tags,
+ * which are used to identify which data is included in each cache item. A cache
+ * item can have multiple cache tags (an array of cache tags), and each cache
+ * tag is a string. The convention is to generate cache tags of the form
+ * [prefix]:[suffix]. Usually, you'll want to associate the cache tags of
+ * entities, or entity listings. You won't have to manually construct cache tags
+ * for them — just get their cache tags via
+ * \Drupal\Core\Cache\CacheableDependencyInterface::getCacheTags() and
+ * \Drupal\Core\Entity\EntityTypeInterface::getListCacheTags().
+ * Data that has been tagged can be invalidated as a group: no matter the Cache
+ * ID (cid) of the cache item, no matter in which cache bin a cache item lives;
+ * as long as it is tagged with a certain cache tag, it will be invalidated.
+ *
+ * Because of that, cache tags are a solution to the cache invalidation problem:
+ * - For caching to be effective, each cache item must only be invalidated when
+ *   absolutely necessary. (i.e. maximizing the cache hit ratio.)
+ * - For caching to be correct, each cache item that depends on a certain thing
+ *   must be invalidated whenever that certain thing is modified.
+ *
+ * A typical scenario: a user has modified a node that appears in two views,
+ * three blocks and on twelve pages. Without cache tags, we couldn't possibly
+ * know which cache items to invalidate, so we'd have to invalidate everything:
+ * we had to sacrifice effectiveness to achieve correctness. With cache tags, we
+ * can have both.
+ *
+ * Example:
+ * @code
+ * // A cache item with nodes, users, and some custom module data.
+ * $tags = array(
+ *   'my_custom_tag',
+ *   'node:1',
+ *   'node:3',
+ *   'user:7',
+ * );
+ * \Drupal::cache()->set($cid, $data, CacheBackendInterface::CACHE_PERMANENT, $tags);
+ *
+ * // Invalidate all cache items with certain tags.
+ * \Drupal\Core\Cache\Cache::invalidateTags(array('user:1'));
+ * @endcode
+ *
+ * Drupal is a content management system, so naturally you want changes to your
+ * content to be reflected everywhere, immediately. That's why we made sure that
+ * every entity type in Drupal 8 automatically has support for cache tags: when
+ * you save an entity, you can be sure that the cache items that have the
+ * corresponding cache tags will be invalidated.
+ * This also is the case when you define your own entity types: you'll get the
+ * exact same cache tag invalidation as any of the built-in entity types, with
+ * the ability to override any of the default behavior if needed.
+ * See \Drupal\Core\Cache\CacheableDependencyInterface::getCacheTags(),
+ * \Drupal\Core\Entity\EntityTypeInterface::getListCacheTags(),
+ * \Drupal\Core\Entity\Entity::invalidateTagsOnSave() and
+ * \Drupal\Core\Entity\Entity::invalidateTagsOnDelete().
+ *
+ * @section context Cache contexts
+ *
+ * Some computed data depends on contextual data, such as the user roles of the
+ * logged-in user who is viewing a page, the language the page is being rendered
+ * in, the theme being used, etc. When caching the output of such a calculation,
+ * you must cache each variation separately, along with information about which
+ * variation of the contextual data was used in the calculation. The next time
+ * the computed data is needed, if the context matches that for an existing
+ * cached data set, the cached data can be reused; if no context matches, a new
+ * data set can be calculated and cached for later use.
+ *
+ * Cache contexts are services tagged with 'cache.context', whose classes
+ * implement \Drupal\Core\Cache\Context\CacheContextInterface. See
+ * https://www.drupal.org/developing/api/8/cache/contexts for more information
+ * on cache contexts, including a list of the contexts that exist in Drupal
+ * core, and information on how to define your own contexts. See the
+ * @link container Services and the Dependency Injection Container @endlink
+ * topic for more information about services.
+ *
+ * Typically, the cache context is specified as part of the #cache property
+ * of a render array; see the Caching section of the
+ * @link theme_render Render API overview topic @endlink for details.
+ *
+ * @section configuration Configuration
+ *
+ * By default cached data is stored in the database. This can be configured
+ * though so that all cached data, or that of an individual cache bin, uses a
+ * different cache backend, such as APCu or Memcache, for storage.
+ *
+ * In a settings.php file, you can override the service used for a particular
+ * cache bin. For example, if your service implementation of
+ * \Drupal\Core\Cache\CacheBackendInterface was called cache.custom, the
+ * following line would make Drupal use it for the 'cache_render' bin:
+ * @code
+ *  $settings['cache']['bins']['render'] = 'cache.custom';
+ * @endcode
+ *
+ * Additionally, you can register your cache implementation to be used by
+ * default for all cache bins with:
+ * @code
+ *  $settings['cache']['default'] = 'cache.custom';
+ * @endcode
+ *
+ * For cache bins that are stored in the database, the number of rows is limited
+ * to 5000 by default. This can be changed for all database cache bins. For
+ * example, to instead limit the number of rows to 50000:
+ * @code
+ * $settings['database_cache_max_rows']['default'] = 50000;
+ * @endcode
+ *
+ * Or per bin (in this example we allow infinite entries):
+ * @code
+ * $settings['database_cache_max_rows']['bins']['dynamic_page_cache'] = -1;
+ * @endcode
+ *
+ * For monitoring reasons it might be useful to figure out the amount of data
+ * stored in tables. The following SQL snippet can be used for that:
+ * @code
+ * SELECT table_name AS `Table`, table_rows AS 'Num. of Rows',
+ * ROUND(((data_length + index_length) / 1024 / 1024), 2) `Size in MB` FROM
+ * information_schema.TABLES WHERE table_schema = '***DATABASE_NAME***' AND
+ * table_name LIKE 'cache_%'  ORDER BY (data_length + index_length) DESC
+ * LIMIT 10;
+ * @endcode
+ *
+ * @see \Drupal\Core\Cache\DatabaseBackend
+ *
+ * Finally, you can chain multiple cache backends together, see
+ * \Drupal\Core\Cache\ChainedFastBackend and \Drupal\Core\Cache\BackendChain.
+ *
+ * @see https://www.drupal.org/node/1884796
+ * @}
+ */
+
+/**
+ * @defgroup user_api User accounts, permissions, and roles
+ * @{
+ * API for user accounts, access checking, roles, and permissions.
+ *
+ * @section sec_overview Overview and terminology
+ * Drupal's permission system is based on the concepts of accounts, roles,
+ * and permissions.
+ *
+ * Users (site visitors) have accounts, which include a user name, an email
+ * address, a password (or some other means of authentication), and possibly
+ * other fields (if defined on the site). Anonymous users have an implicit
+ * account that does not have a real user name or any account information.
+ *
+ * Each user account is assigned one or more roles. The anonymous user account
+ * automatically has the anonymous user role; real user accounts
+ * automatically have the authenticated user role, plus any roles defined on
+ * the site that they have been assigned.
+ *
+ * Each role, including the special anonymous and authenticated user roles, is
+ * granted one or more named permissions, which allow them to perform certain
+ * tasks or view certain content on the site. It is possible to designate a
+ * role to be the "administrator" role; if this is set up, this role is
+ * automatically granted all available permissions whenever a module is
+ * enabled that defines permissions.
+ *
+ * All code in Drupal that allows users to perform tasks or view content must
+ * check that the current user has the correct permission before allowing the
+ * action. In the standard case, access checking consists of answering the
+ * question "Does the current user have permission 'foo'?", and allowing or
+ * denying access based on the answer. Note that access checking should nearly
+ * always be done at the permission level, not by checking for a particular role
+ * or user ID, so that site administrators can set up user accounts and roles
+ * appropriately for their particular sites.
+ *
+ * @section sec_define Defining permissions
+ * Modules define permissions via a $module.permissions.yml file. See
+ * \Drupal\user\PermissionHandler for documentation of permissions.yml files.
+ *
+ * @section sec_access Access permission checking
+ * Depending on the situation, there are several methods for ensuring that
+ * access checks are done properly in Drupal:
+ * - Routes: When you register a route, include a 'requirements' section that
+ *   either gives the machine name of the permission that is needed to visit the
+ *   URL of the route, or tells Drupal to use an access check method or service
+ *   to check access. See the @link menu Routing topic @endlink for more
+ *   information.
+ * - Entities: Access for various entity operations is designated either with
+ *   simple permissions or access control handler classes in the entity
+ *   annotation. See the @link entity_api Entity API topic @endlink for more
+ *   information.
+ * - Other code: There is a 'current_user' service, which can be injected into
+ *   classes to provide access to the current user account (see the
+ *   @link container Services and Dependency Injection topic @endlink for more
+ *   information on dependency injection). In code that cannot use dependency
+ *   injection, you can access this service and retrieve the current user
+ *   account object by calling \Drupal::currentUser(). Once you have a user
+ *   object for the current user (implementing \Drupal\user\UserInterface), you
+ *   can call inherited method
+ *   \Drupal\Core\Session\AccountInterface::hasPermission() to check
+ *   permissions, or pass this object into other functions/methods.
+ * - Forms: Each element of a form array can have a Boolean '#access' property,
+ *   which determines whether that element is visible and/or usable. This is a
+ *   common need in forms, so the current user service (described above) is
+ *   injected into the form base class as method
+ *   \Drupal\Core\Form\FormBase::currentUser().
+ *
+ * @section sec_entities User and role objects
+ * User objects in Drupal are entity items, implementing
+ * \Drupal\user\UserInterface. Role objects in Drupal are also entity items,
+ * implementing \Drupal\user\RoleInterface. See the
+ * @link entity_api Entity API topic @endlink for more information about
+ * entities in general (including how to load, create, modify, and query them).
+ *
+ * Roles often need to be manipulated in automated test code, such as to add
+ * permissions to them. Here's an example:
+ * @code
+ * $role = \Drupal\user\Entity\Role::load('authenticated');
+ * $role->grantPermission('access comments');
+ * $role->save();
+ * @endcode
+ *
+ * Other important interfaces:
+ * - \Drupal\Core\Session\AccountInterface: The part of UserInterface that
+ *   deals with access checking. In writing code that checks access, your
+ *   method parameters should use this interface, not UserInterface.
+ * - \Drupal\Core\Session\AccountProxyInterface: The interface for the
+ *   current_user service (described above).
+ * @}
+ */
+
+/**
+ * @defgroup container Services and Dependency Injection Container
+ * @{
+ * Overview of the Dependency Injection Container and Services.
+ *
+ * @section sec_overview Overview of container, injection, and services
+ * The Services and Dependency Injection Container concepts have been adopted by
+ * Drupal from the @link http://symfony.com/ Symfony framework. @endlink A
+ * "service" (such as accessing the database, sending email, or translating user
+ * interface text) is defined (given a name and an interface or at least a
+ * class that defines the methods that may be called), and a default class is
+ * designated to provide the service. These two steps must be done together, and
+ * can be done by Drupal Core or a module. Other modules can then define
+ * alternative classes to provide the same services, overriding the default
+ * classes. Classes and functions that need to use the service should always
+ * instantiate the class via the dependency injection container (also known
+ * simply as the "container"), rather than instantiating a particular service
+ * provider class directly, so that they get the correct class (default or
+ * overridden).
+ *
+ * See https://www.drupal.org/node/2133171 for more detailed information on
+ * services and the dependency injection container.
+ *
+ * @section sec_discover Discovering existing services
+ * Drupal core defines many core services in the core.services.yml file (in the
+ * top-level core directory). Some Drupal Core modules and contributed modules
+ * also define services in modulename.services.yml files. API reference sites
+ * (such as https://api.drupal.org) generate lists of all existing services from
+ * these files. Look for the Services link in the API Navigation block.
+ * Alternatively you can look through the individual files manually.
+ *
+ * A typical service definition in a *.services.yml file looks like this:
+ * @code
+ * path_alias.manager:
+ *   class: Drupal\path_alias\AliasManager
+ *   arguments: ['@path_alias.repository', '@path_alias.whitelist', '@language_manager']
+ * @endcode
+ * Some services use other services as factories; a typical service definition
+ * is:
+ * @code
+ *   cache.entity:
+ *     class: Drupal\Core\Cache\CacheBackendInterface
+ *     tags:
+ *       - { name: cache.bin }
+ *     factory: cache_factory:get
+ *     arguments: [entity]
+ * @endcode
+ *
+ * The first line of a service definition gives the unique machine name of the
+ * service. This is often prefixed by the module name if provided by a module;
+ * however, by convention some service names are prefixed by a group name
+ * instead, such as cache.* for cache bins and plugin.manager.* for plugin
+ * managers.
+ *
+ * The class line either gives the default class that provides the service, or
+ * if the service uses a factory class, the interface for the service. If the
+ * class depends on other services, the arguments line lists the machine
+ * names of the dependencies (preceded by '@'); objects for each of these
+ * services are instantiated from the container and passed to the class
+ * constructor when the service class is instantiated. Other arguments can also
+ * be passed in; see the section at https://www.drupal.org/node/2133171 for more
+ * detailed information.
+ *
+ * Services using factories can be defined as shown in the above example, if the
+ * factory is itself a service. The factory can also be a class; details of how
+ * to use service factories can be found in the section at
+ * https://www.drupal.org/node/2133171.
+ *
+ * @section sec_container Accessing a service through the container
+ * As noted above, if you need to use a service in your code, you should always
+ * instantiate the service class via a call to the container, using the machine
+ * name of the service, so that the default class can be overridden. There are
+ * several ways to make sure this happens:
+ * - For service-providing classes, see other sections of this documentation
+ *   describing how to pass services as arguments to the constructor.
+ * - Plugin classes, controllers, and similar classes have create() or
+ *   createInstance() methods that are used to create an instance of the class.
+ *   These methods come from different interfaces, and have different
+ *   arguments, but they all include an argument $container of type
+ *   \Symfony\Component\DependencyInjection\ContainerInterface.
+ *   If you are defining one of these classes, in the create() or
+ *   createInstance() method, call $container->get('myservice.name') to
+ *   instantiate a service. The results of these calls are generally passed to
+ *   the class constructor and saved as member variables in the class.
+ * - For functions and class methods that do not have access to either of
+ *   the above methods of dependency injection, you can use service location to
+ *   access services, via a call to the global \Drupal class. This class has
+ *   special methods for accessing commonly-used services, or you can call a
+ *   generic method to access any service. Examples:
+ *   @code
+ *   // Retrieve the entity.manager service object (special method exists).
+ *   $manager = \Drupal::entityManager();
+ *   // Retrieve the service object for machine name 'foo.bar'.
+ *   $foobar = \Drupal::service('foo.bar');
+ *   @endcode
+ *
+ * As a note, you should always use dependency injection (via service arguments
+ * or create()/createInstance() methods) if possible to instantiate services,
+ * rather than service location (via the \Drupal class), because:
+ * - Dependency injection facilitates writing unit tests, since the container
+ *   argument can be mocked and the create() method can be bypassed by using
+ *   the class constructor. If you use the \Drupal class, unit tests are much
+ *   harder to write and your code has more dependencies.
+ * - Having the service interfaces on the class constructor and member variables
+ *   is useful for IDE auto-complete and self-documentation.
+ *
+ * @section sec_define Defining a service
+ * If your module needs to define a new service, here are the steps:
+ * - Choose a unique machine name for your service. Typically, this should
+ *   start with your module name. Example: mymodule.myservice.
+ * - Create a PHP interface to define what your service does.
+ * - Create a default class implementing your interface that provides your
+ *   service. If your class needs to use existing services (such as database
+ *   access), be sure to make these services arguments to your class
+ *   constructor, and save them in member variables. Also, if the needed
+ *   services are provided by other modules and not Drupal Core, you'll want
+ *   these modules to be dependencies of your module.
+ * - Add an entry to a modulename.services.yml file for the service. See
+ *   @ref sec_discover above, or existing *.services.yml files in Core, for the
+ *   syntax; it will start with your machine name, refer to your default class,
+ *   and list the services that need to be passed into your constructor.
+ *
+ * Services can also be defined dynamically, as in the
+ * \Drupal\Core\CoreServiceProvider class, but this is less common for modules.
+ *
+ * @section sec_tags Service tags
+ * Some services have tags, which are defined in the service definition. See
+ * @link service_tag Service Tags @endlink for usage.
+ *
+ * @section sec_injection Overriding the default service class
+ * Modules can override the default classes used for services. Here are the
+ * steps:
+ * - Define a class in the top-level namespace for your module
+ *   (Drupal\my_module), whose name is the camel-case version of your module's
+ *   machine name followed by "ServiceProvider" (for example, if your module
+ *   machine name is my_module, the class must be named
+ *   MyModuleServiceProvider).
+ * - The class needs to implement
+ *   \Drupal\Core\DependencyInjection\ServiceModifierInterface, which is
+ *   typically done by extending
+ *   \Drupal\Core\DependencyInjection\ServiceProviderBase.
+ * - The class needs to contain one method: alter(). This method does the
+ *   actual work of telling Drupal to use your class instead of the default.
+ *   Here's an example:
+ *   @code
+ *   public function alter(ContainerBuilder $container) {
+ *     // Override the language_manager class with a new class.
+ *     $definition = $container->getDefinition('language_manager');
+ *     $definition->setClass('Drupal\my_module\MyLanguageManager');
+ *   }
+ *   @endcode
+ *   Note that $container here is an instance of
+ *   \Drupal\Core\DependencyInjection\ContainerBuilder.
+ *
+ * @see https://www.drupal.org/node/2133171
+ * @see core.services.yml
+ * @see \Drupal
+ * @see \Symfony\Component\DependencyInjection\ContainerInterface
+ * @see plugin_api
+ * @see menu
+ * @}
+ */
+
+/**
+ * @defgroup listing_page_service Page header for Services page
+ * @{
+ * Introduction to services
+ *
+ * A "service" (such as accessing the database, sending email, or translating
+ * user interface text) can be defined by a module or Drupal core. Defining a
+ * service means giving it a name and designating a default class to provide the
+ * service; ideally, there should also be an interface that defines the methods
+ * that may be called. Services are collected into the Dependency Injection
+ * Container, and can be overridden to use different classes or different
+ * instantiation by modules. See the
+ * @link container Services and Dependency Injection Container topic @endlink
+ * for details.
+ *
+ * Some services have tags, which are defined in the service definition. Tags
+ * are used to define a group of related services, or to specify some aspect of
+ * how the service behaves. See the
+ * @link service_tag Service Tags topic @endlink for more information.
+ *
+ * @see container
+ * @see service_tag
+ *
+ * @}
+ */
+
+/**
+ * @defgroup typed_data Typed Data API
+ * @{
+ * API for describing data based on a set of available data types.
+ *
+ * PHP has data types, such as int, string, float, array, etc., and it is an
+ * object-oriented language that lets you define classes and interfaces.
+ * However, in some cases, it is useful to be able to define an abstract
+ * type (as in an interface, free of implementation details), that still has
+ * properties (which an interface cannot) as well as meta-data. The Typed Data
+ * API provides this abstraction.
+ *
+ * @section sec_overview Overview
+ * Each data type in the Typed Data API is a plugin class (annotation class
+ * example: \Drupal\Core\TypedData\Annotation\DataType); these plugins are
+ * managed by the typed_data_manager service (by default
+ * \Drupal\Core\TypedData\TypedDataManager). Each data object encapsulates a
+ * single piece of data, provides access to the metadata, and provides
+ * validation capability. Also, the typed data plugins have a shorthand
+ * for easily accessing data values, described in @ref sec_tree.
+ *
+ * The metadata of a data object is defined by an object based on a class called
+ * the definition class (see \Drupal\Core\TypedData\DataDefinitionInterface).
+ * The class used can vary by data type and can be specified in the data type's
+ * plugin definition, while the default is set in the $definition_class property
+ * of the annotation class. The default class is
+ * \Drupal\Core\TypedData\DataDefinition. For data types provided by a plugin
+ * deriver, the plugin deriver can set the definition_class property too.
+ * The metadata object provides information about the data, such as the data
+ * type, whether it is translatable, the names of its properties (for complex
+ * types), and who can access it.
+ *
+ * See https://www.drupal.org/node/1794140 for more information about the Typed
+ * Data API.
+ *
+ * @section sec_varieties Varieties of typed data
+ * There are three kinds of typed data: primitive, complex, and list.
+ *
+ * @subsection sub_primitive Primitive data types
+ * Primitive data types wrap PHP data types and also serve as building blocks
+ * for complex and list typed data. Each primitive data type has an interface
+ * that extends \Drupal\Core\TypedData\PrimitiveInterface, with getValue()
+ * and setValue() methods for accessing the data value, and a default plugin
+ * implementation. Here's a list:
+ * - \Drupal\Core\TypedData\Type\IntegerInterface: Plugin ID integer,
+ *   corresponds to PHP type int.
+ * - \Drupal\Core\TypedData\Type\StringInterface: Plugin ID string,
+ *   corresponds to PHP type string.
+ * - \Drupal\Core\TypedData\Type\FloatInterface: Plugin ID float,
+ *   corresponds to PHP type float.
+ * - \Drupal\Core\TypedData\Type\BooleanInterface: Plugin ID bool,
+ *   corresponds to PHP type bool.
+ * - \Drupal\Core\TypedData\Type\BinaryInterface: Plugin ID binary,
+ *   corresponds to a PHP file resource.
+ * - \Drupal\Core\TypedData\Type\UriInterface: Plugin ID uri.
+ *
+ * @subsection sec_complex Complex data
+ * Complex data types, with interface
+ * \Drupal\Core\TypedData\ComplexDataInterface, represent data with named
+ * properties; the properties can be accessed with get() and set() methods.
+ * The value of each property is itself a typed data object, which can be
+ * primitive, complex, or list data.
+ *
+ * The base type for most complex data is the
+ * \Drupal\Core\TypedData\Plugin\DataType\Map class, which represents an
+ * associative array. Map provides its own definition class in the annotation,
+ * \Drupal\Core\TypedData\MapDataDefinition, and most complex data classes
+ * extend this class. The getValue() and setValue() methods on the Map class
+ * enforce the data definition and its property structure.
+ *
+ * The Drupal Field API uses complex typed data for its field items, with
+ * definition class \Drupal\Core\Field\TypedData\FieldItemDataDefinition.
+ *
+ * @section sec_list Lists
+ * List data types, with interface \Drupal\Core\TypedData\ListInterface,
+ * represent data that is an ordered list of typed data, all of the same type.
+ * More precisely, the plugins in the list must have the same base plugin ID;
+ * however, some types (for example field items and entities) are provided by
+ * plugin derivatives and the sub IDs can be different.
+ *
+ * @section sec_tree Tree handling
+ * Typed data allows you to use shorthand to get data values nested in the
+ * implicit tree structure of the data. For example, to get the value from
+ * an entity field item, the Entity Field API allows you to call:
+ * @code
+ * $value = $entity->fieldName->propertyName;
+ * @endcode
+ * This is really shorthand for:
+ * @code
+ * $field_item_list = $entity->get('fieldName');
+ * $field_item = $field_item_list->get(0);
+ * $property = $field_item->get('propertyName');
+ * $value = $property->getValue();
+ * @endcode
+ * Some notes:
+ * - $property, $field_item, and $field_item_list are all typed data objects,
+ *   while $value is a raw PHP value.
+ * - You can call $property->getParent() to get $field_item,
+ *   $field_item->getParent() to get $field_item_list, or
+ *   $field_item_list->getParent() to get $typed_entity ($entity wrapped in a
+ *   typed data object). $typed_entity->getParent() is NULL.
+ * - For all of these ->getRoot() returns $typed_entity.
+ * - The langcode property is on $field_item_list, but you can access it
+ *   on $property as well, so that all items will report the same langcode.
+ * - When the value of $property is changed by calling $property->setValue(),
+ *   $property->onChange() will fire, which in turn calls the parent object's
+ *   onChange() method and so on. This allows parent objects to react upon
+ *   changes of contained properties or list items.
+ *
+ * @section sec_defining Defining data types
+ * To define a new data type:
+ * - Create a class that implements one of the Typed Data interfaces.
+ *   Typically, you will want to extend one of the classes listed in the
+ *   sections above as a starting point.
+ * - Make your class into a DataType plugin. To do that, put it in namespace
+ *   \Drupal\yourmodule\Plugin\DataType (where "yourmodule" is your module's
+ *   short name), and add annotation of type
+ *   \Drupal\Core\TypedData\Annotation\DataType to the documentation header.
+ *   See the @link plugin_api Plugin API topic @endlink and the
+ *   @link annotation Annotations topic @endlink for more information.
+ *
+ * @section sec_using Using data types
+ * The data types of the Typed Data API can be used in several ways, once they
+ * have been defined:
+ * - In the Field API, data types can be used as the class in the property
+ *   definition of the field. See the @link field Field API topic @endlink for
+ *   more information.
+ * - In configuration schema files, you can use the unique ID ('id' annotation)
+ *   from any DataType plugin class as the 'type' value for an entry. See the
+ *   @link config_api Configuration API topic @endlink for more information.
+ * - If you need to create a typed data object in code, first get the
+ *   typed_data_manager service from the container or by calling
+ *   \Drupal::typedDataManager(). Then pass the plugin ID to
+ *   $manager::createDataDefinition() to create an appropriate data definition
+ *   object. Then pass the data definition object and the value of the data to
+ *   $manager::create() to create a typed data object.
+ *
+ * @see plugin_api
+ * @see container
+ * @}
+ */
+
+/**
+ * @defgroup testing Automated tests
+ * @{
+ * Overview of PHPUnit and Nightwatch automated tests.
+ *
+ * The Drupal project has embraced a philosophy of using automated tests,
+ * consisting of both unit tests (which test the functionality of classes at a
+ * low level) and functional tests (which test the functionality of Drupal
+ * systems at a higher level, usually involving web output). The goal is to
+ * have test coverage for all or most of the components and features, and to
+ * run the automated tests before any code is changed or added, to make sure
+ * it doesn't break any existing functionality (regression testing).
+ *
+ * In order to implement this philosophy, developers need to do the following:
+ * - When making a patch to fix a bug, make sure that the bug fix patch includes
+ *   a test that fails without the code change and passes with the code change.
+ *   This helps reviewers understand what the bug is, demonstrates that the code
+ *   actually fixes the bug, and ensures the bug will not reappear due to later
+ *   code changes.
+ * - When making a patch to implement a new feature, include new unit and/or
+ *   functional tests in the patch. This serves to both demonstrate that the
+ *   code actually works, and ensure that later changes do not break the new
+ *   functionality.
+ *
+ * @section write_test Writing tests
+ * All PHP-based tests for Drupal core are written using the industry-standard
+ * PHPUnit framework, with Drupal extensions. There are several categories of
+ * tests; each has its own purpose, base class, namespace, and directory:
+ * - Unit tests:
+ *   - Purpose: Test functionality of a class if the Drupal environment
+ *     (database, settings, etc.) and web browser are not needed for the test,
+ *     or if the Drupal environment can be replaced by a "mock" object.
+ *   - Base class: \Drupal\Tests\UnitTestCase
+ *   - Namespace: \Drupal\Tests\yourmodule\Unit (or a subdirectory)
+ *   - Directory location: yourmodule/tests/src/Unit (or a subdirectory)
+ * - Kernel tests:
+ *   - Purpose: Test functionality of a class if the full Drupal environment
+ *     and web browser are not needed for the test, but the functionality has
+ *     significant Drupal dependencies that cannot easily be mocked. Kernel
+ *     tests can access services, the database, and a minimal mocked file
+ *     system, and they use an in-memory pseudo-installation. However, modules
+ *     are only installed to the point of having services and hooks, unless you
+ *     install them explicitly.
+ *   - Base class: \Drupal\KernelTests\KernelTestBase
+ *   - Namespace: \Drupal\Tests\yourmodule\Kernel (or a subdirectory)
+ *   - Directory location: yourmodule/tests/src/Kernel (or a subdirectory)
+ * - Browser tests:
+ *   - Purpose: Test functionality with the full Drupal environment and an
+ *     internal simulated web browser, if JavaScript is not needed.
+ *   - Base class: \Drupal\Tests\BrowserTestBase
+ *   - Namespace: \Drupal\Tests\yourmodule\Functional (or a subdirectory)
+ *   - Directory location: yourmodule/tests/src/Functional (or a subdirectory)
+ * - Browser tests with JavaScript:
+ *   - Purpose: Test functionality with the full Drupal environment and an
+ *     internal web browser that includes JavaScript execution.
+ *   - Base class: \Drupal\FunctionalJavascriptTests\WebDriverTestBase
+ *   - Namespace: \Drupal\Tests\yourmodule\FunctionalJavascript (or a
+ *     subdirectory)
+ *   - Directory location: yourmodule/tests/src/FunctionalJavascript (or a
+ *     subdirectory)
+ * - Build tests:
+ *   - Purpose: Test building processes and their outcomes, such as whether a
+ *     live update process actually works, or whether a Composer project
+ *     template actually builds a working site. Provides a temporary build
+ *     workspace and a PHP-native HTTP server to send requests to the site
+ *     you've built.
+ *   - Base class: \Drupal\BuildTests\Framework\BuildTestBase
+ *   - Namespace: \Drupal\Tests\yourmodule\Build (or a
+ *     subdirectory)
+ *   - Directory location: yourmodule/tests/src/Build (or a
+ *     subdirectory)
+ *
+ * Some notes about writing PHP test classes:
+ * - The class needs a phpDoc comment block with a description and
+ *   @group annotation, which gives information about the test.
+ * - For unit tests, this comment block should also have @coversDefaultClass
+ *   annotation.
+ * - When writing tests, put the test code into public methods, each covering a
+ *   logical subset of the functionality that is being tested.
+ * - The test methods must have names starting with 'test'. For unit tests, the
+ *   test methods need to have a phpDoc block with @covers annotation telling
+ *   which class method they are testing.
+ * - In some cases, you may need to write a test module to support your test;
+ *   put such modules under the yourmodule/tests/modules directory.
+ *
+ * Besides the PHPUnit tests described above, Drupal Core also includes a few
+ * JavaScript-only tests, which use the Nightwatch.js framework to test
+ * JavaScript code using only JavaScript. These are located in
+ * core/tests/Drupal/Nightwatch.
+ *
+ * For more details, see:
+ * - core/tests/README.md for instructions on running tests
+ * - https://www.drupal.org/phpunit for full documentation on how to write
+ *   and run PHPUnit tests for Drupal.
+ * - http://phpunit.de for general information on the PHPUnit framework.
+ * - @link oo_conventions Object-oriented programming topic @endlink for more
+ *   on PSR-4, namespaces, and where to place classes.
+ * - http://nightwatchjs.org/ for information about Nightwatch testing for
+ *   JavaScript
+ * @}
+ */
+
+/**
+ * @defgroup php_assert PHP Runtime Assert Statements
+ * @{
+ * Use of the assert() statement in Drupal.
+ *
+ * Unit tests also use the term "assertion" to refer to test conditions, so to
+ * avoid confusion the term "runtime assertion" will be used for the assert()
+ * statement throughout the documentation.
+ *
+ * A runtime assertion is a statement that is expected to always be true at
+ * the point in the code it appears at. They are tested using PHP's internal
+ * @link http://php.net/assert assert() @endlink statement. If an
+ * assertion is ever FALSE it indicates an error in the code or in module or
+ * theme configuration files. User-provided configuration files should be
+ * verified with standard control structures at all times, not just checked in
+ * development environments with assert() statements on.
+ *
+ * The Drupal project primarily uses runtime assertions to enforce the
+ * expectations of the API by failing when incorrect calls are made by code
+ * under development. While PHP type hinting does this for objects and arrays,
+ * runtime assertions do this for scalars (strings, integers, floats, etc.) and
+ * complex data structures such as cache and render arrays. They ensure that
+ * methods' return values are the documented data types. They also verify that
+ * objects have been properly configured and set up by the service container.
+ * They supplement unit tests by checking scenarios that do not have unit tests
+ * written for them.
+ *
+ * There are two php settings which affect runtime assertions. The first,
+ * assert.exception, should always be set to 1. The second is zend.assertions.
+ * Set this to -1 in production and 1 in development.
+ *
+ * See https://www.drupal.org/node/2492225 for more information on runtime
+ * assertions.
+ * @}
+ */
+
+/**
+ * @defgroup info_types Information types
+ * @{
+ * Types of information in Drupal.
+ *
+ * Drupal has several distinct types of information, each with its own methods
+ * for storage and retrieval:
+ * - Content: Information meant to be displayed on your site: articles, basic
+ *   pages, images, files, custom blocks, etc. Content is stored and accessed
+ *   using @link entity_api Entities @endlink.
+ * - Session: Information about individual users' interactions with the site,
+ *   such as whether they are logged in. This is really "state" information, but
+ *   it is not stored the same way so it's a separate type here. Session data is
+ *   accessed via \Symfony\Component\HttpFoundation\Request::getSession(), which
+ *   returns an instance of
+ *   \Symfony\Component\HttpFoundation\Session\SessionInterface.
+ *   See the @link session Sessions topic @endlink for more information.
+ * - State: Information of a temporary nature, generally machine-generated and
+ *   not human-edited, about the current state of your site. Examples: the time
+ *   when Cron was last run, whether node access permissions need rebuilding,
+ *   etc. See @link state_api the State API topic @endlink for more information.
+ * - Configuration: Information about your site that is generally (or at least
+ *   can be) human-edited, but is not Content, and is meant to be relatively
+ *   permanent. Examples: the name of your site, the content types and views
+ *   you have defined, etc. See
+ *   @link config_api the Configuration API topic @endlink for more information.
+ *
+ * @see cache
+ * @see i18n
+ * @}
+ */
+
+/**
+ * @defgroup extending Extending and altering Drupal
+ * @{
+ * Overview of extensions and alteration methods for Drupal.
+ *
+ * @section sec_types Types of extensions
+ * Drupal's core behavior can be extended and altered via these three basic
+ * types of extensions:
+ * - Themes: Themes alter the appearance of Drupal sites. They can include
+ *   template files, which alter the HTML markup and other raw output of the
+ *   site; CSS files, which alter the styling applied to the HTML; and
+ *   JavaScript, Flash, images, and other files. For more information, see the
+ *   @link theme_render Theme system and render API topic @endlink and
+ *   https://www.drupal.org/docs/8/theming
+ * - Modules: Modules add to or alter the behavior and functionality of Drupal,
+ *   by using one or more of the methods listed below. For more information
+ *   about creating modules, see https://www.drupal.org/developing/modules/8
+ * - Installation profiles: Installation profiles can be used to
+ *   create distributions, which are complete specific-purpose packages of
+ *   Drupal including additional modules, themes, and data. For more
+ *   information, see https://www.drupal.org/developing/distributions.
+ *
+ * @section sec_alter Alteration methods for modules
+ * Here is a list of the ways that modules can alter or extend Drupal's core
+ * behavior, or the behavior of other modules:
+ * - Hooks: Specially-named functions that a module defines, which are
+ *   discovered and called at specific times, usually to alter behavior or data.
+ *   See the @link hooks Hooks topic @endlink for more information.
+ * - Plugins: Classes that a module defines, which are discovered and
+ *   instantiated at specific times to add functionality. See the
+ *   @link plugin_api Plugin API topic @endlink for more information.
+ * - Entities: Special plugins that define entity types for storing new types
+ *   of content or configuration in Drupal. See the
+ *   @link entity_api Entity API topic @endlink for more information.
+ * - Services: Classes that perform basic operations within Drupal, such as
+ *   accessing the database and sending email. See the
+ *   @link container Dependency Injection Container and Services topic @endlink
+ *   for more information.
+ * - Routing: Providing or altering "routes", which are URLs that Drupal
+ *   responds to, or altering routing behavior with event listener classes.
+ *   See the @link menu Routing and menu topic @endlink for more information.
+ * - Events: Modules can register as event subscribers; when an event is
+ *   dispatched, a method is called on each registered subscriber, allowing each
+ *   one to react. See the @link events Events topic @endlink for more
+ *   information.
+ *
+ * @section sec_sample *.info.yml files
+ * Extensions must each be located in a directory whose name matches the short
+ * name (or machine name) of the extension, and this directory must contain a
+ * file named machine_name.info.yml (where machine_name is the machine name of
+ * the extension). See \Drupal\Core\Extension\InfoParserInterface::parse() for
+ * documentation of the format of .info.yml files.
+ * @}
+ */
+
+/**
+ * @defgroup plugin_api Plugin API
+ * @{
+ * Using the Plugin API
+ *
+ * @section sec_overview Overview and terminology
+ *
+ * The basic idea of plugins is to allow a particular module or subsystem of
+ * Drupal to provide functionality in an extensible, object-oriented way. The
+ * controlling module or subsystem defines the basic framework (interface) for
+ * the functionality, and other modules can create plugins (implementing the
+ * interface) with particular behaviors. The controlling module instantiates
+ * existing plugins as needed, and calls methods to invoke their functionality.
+ * Examples of functionality in Drupal Core that use plugins include: the block
+ * system (block types are plugins), the entity/field system (entity types,
+ * field types, field formatters, and field widgets are plugins), the image
+ * manipulation system (image effects and image toolkits are plugins), and the
+ * search system (search page types are plugins).
+ *
+ * Plugins are grouped into plugin types, each generally defined by an
+ * interface. Each plugin type is managed by a plugin manager service, which
+ * uses a plugin discovery method to discover provided plugins of that type and
+ * instantiate them using a plugin factory.
+ *
+ * Some plugin types make use of the following concepts or components:
+ * - Plugin derivatives: Allows a single plugin class to present itself as
+ *   multiple plugins. Example: the Menu module provides a block for each
+ *   defined menu via a block plugin derivative.
+ * - Plugin mapping: Allows a plugin class to map a configuration string to an
+ *   instance, and have the plugin automatically instantiated without writing
+ *   additional code.
+ * - Plugin collections: Provide a way to lazily instantiate a set of plugin
+ *   instances from a single plugin definition.
+ *
+ * There are several things a module developer may need to do with plugins:
+ * - Define a completely new plugin type: see @ref sec_define below.
+ * - Create a plugin of an existing plugin type: see @ref sec_create below.
+ * - Perform tasks that involve plugins: see @ref sec_use below.
+ *
+ * See https://www.drupal.org/developing/api/8/plugins for more detailed
+ * documentation on the plugin system. There are also topics for a few
+ * of the many existing types of plugins:
+ * - @link block_api Block API @endlink
+ * - @link entity_api Entity API @endlink
+ * - @link field Various types of field-related plugins @endlink
+ * - @link views_plugins Views plugins @endlink (has links to topics covering
+ *   various specific types of Views plugins).
+ * - @link search Search page plugins @endlink
+ *
+ * @section sec_define Defining a new plugin type
+ * To define a new plugin type:
+ * - Define an interface for the plugin. This describes the common set of
+ *   behavior, and the methods you will call on each plugin class that is
+ *   instantiated. Usually this interface will extend one or more of the
+ *   following interfaces:
+ *   - \Drupal\Component\Plugin\PluginInspectionInterface
+ *   - \Drupal\Component\Plugin\ConfigurableInterface
+ *   - \Drupal\Component\Plugin\DependentPluginInterface
+ *   - \Drupal\Component\Plugin\ContextAwarePluginInterface
+ *   - \Drupal\Core\Plugin\PluginFormInterface
+ *   - \Drupal\Core\Executable\ExecutableInterface
+ * - (optional) Create a base class that provides a partial implementation of
+ *   the interface, for the convenience of developers wishing to create plugins
+ *   of your type. The base class usually extends
+ *   \Drupal\Core\Plugin\PluginBase, or one of the base classes that extends
+ *   this class.
+ * - Choose a method for plugin discovery, and define classes as necessary.
+ *   See @ref sub_discovery below.
+ * - Create a plugin manager/factory class and service, which will discover and
+ *   instantiate plugins. See @ref sub_manager below.
+ * - Use the plugin manager to instantiate plugins. Call methods on your plugin
+ *   interface to perform the tasks of your plugin type.
+ * - (optional) If appropriate, define a plugin collection. See @ref
+ *    sub_collection below for more information.
+ *
+ * @subsection sub_discovery Plugin discovery
+ * Plugin discovery is the process your plugin manager uses to discover the
+ * individual plugins of your type that have been defined by your module and
+ * other modules. Plugin discovery methods are classes that implement
+ * \Drupal\Component\Plugin\Discovery\DiscoveryInterface. Most plugin types use
+ * one of the following discovery mechanisms:
+ * - Annotation: Plugin classes are annotated and placed in a defined namespace
+ *   subdirectory. Most Drupal Core plugins use this method of discovery.
+ * - Hook: Plugin modules need to implement a hook to tell the manager about
+ *   their plugins.
+ * - YAML: Plugins are listed in YAML files. Drupal Core uses this method for
+ *   discovering local tasks and local actions. This is mainly useful if all
+ *   plugins use the same class, so it is kind of like a global derivative.
+ * - Static: Plugin classes are registered within the plugin manager class
+ *   itself. Static discovery is only useful if modules cannot define new
+ *   plugins of this type (if the list of available plugins is static).
+ *
+ * It is also possible to define your own custom discovery mechanism or mix
+ * methods together. And there are many more details, such as annotation
+ * decorators, that apply to some of the discovery methods. See
+ * https://www.drupal.org/developing/api/8/plugins for more details.
+ *
+ * The remainder of this documentation will assume Annotation-based discovery,
+ * since this is the most common method.
+ *
+ * @subsection sub_manager Defining a plugin manager class and service
+ * To define an annotation-based plugin manager:
+ * - Choose a namespace subdirectory for your plugin. For example, search page
+ *   plugins go in directory Plugin/Search under the module namespace.
+ * - Define an annotation class for your plugin type. This class should extend
+ *   \Drupal\Component\Annotation\Plugin, and for most plugin types, it should
+ *   contain member variables corresponding to the annotations plugins will
+ *   need to provide. All plugins have at least $id: a unique string
+ *   identifier.
+ * - Define an alter hook for altering the discovered plugin definitions. You
+ *   should document the hook in a *.api.php file.
+ * - Define a plugin manager class. This class should implement
+ *   \Drupal\Component\Plugin\PluginManagerInterface; most plugin managers do
+ *   this by extending \Drupal\Core\Plugin\DefaultPluginManager. If you do
+ *   extend the default plugin manager, the only method you will probably need
+ *   to define is the class constructor, which will need to call the parent
+ *   constructor to provide information about the annotation class and plugin
+ *   namespace for discovery, set up the alter hook, and possibly set up
+ *   caching. See classes that extend DefaultPluginManager for examples.
+ * - Define a service for your plugin manager. See the
+ *   @link container Services topic for more information. @endlink Your service
+ *   definition should look something like this, referencing your manager
+ *   class and the parent (default) plugin manager service to inherit
+ *   constructor arguments:
+ *   @code
+ *   plugin.manager.mymodule:
+ *     class: Drupal\mymodule\MyPluginManager
+ *     parent: default_plugin_manager
+ *   @endcode
+ * - If your plugin is configurable, you will also need to define the
+ *   configuration schema and possibly a configuration entity type. See the
+ *   @link config_api Configuration API topic @endlink for more information.
+ *
+ * @subsection sub_collection Defining a plugin collection
+ * Some configurable plugin types allow administrators to create zero or more
+ * instances of each plugin, each with its own configuration. For example,
+ * a single block plugin can be configured several times, to display in
+ * different regions of a theme, with different visibility settings, a
+ * different title, or other plugin-specific settings. To make this possible,
+ * a plugin type can make use of what's known as a plugin collection.
+ *
+ * A plugin collection is a class that extends
+ * \Drupal\Component\Plugin\LazyPluginCollection or one of its subclasses; there
+ * are several examples in Drupal Core. If your plugin type uses a plugin
+ * collection, it will usually also have a configuration entity, and the entity
+ * class should implement
+ * \Drupal\Core\Entity\EntityWithPluginCollectionInterface. Again, there are
+ * several examples in Drupal Core; see also the @link config_api Configuration
+ * API topic @endlink for more information about configuration entities.
+ *
+ * @section sec_create Creating a plugin of an existing type
+ * Assuming the plugin type uses annotation-based discovery, in order to create
+ * a plugin of an existing type, you will be creating a class. This class must:
+ * - Implement the plugin interface, so that it has the required methods
+ *   defined. Usually, you'll want to extend the plugin base class, if one has
+ *   been provided.
+ * - Have the right annotation in its documentation header. See the
+ *   @link annotation Annotation topic @endlink for more information about
+ *   annotation.
+ * - Be in the right plugin namespace, in order to be discovered.
+ * Often, the easiest way to make sure this happens is to find an existing
+ * example of a working plugin class of the desired type, and copy it into your
+ * module as a starting point.
+ *
+ * You can also create a plugin derivative, which allows your plugin class
+ * to present itself to the user interface as multiple plugins. To do this,
+ * in addition to the plugin class, you'll need to create a separate plugin
+ * derivative class implementing
+ * \Drupal\Component\Plugin\Derivative\DerivativeInterface. The classes
+ * \Drupal\system\Plugin\Block\SystemMenuBlock (plugin class) and
+ * \Drupal\system\Plugin\Derivative\SystemMenuBlock (derivative class) are a
+ * good example to look at.
+ *
+ * @section sec_use Performing tasks involving plugins
+ * Here are the steps to follow to perform a task that involves plugins:
+ * - Locate the machine name of the plugin manager service, and instantiate the
+ *   service. See the @link container Services topic @endlink for more
+ *   information on how to do this.
+ * - On the plugin manager class, use methods like getDefinition(),
+ *   getDefinitions(), or other methods specific to particular plugin managers
+ *   to retrieve information about either specific plugins or the entire list of
+ *   defined plugins.
+ * - Call the createInstance() method on the plugin manager to instantiate
+ *   individual plugin objects.
+ * - Call methods on the plugin objects to perform the desired tasks.
+ *
+ * @see annotation
+ * @}
+ */
+
+/**
+ * @defgroup oo_conventions Objected-oriented programming conventions
+ * @{
+ * PSR-4, namespaces, class naming, and other conventions.
+ *
+ * A lot of the PHP code in Drupal is object oriented (OO), making use of
+ * @link http://php.net/manual/language.oop5.php PHP classes, interfaces, and traits @endlink
+ * (which are loosely referred to as "classes" in the rest of this topic). The
+ * following conventions and standards apply to this version of Drupal:
+ * - Each class must be in its own file.
+ * - Classes must be namespaced. If a module defines a class, the namespace
+ *   must start with \Drupal\module_name. If it is defined by Drupal Core for
+ *   use across many modules, the namespace should be \Drupal\Core or
+ *   \Drupal\Component, with the exception of the global class \Drupal. See
+ *   https://www.drupal.org/node/1353118 for more about namespaces.
+ * - In order for the PSR-4-based class auto-loader to find the class, it must
+ *   be located in a directory corresponding to the namespace. For
+ *   module-defined classes, if the namespace is \Drupal\module_name\foo\bar,
+ *   then the class goes under the main module directory in directory
+ *   src/foo/bar. For Drupal-wide classes, if the namespace is
+ *   \Drupal\Core\foo\bar, then it goes in directory
+ *   core/lib/Drupal/Core/foo/bar. See https://www.drupal.org/node/2156625 for
+ *   more information about PSR-4.
+ * - Some classes have annotations added to their documentation headers. See
+ *   the @link annotation Annotation topic @endlink for more information.
+ * - Standard plugin discovery requires particular namespaces and annotation
+ *   for most plugin classes. See the
+ *   @link plugin_api Plugin API topic @endlink for more information.
+ * - There are project-wide coding standards for OO code, including naming:
+ *   https://www.drupal.org/node/608152
+ * - Documentation standards for classes are covered on:
+ *   https://www.drupal.org/coding-standards/docs#classes
+ * @}
+ */
+
+/**
+ * @defgroup listing_page_class Page header for Classes page
+ * @{
+ * Introduction to classes
+ *
+ * A lot of the PHP code in Drupal is object oriented (OO), making use of
+ * @link http://php.net/manual/language.oop5.php PHP classes, interfaces, and traits. @endlink
+ * See the
+ * @link oo_conventions Objected-oriented programming conventions @endlink
+ * for more information.
+ *
+ * @see oo_conventions
+ *
+ * @}
+ */
+
+/**
+ * @defgroup listing_page_namespace Page header for Namespaces page
+ * @{
+ * Introduction to namespaces
+ *
+ * PHP classes, interfaces, and traits in Drupal are
+ * @link http://php.net/manual/language.namespaces.rationale.php namespaced. @endlink
+ * See the
+ * @link oo_conventions Objected-oriented programming conventions @endlink
+ * for more information.
+ *
+ * @see oo_conventions
+ *
+ * @}
+ */
+
+/**
+ * @defgroup best_practices Best practices for developers
+ * @{
+ * Overview of standards and best practices for developers
+ *
+ * Ideally, all code that is included in Drupal Core and contributed modules,
+ * themes, and distributions will be secure, internationalized, maintainable,
+ * and efficient. In order to facilitate this, the Drupal community has
+ * developed a set of guidelines and standards for developers to follow. Most of
+ * these standards can be found under
+ * @link https://www.drupal.org/developing/best-practices Best practices on Drupal.org @endlink
+ *
+ * Standards and best practices that developers should be aware of include:
+ * - Security: https://www.drupal.org/writing-secure-code and the
+ *   @link sanitization Sanitization functions topic @endlink
+ * - Coding standards: https://www.drupal.org/coding-standards
+ *   and https://www.drupal.org/coding-standards/docs
+ * - Accessibility: https://www.drupal.org/node/1637990 (modules) and
+ *   https://www.drupal.org/node/464472 (themes)
+ * - Usability: https://www.drupal.org/ui-standards
+ * - Internationalization: @link i18n Internationalization topic @endlink
+ * - Automated testing: @link testing Automated tests topic @endlink
+ * @}
+ */
+
+/**
+ * @defgroup utility Utility classes and functions
+ * @{
+ * Overview of utility classes and functions for developers.
+ *
+ * Drupal provides developers with a variety of utility functions that make it
+ * easier and more efficient to perform tasks that are either really common,
+ * tedious, or difficult. Utility functions help to reduce code duplication and
+ * should be used in place of one-off code whenever possible.
+ *
+ * @see common.inc
+ * @see file
+ * @see format
+ * @see php_wrappers
+ * @see sanitization
+ * @see transliteration
+ * @see validation
+ * @}
+ */
+
+/**
+ * @defgroup hooks Hooks
+ * @{
+ * Define functions that alter the behavior of Drupal core.
+ *
+ * One way for modules to alter the core behavior of Drupal (or another module)
+ * is to use hooks. Hooks are specially-named functions that a module defines
+ * (this is known as "implementing the hook"), which are discovered and called
+ * at specific times to alter or add to the base behavior or data (this is
+ * known as "invoking the hook"). Each hook has a name (example:
+ * hook_batch_alter()), a defined set of parameters, and a defined return value.
+ * Your modules can implement hooks that are defined by Drupal core or other
+ * modules that they interact with. Your modules can also define their own
+ * hooks, in order to let other modules interact with them.
+ *
+ * To implement a hook:
+ * - Locate the documentation for the hook. Hooks are documented in *.api.php
+ *   files, by defining functions whose name starts with "hook_" (these
+ *   files and their functions are never loaded by Drupal -- they exist solely
+ *   for documentation). The function should have a documentation header, as
+ *   well as a sample function body. For example, in the core file form.api.php,
+ *   you can find hooks such as hook_batch_alter(). Also, if you are viewing
+ *   this documentation on an API reference site, the Core hooks will be listed
+ *   in this topic.
+ * - Copy the function to your module's .module file.
+ * - Change the name of the function, substituting your module's short name
+ *   (name of the module's directory, and .info.yml file without the extension)
+ *   for the "hook" part of the sample function name. For instance, to implement
+ *   hook_batch_alter(), you would rename it to my_module_batch_alter().
+ * - Edit the documentation for the function (normally, your implementation
+ *   should just have one line saying "Implements hook_batch_alter().").
+ * - Edit the body of the function, substituting in what you need your module
+ *   to do.
+ *
+ * To define a hook:
+ * - Choose a unique name for your hook. It should start with "hook_", followed
+ *   by your module's short name.
+ * - Provide documentation in a *.api.php file in your module's main
+ *   directory. See the "implementing" section above for details of what this
+ *   should contain (parameters, return value, and sample function body).
+ * - Invoke the hook in your module's code.
+ *
+ * To invoke a hook, use methods on
+ * \Drupal\Core\Extension\ModuleHandlerInterface such as alter(), invoke(),
+ * and invokeAll(). You can obtain a module handler by calling
+ * \Drupal::moduleHandler(), or getting the 'module_handler' service on an
+ * injected container.
+ *
+ * @see extending
+ * @see themeable
+ * @see callbacks
+ * @see \Drupal\Core\Extension\ModuleHandlerInterface
+ * @see \Drupal::moduleHandler()
+ *
+ * @}
+ */
+
+/**
+ * @defgroup callbacks Callbacks
+ * @{
+ * Callback function signatures.
+ *
+ * Drupal's API sometimes uses callback functions to allow you to define how
+ * some type of processing happens. A callback is a function with a defined
+ * signature, which you define in a module. Then you pass the function name as
+ * a parameter to a Drupal API function or return it as part of a hook
+ * implementation return value, and your function is called at an appropriate
+ * time. For instance, when setting up batch processing you might need to
+ * provide a callback function for each processing step and/or a callback for
+ * when processing is finished; you would do that by defining these functions
+ * and passing their names into the batch setup function.
+ *
+ * Callback function signatures, like hook definitions, are described by
+ * creating and documenting dummy functions in a *.api.php file; normally, the
+ * dummy callback function's name should start with "callback_", and you should
+ * document the parameters and return value and provide a sample function body.
+ * Then your API documentation can refer to this callback function in its
+ * documentation. A user of your API can usually name their callback function
+ * anything they want, although a standard name would be to replace "callback_"
+ * with the module name.
+ *
+ * @see hooks
+ * @see themeable
+ *
+ * @}
+ */
+
+/**
+ * @defgroup form_api Form generation
+ * @{
+ * Describes how to generate and manipulate forms and process form submissions.
+ *
+ * Drupal provides a Form API in order to achieve consistency in its form
+ * processing and presentation, while simplifying code and reducing the amount
+ * of HTML that must be explicitly generated by a module.
+ *
+ * @section generating_forms Creating forms
+ * Forms are defined as classes that implement the
+ * \Drupal\Core\Form\FormInterface and are built using the
+ * \Drupal\Core\Form\FormBuilder class. Drupal provides a couple of utility
+ * classes that can be extended as a starting point for most basic forms, the
+ * most commonly used of which is \Drupal\Core\Form\FormBase. FormBuilder
+ * handles the low level processing of forms such as rendering the necessary
+ * HTML, initial processing of incoming $_POST data, and delegating to your
+ * implementation of FormInterface for validation and processing of submitted
+ * data.
+ *
+ * Here is an example of a Form class:
+ * @code
+ * namespace Drupal\mymodule\Form;
+ *
+ * use Drupal\Core\Form\FormBase;
+ * use Drupal\Core\Form\FormStateInterface;
+ *
+ * class ExampleForm extends FormBase {
+ *   public function getFormId() {
+ *     // Unique ID of the form.
+ *     return 'example_form';
+ *   }
+ *
+ *   public function buildForm(array $form, FormStateInterface $form_state) {
+ *     // Create a $form API array.
+ *     $form['phone_number'] = array(
+ *       '#type' => 'tel',
+ *       '#title' => $this->t('Your phone number'),
+ *     );
+ *     $form['save'] = array(
+ *       '#type' => 'submit',
+ *       '#value' => $this->t('Save'),
+ *     );
+ *     return $form;
+ *   }
+ *
+ *   public function validateForm(array &$form, FormStateInterface $form_state) {
+ *     // Validate submitted form data.
+ *   }
+ *
+ *   public function submitForm(array &$form, FormStateInterface $form_state) {
+ *     // Handle submitted form data.
+ *   }
+ * }
+ * @endcode
+ *
+ * @section retrieving_forms Retrieving and displaying forms
+ * \Drupal::formBuilder()->getForm() should be used to handle retrieving,
+ * processing, and displaying a rendered HTML form. Given the ExampleForm
+ * defined above,
+ * \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm') would
+ * return the rendered HTML of the form defined by ExampleForm::buildForm(), or
+ * call the validateForm() and submitForm(), methods depending on the current
+ * processing state.
+ *
+ * The argument to \Drupal::formBuilder()->getForm() is the name of a class that
+ * implements FormInterface. Any additional arguments passed to the getForm()
+ * method will be passed along as additional arguments to the
+ * ExampleForm::buildForm() method.
+ *
+ * For example:
+ * @code
+ * $extra = '612-123-4567';
+ * $form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\ExampleForm', $extra);
+ * ...
+ * public function buildForm(array $form, FormStateInterface $form_state, $extra = NULL)
+ *   $form['phone_number'] = array(
+ *     '#type' => 'tel',
+ *     '#title' => $this->t('Your phone number'),
+ *     '#value' => $extra,
+ *   );
+ *   return $form;
+ * }
+ * @endcode
+ *
+ * Alternatively, forms can be built directly via the routing system which will
+ * take care of calling \Drupal::formBuilder()->getForm(). The following example
+ * demonstrates the use of a routing.yml file to display a form at the given
+ * route.
+ *
+ * @code
+ * example.form:
+ *   path: '/example-form'
+ *   defaults:
+ *     _title: 'Example form'
+ *     _form: '\Drupal\mymodule\Form\ExampleForm'
+ * @endcode
+ *
+ * The $form argument to form-related functions is a specialized render array
+ * containing the elements and properties of the form. For more about render
+ * arrays, see the @link theme_render Render API topic. @endlink For more
+ * detailed explanations of the Form API workflow, see the
+ * @link https://www.drupal.org/node/2117411 Form API documentation section. @endlink
+ * In addition, there is a set of Form API tutorials in the
+ * @link https://www.drupal.org/project/examples Examples for Developers project. @endlink
+ *
+ * In the form builder, validation, submission, and other form methods,
+ * $form_state is the primary influence on the processing of the form and is
+ * passed to most methods, so they can use it to communicate with the form
+ * system and each other. $form_state is an object that implements
+ * \Drupal\Core\Form\FormStateInterface.
+ * @}
+ */
+
+/**
+ * @defgroup queue Queue operations
+ * @{
+ * Queue items to allow later processing.
+ *
+ * The queue system allows placing items in a queue and processing them later.
+ * The system tries to ensure that only one consumer can process an item.
+ *
+ * Before a queue can be used it needs to be created by
+ * Drupal\Core\Queue\QueueInterface::createQueue().
+ *
+ * Items can be added to the queue by passing an arbitrary data object to
+ * Drupal\Core\Queue\QueueInterface::createItem().
+ *
+ * To process an item, call Drupal\Core\Queue\QueueInterface::claimItem() and
+ * specify how long you want to have a lease for working on that item.
+ * When finished processing, the item needs to be deleted by calling
+ * Drupal\Core\Queue\QueueInterface::deleteItem(). If the consumer dies, the
+ * item will be made available again by the Drupal\Core\Queue\QueueInterface
+ * implementation once the lease expires. Another consumer will then be able to
+ * receive it when calling Drupal\Core\Queue\QueueInterface::claimItem().
+ * Due to this, the processing code should be aware that an item might be handed
+ * over for processing more than once.
+ *
+ * The $item object used by the Drupal\Core\Queue\QueueInterface can contain
+ * arbitrary metadata depending on the implementation. Systems using the
+ * interface should only rely on the data property which will contain the
+ * information passed to Drupal\Core\Queue\QueueInterface::createItem().
+ * The full queue item returned by Drupal\Core\Queue\QueueInterface::claimItem()
+ * needs to be passed to Drupal\Core\Queue\QueueInterface::deleteItem() once
+ * processing is completed.
+ *
+ * There are two kinds of queue backends available: reliable, which preserves
+ * the order of messages and guarantees that every item will be executed at
+ * least once. The non-reliable kind only does a best effort to preserve order
+ * in messages and to execute them at least once but there is a small chance
+ * that some items get lost. For example, some distributed back-ends like
+ * Amazon SQS will be managing jobs for a large set of producers and consumers
+ * where a strict FIFO ordering will likely not be preserved. Another example
+ * would be an in-memory queue backend which might lose items if it crashes.
+ * However, such a backend would be able to deal with significantly more writes
+ * than a reliable queue and for many tasks this is more important. See
+ * aggregator_cron() for an example of how to effectively use a non-reliable
+ * queue. Another example is doing Twitter statistics -- the small possibility
+ * of losing a few items is insignificant next to power of the queue being able
+ * to keep up with writes. As described in the processing section, regardless
+ * of the queue being reliable or not, the processing code should be aware that
+ * an item might be handed over for processing more than once (because the
+ * processing code might time out before it finishes).
+ * @}
+ */
+
+/**
+ * @defgroup annotation Annotations
+ * @{
+ * Annotations for class discovery and metadata description.
+ *
+ * The Drupal plugin system has a set of reusable components that developers
+ * can use, override, and extend in their modules. Most of the plugins use
+ * annotations, which let classes register themselves as plugins and describe
+ * their metadata. (Annotations can also be used for other purposes, though
+ * at the moment, Drupal only uses them for the plugin system.)
+ *
+ * To annotate a class as a plugin, add code similar to the following to the
+ * end of the documentation block immediately preceding the class declaration:
+ * @code
+ * * @ContentEntityType(
+ * *   id = "comment",
+ * *   label = @Translation("Comment"),
+ * *   ...
+ * *   base_table = "comment"
+ * * )
+ * @endcode
+ *
+ * Note that you must use double quotes; single quotes will not work in
+ * annotations.
+ *
+ * Some annotation types, which extend the "@ PluginID" annotation class, have
+ * only a single 'id' key in their annotation. For these, it is possible to use
+ * a shorthand annotation. For example:
+ * @code
+ * * @ViewsArea("entity")
+ * @endcode
+ * in place of
+ * @code
+ * * @ViewsArea(
+ * *   id = "entity"
+ * *)
+ * @endcode
+ *
+ * The available annotation classes are listed in this topic, and can be
+ * identified when you are looking at the Drupal source code by having
+ * "@ Annotation" in their documentation blocks (without the space after @). To
+ * find examples of annotation for a particular annotation class, such as
+ * EntityType, look for class files that have an @ annotation section using the
+ * annotation class.
+ *
+ * @see plugin_translatable
+ * @see plugin_context
+ *
+ * @}
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Perform periodic actions.
+ *
+ * Modules that require some commands to be executed periodically can
+ * implement hook_cron(). The engine will then call the hook whenever a cron
+ * run happens, as defined by the administrator. Typical tasks managed by
+ * hook_cron() are database maintenance, backups, recalculation of settings
+ * or parameters, automated mailing, and retrieving remote data.
+ *
+ * Short-running or non-resource-intensive tasks can be executed directly in
+ * the hook_cron() implementation.
+ *
+ * Long-running tasks and tasks that could time out, such as retrieving remote
+ * data, sending email, and intensive file tasks, should use the queue API
+ * instead of executing the tasks directly. To do this, first define one or
+ * more queues via a \Drupal\Core\Annotation\QueueWorker plugin. Then, add items
+ * that need to be processed to the defined queues.
+ */
+function hook_cron() {
+  // Short-running operation example, not using a queue:
+  // Delete all expired records since the last cron run.
+  $expires = \Drupal::state()->get('mymodule.last_check', 0);
+  \Drupal::database()->delete('mymodule_table')
+    ->condition('expires', $expires, '>=')
+    ->execute();
+  \Drupal::state()->set('mymodule.last_check', REQUEST_TIME);
+
+  // Long-running operation example, leveraging a queue:
+  // Queue news feeds for updates once their refresh interval has elapsed.
+  $queue = \Drupal::queue('aggregator_feeds');
+  $ids = \Drupal::entityTypeManager()->getStorage('aggregator_feed')->getFeedIdsToRefresh();
+  foreach (Feed::loadMultiple($ids) as $feed) {
+    if ($queue->createItem($feed)) {
+      // Add timestamp to avoid queueing item more than once.
+      $feed->setQueuedTime(REQUEST_TIME);
+      $feed->save();
+    }
+  }
+  $ids = \Drupal::entityQuery('aggregator_feed')
+    ->condition('queued', REQUEST_TIME - (3600 * 6), '<')
+    ->execute();
+  if ($ids) {
+    $feeds = Feed::loadMultiple($ids);
+    foreach ($feeds as $feed) {
+      $feed->setQueuedTime(0);
+      $feed->save();
+    }
+  }
+}
+
+/**
+ * Alter available data types for typed data wrappers.
+ *
+ * @param array $data_types
+ *   An array of data type information.
+ *
+ * @see hook_data_type_info()
+ */
+function hook_data_type_info_alter(&$data_types) {
+  $data_types['email']['class'] = '\Drupal\mymodule\Type\Email';
+}
+
+/**
+ * Alter cron queue information before cron runs.
+ *
+ * Called by \Drupal\Core\Cron to allow modules to alter cron queue settings
+ * before any jobs are processed.
+ *
+ * @param array $queues
+ *   An array of cron queue information.
+ *
+ * @see \Drupal\Core\Queue\QueueWorkerInterface
+ * @see \Drupal\Core\Annotation\QueueWorker
+ * @see \Drupal\Core\Cron
+ */
+function hook_queue_info_alter(&$queues) {
+  // This site has many feeds so let's spend 90 seconds on each cron run
+  // updating feeds instead of the default 60.
+  $queues['aggregator_feeds']['cron']['time'] = 90;
+}
+
+/**
+ * Alter an email message created with MailManagerInterface->mail().
+ *
+ * Hook hook_mail_alter() allows modification of email messages created and sent
+ * with MailManagerInterface->mail(). Usage examples include adding and/or
+ * changing message text, message fields, and message headers.
+ *
+ * Email messages sent using functions other than MailManagerInterface->mail()
+ * will not invoke hook_mail_alter(). For example, a contributed module directly
+ * calling the MailInterface->mail() or PHP mail() function will not invoke
+ * this hook. All core modules use MailManagerInterface->mail() for messaging,
+ * it is best practice but not mandatory in contributed modules.
+ *
+ * @param $message
+ *   An array containing the message data. Keys in this array include:
+ *   - 'id':
+ *     The MailManagerInterface->mail() id of the message. Look at module source
+ *     code or MailManagerInterface->mail() for possible id values.
+ *   - 'to':
+ *     The address or addresses the message will be sent to. The
+ *     formatting of this string must comply with RFC 2822.
+ *   - 'from':
+ *     The address the message will be marked as being from, which is
+ *     either a custom address or the site-wide default email address.
+ *   - 'subject':
+ *     Subject of the email to be sent. This must not contain any newline
+ *     characters, or the email may not be sent properly.
+ *   - 'body':
+ *     An array of strings or objects that implement
+ *     \Drupal\Component\Render\MarkupInterface containing the message text. The
+ *     message body is created by concatenating the individual array strings
+ *     into a single text string using "\n\n" as a separator.
+ *   - 'headers':
+ *     Associative array containing mail headers, such as From, Sender,
+ *     MIME-Version, Content-Type, etc.
+ *   - 'params':
+ *     An array of optional parameters supplied by the caller of
+ *     MailManagerInterface->mail() that is used to build the message before
+ *     hook_mail_alter() is invoked.
+ *   - 'language':
+ *     The language object used to build the message before hook_mail_alter()
+ *     is invoked.
+ *   - 'send':
+ *     Set to FALSE to abort sending this email message.
+ *
+ * @see \Drupal\Core\Mail\MailManagerInterface::mail()
+ */
+function hook_mail_alter(&$message) {
+  if ($message['id'] == 'modulename_messagekey') {
+    if (!example_notifications_optin($message['to'], $message['id'])) {
+      // If the recipient has opted to not receive such messages, cancel
+      // sending.
+      $message['send'] = FALSE;
+      return;
+    }
+    $message['body'][] = "--\nMail sent out from " . \Drupal::config('system.site')->get('name');
+  }
+}
+
+/**
+ * Prepares a message based on parameters;
+ *
+ * This hook is called from MailManagerInterface->mail(). Note that hook_mail(),
+ * unlike hook_mail_alter(), is only called on the $module argument to
+ * MailManagerInterface->mail(), not all modules.
+ *
+ * @param $key
+ *   An identifier of the mail.
+ * @param $message
+ *   An array to be filled in. Elements in this array include:
+ *   - id: An ID to identify the mail sent. Look at module source code or
+ *     MailManagerInterface->mail() for possible id values.
+ *   - to: The address or addresses the message will be sent to. The
+ *     formatting of this string must comply with RFC 2822.
+ *   - subject: Subject of the email to be sent. This must not contain any
+ *     newline characters, or the mail may not be sent properly.
+ *     MailManagerInterface->mail() sets this to an empty
+ *     string when the hook is invoked.
+ *   - body: An array of lines containing the message to be sent. Drupal will
+ *     format the correct line endings for you. MailManagerInterface->mail()
+ *     sets this to an empty array when the hook is invoked. The array may
+ *     contain either strings or objects implementing
+ *     \Drupal\Component\Render\MarkupInterface.
+ *   - from: The address the message will be marked as being from, which is
+ *     set by MailManagerInterface->mail() to either a custom address or the
+ *     site-wide default email address when the hook is invoked.
+ *   - headers: Associative array containing mail headers, such as From,
+ *     Sender, MIME-Version, Content-Type, etc.
+ *     MailManagerInterface->mail() pre-fills several headers in this array.
+ * @param $params
+ *   An array of parameters supplied by the caller of
+ *   MailManagerInterface->mail().
+ *
+ * @see \Drupal\Core\Mail\MailManagerInterface::mail()
+ */
+function hook_mail($key, &$message, $params) {
+  $account = $params['account'];
+  $context = $params['context'];
+  $variables = [
+    '%site_name' => \Drupal::config('system.site')->get('name'),
+    '%username' => $account->getDisplayName(),
+  ];
+  if ($context['hook'] == 'taxonomy') {
+    $entity = $params['entity'];
+    $vocabulary = Vocabulary::load($entity->id());
+    $variables += [
+      '%term_name' => $entity->name,
+      '%term_description' => $entity->description,
+      '%term_id' => $entity->id(),
+      '%vocabulary_name' => $vocabulary->label(),
+      '%vocabulary_description' => $vocabulary->getDescription(),
+      '%vocabulary_id' => $vocabulary->id(),
+    ];
+  }
+
+  // Node-based variable translation is only available if we have a node.
+  if (isset($params['node'])) {
+    /** @var \Drupal\node\NodeInterface $node */
+    $node = $params['node'];
+    $variables += [
+      '%uid' => $node->getOwnerId(),
+      '%url' => $node->toUrl('canonical', ['absolute' => TRUE])->toString(),
+      '%node_type' => node_get_type_label($node),
+      '%title' => $node->getTitle(),
+      '%teaser' => $node->teaser,
+      '%body' => $node->body,
+    ];
+  }
+  $subject = strtr($context['subject'], $variables);
+  $body = strtr($context['message'], $variables);
+  $message['subject'] .= str_replace(["\r", "\n"], '', $subject);
+  $message['body'][] = MailFormatHelper::htmlToText($body);
+}
+
+/**
+ * Alter the list of mail backend plugin definitions.
+ *
+ * @param array $info
+ *   The mail backend plugin definitions to be altered.
+ *
+ * @see \Drupal\Core\Annotation\Mail
+ * @see \Drupal\Core\Mail\MailManager
+ */
+function hook_mail_backend_info_alter(&$info) {
+  unset($info['test_mail_collector']);
+}
+
+/**
+ * Alter the default country list.
+ *
+ * @param $countries
+ *   The associative array of countries keyed by two-letter country code.
+ *
+ * @see \Drupal\Core\Locale\CountryManager::getList()
+ */
+function hook_countries_alter(&$countries) {
+  // Elbonia is now independent, so add it to the country list.
+  $countries['EB'] = 'Elbonia';
+}
+
+/**
+ * Alter display variant plugin definitions.
+ *
+ * @param array $definitions
+ *   The array of display variant definitions, keyed by plugin ID.
+ *
+ * @see \Drupal\Core\Display\VariantManager
+ * @see \Drupal\Core\Display\Annotation\DisplayVariant
+ */
+function hook_display_variant_plugin_alter(array &$definitions) {
+  $definitions['full_page']['admin_label'] = t('Block layout');
+}
+
+/**
+ * Allow modules to alter layout plugin definitions.
+ *
+ * @param \Drupal\Core\Layout\LayoutDefinition[] $definitions
+ *   The array of layout definitions, keyed by plugin ID.
+ */
+function hook_layout_alter(&$definitions) {
+  // Remove a layout.
+  unset($definitions['twocol']);
+}
+
+/**
+ * Flush all persistent and static caches.
+ *
+ * This hook asks your module to clear all of its static caches,
+ * in order to ensure a clean environment for subsequently
+ * invoked data rebuilds.
+ *
+ * Do NOT use this hook for rebuilding information. Only use it to flush custom
+ * caches.
+ *
+ * Static caches using drupal_static() do not need to be reset manually.
+ * However, all other static variables that do not use drupal_static() must be
+ * manually reset.
+ *
+ * This hook is invoked by drupal_flush_all_caches(). It runs before module data
+ * is updated and before hook_rebuild().
+ *
+ * @see drupal_flush_all_caches()
+ * @see hook_rebuild()
+ */
+function hook_cache_flush() {
+  if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') {
+    _update_cache_clear();
+  }
+}
+
+/**
+ * Rebuild data based upon refreshed caches.
+ *
+ * This hook allows your module to rebuild its data based on the latest/current
+ * module data. It runs after hook_cache_flush() and after all module data has
+ * been updated.
+ *
+ * This hook is only invoked after the system has been completely cleared;
+ * i.e., all previously cached data is known to be gone and every API in the
+ * system is known to return current information, so your module can safely rely
+ * on all available data to rebuild its own.
+ *
+ * @see hook_cache_flush()
+ * @see drupal_flush_all_caches()
+ */
+function hook_rebuild() {
+  $themes = \Drupal::service('theme_handler')->listInfo();
+  foreach ($themes as $theme) {
+    _block_rehash($theme->getName());
+  }
+}
+
+/**
+ * Alter the configuration synchronization steps.
+ *
+ * @param array $sync_steps
+ *   A one-dimensional array of \Drupal\Core\Config\ConfigImporter method names
+ *   or callables that are invoked to complete the import, in the order that
+ *   they will be processed. Each callable item defined in $sync_steps should
+ *   either be a global function or a public static method. The callable should
+ *   accept a $context array by reference. For example:
+ *   @code
+ *     function _additional_configuration_step(&$context) {
+ *       // Do stuff.
+ *       // If finished set $context['finished'] = 1.
+ *     }
+ *   @endcode
+ *   For more information on creating batches, see the
+ *   @link batch Batch operations @endlink documentation.
+ *
+ * @see callback_batch_operation()
+ * @see \Drupal\Core\Config\ConfigImporter::initialize()
+ */
+function hook_config_import_steps_alter(&$sync_steps, \Drupal\Core\Config\ConfigImporter $config_importer) {
+  $deletes = $config_importer->getUnprocessedConfiguration('delete');
+  if (isset($deletes['field.storage.node.body'])) {
+    $sync_steps[] = '_additional_configuration_step';
+  }
+}
+
+/**
+ * Alter config typed data definitions.
+ *
+ * For example you can alter the typed data types representing each
+ * configuration schema type to change default labels or form element renderers
+ * used for configuration translation.
+ *
+ * If implementations of this hook add or remove configuration schema a
+ * ConfigSchemaAlterException will be thrown. Keep in mind that there are tools
+ * that may use the configuration schema for static analysis of configuration
+ * files, like the string extractor for the localization system. Such systems
+ * won't work with dynamically defined configuration schemas.
+ *
+ * For adding new data types use configuration schema YAML files instead.
+ *
+ * @param $definitions
+ *   Associative array of configuration type definitions keyed by schema type
+ *   names. The elements are themselves array with information about the type.
+ *
+ * @see \Drupal\Core\Config\TypedConfigManager
+ * @see \Drupal\Core\Config\Schema\ConfigSchemaAlterException
+ */
+function hook_config_schema_info_alter(&$definitions) {
+  // Enhance the text and date type definitions with classes to generate proper
+  // form elements in ConfigTranslationFormBase. Other translatable types will
+  // appear as a one line textfield.
+  $definitions['text']['form_element_class'] = '\Drupal\config_translation\FormElement\Textarea';
+  $definitions['date_format']['form_element_class'] = '\Drupal\config_translation\FormElement\DateFormat';
+}
+
+/**
+ * Alter validation constraint plugin definitions.
+ *
+ * @param array[] $definitions
+ *   The array of validation constraint definitions, keyed by plugin ID.
+ *
+ * @see \Drupal\Core\Validation\ConstraintManager
+ * @see \Drupal\Core\Validation\Annotation\Constraint
+ */
+function hook_validation_constraint_alter(array &$definitions) {
+  $definitions['Null']['class'] = '\Drupal\mymodule\Validator\Constraints\MyClass';
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
+
+/**
+ * @defgroup ajax Ajax API
+ * @{
+ * Overview for Drupal's Ajax API.
+ *
+ * @section sec_overview Overview of Ajax
+ * Ajax is the process of dynamically updating parts of a page's HTML based on
+ * data from the server. When a specified event takes place, a PHP callback is
+ * triggered, which performs server-side logic and may return updated markup or
+ * JavaScript commands to run. After the return, the browser runs the JavaScript
+ * or updates the markup on the fly, with no full page refresh necessary.
+ *
+ * Many different events can trigger Ajax responses, including:
+ * - Clicking a button
+ * - Pressing a key
+ * - Moving the mouse
+ *
+ * @section sec_framework Ajax responses in forms
+ * Forms that use the Drupal Form API (see the
+ * @link form_api Form API topic @endlink for more information about forms) can
+ * trigger AJAX responses. Here is an outline of the steps:
+ * - Add property '#ajax' to a form element in your form array, to trigger an
+ *   Ajax response.
+ * - Write an Ajax callback to process the input and respond.
+ * See sections below for details on these two steps.
+ *
+ * @subsection sub_form Adding Ajax triggers to a form
+ * As an example of adding Ajax triggers to a form, consider editing a date
+ * format, where the user is provided with a sample of the generated date output
+ * as they type. To accomplish this, typing in the text field should trigger an
+ * Ajax response. This is done in the text field form array element
+ * in \Drupal\config_translation\FormElement\DateFormat::getFormElement():
+ * @code
+ * '#ajax' => array(
+ *   'callback' => 'Drupal\config_translation\FormElement\DateFormat::ajaxSample',
+ *   'event' => 'keyup',
+ *   'progress' => array(
+ *     'type' => 'throbber',
+ *     'message' => NULL,
+ *   ),
+ * ),
+ * @endcode
+ *
+ * As you can see from this example, the #ajax property for a form element is
+ * an array. Here are the details of its elements, all of which are optional:
+ * - callback: The callback to invoke to handle the server side of the
+ *   Ajax event. More information on callbacks is below in @ref sub_callback.
+ * - wrapper: The HTML 'id' attribute of the area where the content returned by
+ *   the callback should be placed. Note that callbacks have a choice of
+ *   returning content or JavaScript commands; 'wrapper' is used for content
+ *   returns.
+ * - method: The jQuery method for placing the new content (used with
+ *   'wrapper'). Valid options are 'replaceWith' (default), 'append', 'prepend',
+ *   'before', 'after', or 'html'. See
+ *   http://api.jquery.com/category/manipulation/ for more information on these
+ *   methods.
+ * - effect: The jQuery effect to use when placing the new HTML (used with
+ *   'wrapper'). Valid options are 'none' (default), 'slide', or 'fade'.
+ * - speed: The effect speed to use (used with 'effect' and 'wrapper'). Valid
+ *   options are 'slow' (default), 'fast', or the number of milliseconds the
+ *   effect should run.
+ * - event: The JavaScript event to respond to. This is selected automatically
+ *   for the type of form element; provide a value to override the default.
+ * - prevent: A JavaScript event to prevent when the event is triggered. For
+ *   example, if you use event 'mousedown' on a button, you might want to
+ *   prevent 'click' events from also being triggered.
+ * - progress: An array indicating how to show Ajax processing progress. Can
+ *   contain one or more of these elements:
+ *   - type: Type of indicator: 'throbber' (default) or 'bar'.
+ *   - message: Translated message to display.
+ *   - url: For a bar progress indicator, URL path for determining progress.
+ *   - interval: For a bar progress indicator, how often to update it.
+ * - url: A \Drupal\Core\Url to which to submit the Ajax request. If omitted,
+ *   defaults to either the same URL as the form or link destination is for
+ *   someone with JavaScript disabled, or a slightly modified version (e.g.,
+ *   with a query parameter added, removed, or changed) of that URL if
+ *   necessary to support Drupal's content negotiation. It is recommended to
+ *   omit this key and use Drupal's content negotiation rather than using
+ *   substantially different URLs between Ajax and non-Ajax.
+ *
+ * @subsection sub_callback Setting up a callback to process Ajax
+ * Once you have set up your form to trigger an Ajax response (see @ref sub_form
+ * above), you need to write some PHP code to process the response. If you use
+ * 'path' in your Ajax set-up, your route controller will be triggered with only
+ * the information you provide in the URL. If you use 'callback', your callback
+ * method is a function, which will receive the $form and $form_state from the
+ * triggering form. You can use $form_state to get information about the
+ * data the user has entered into the form. For instance, in the above example
+ * for the date format preview,
+ * \Drupal\config_translation\FormElement\DateFormat\ajaxSample() does this to
+ * get the format string entered by the user:
+ * @code
+ * $format_value = \Drupal\Component\Utility\NestedArray::getValue(
+ *   $form_state->getValues(),
+ *   $form_state->getTriggeringElement()['#array_parents']);
+ * @endcode
+ *
+ * Once you have processed the input, you have your choice of returning HTML
+ * markup or a set of Ajax commands. If you choose to return HTML markup, you
+ * can return it as a string or a renderable array, and it will be placed in
+ * the defined 'wrapper' element (see documentation above in @ref sub_form).
+ * In addition, any messages returned by
+ * \Drupal\Core\Messenger\Messenger::all(), themed as in
+ * status-messages.html.twig, will be prepended.
+ *
+ * To return commands, you need to set up an object of class
+ * \Drupal\Core\Ajax\AjaxResponse, and then use its addCommand() method to add
+ * individual commands to it. In the date format preview example, the format
+ * output is calculated, and then it is returned as replacement markup for a div
+ * like this:
+ * @code
+ * $response = new AjaxResponse();
+ * $response->addCommand(new ReplaceCommand(
+ *   '#edit-date-format-suffix',
+ *   '<small id="edit-date-format-suffix">' . $format . '</small>'));
+ * return $response;
+ * @endcode
+ *
+ * The individual commands that you can return implement interface
+ * \Drupal\Core\Ajax\CommandInterface. Available commands provide the ability
+ * to pop up alerts, manipulate text and markup in various ways, redirect
+ * to a new URL, and the generic \Drupal\Core\Ajax\InvokeCommand, which
+ * invokes an arbitrary jQuery command.
+ *
+ * As noted above, status messages are prepended automatically if you use the
+ * 'wrapper' method and return HTML markup. This is not the case if you return
+ * commands, but if you would like to show status messages, you can add
+ * @code
+ * array('#type' => 'status_messages')
+ * @endcode
+ * to a render array, use drupal_render() to render it, and add a command to
+ * place the messages in an appropriate location.
+ *
+ * @section sec_other Other methods for triggering Ajax
+ * Here are some additional methods you can use to trigger Ajax responses in
+ * Drupal:
+ * - Add class 'use-ajax' to a link. The link will be loaded using an Ajax
+ *   call. When using this method, the href of the link can contain '/nojs/' as
+ *   part of the path. When the Ajax JavaScript processes the page, it will
+ *   convert this to '/ajax/'. The server is then able to easily tell if this
+ *   request was made through an actual Ajax request or in a degraded state, and
+ *   respond appropriately.
+ * - Add class 'use-ajax-submit' to a submit button in a form. The form will
+ *   then be submitted via Ajax to the path specified in the #action.  Like the
+ *   ajax-submit class on links, this path will have '/nojs/' replaced with
+ *   '/ajax/' so that the submit handler can tell if the form was submitted in a
+ *   degraded state or not.
+ * - Add property '#autocomplete_route_name' to a text field in a form. The
+ *   route controller for this route must return an array of options for
+ *   autocomplete, as a \Symfony\Component\HttpFoundation\JsonResponse object.
+ *   See the @link menu Routing topic @endlink for more information about
+ *   routing.
+ */
+
+/**
+ * @} End of "defgroup ajax".
+ */
+
+/**
+ * @defgroup service_tag Service Tags
+ * @{
+ * Service tags overview
+ *
+ * Some services have tags, which are defined in the service definition. Tags
+ * are used to define a group of related services, or to specify some aspect of
+ * how the service behaves. Typically, if you tag a service, your service class
+ * must also implement a corresponding interface. Some common examples:
+ * - access_check: Indicates a route access checking service; see the
+ *   @link menu Menu and routing system topic @endlink for more information.
+ * - cache.bin: Indicates a cache bin service; see the
+ *   @link cache Cache topic @endlink for more information.
+ * - event_subscriber: Indicates an event subscriber service. Event subscribers
+ *   can be used for dynamic routing and route altering; see the
+ *   @link menu Menu and routing system topic @endlink for more information.
+ *   They can also be used for other purposes; see
+ *   http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html
+ *   for more information.
+ * - needs_destruction: Indicates that a destruct() method needs to be called
+ *   at the end of a request to finalize operations, if this service was
+ *   instantiated. Services should implement \Drupal\Core\DestructableInterface
+ *   in this case.
+ * - context_provider: Indicates a block context provider, used for example
+ *   by block conditions. It has to implement
+ *   \Drupal\Core\Plugin\Context\ContextProviderInterface.
+ * - http_client_middleware: Indicates that the service provides a guzzle
+ *   middleware, see
+ *   https://guzzle.readthedocs.org/en/latest/handlers-and-middleware.html for
+ *   more information.
+ *
+ * Creating a tag for a service does not do anything on its own, but tags
+ * can be discovered or queried in a compiler pass when the container is built,
+ * and a corresponding action can be taken. See
+ * \Drupal\Core\Render\MainContent\MainContentRenderersPass for an example of
+ * finding tagged services.
+ *
+ * See @link container Services and Dependency Injection Container @endlink for
+ * information on services and the dependency injection container.
+ *
+ * @}
+ */
+
+/**
+ * @defgroup events Events
+ * @{
+ * Overview of event dispatch and subscribing
+ *
+ * @section sec_intro Introduction and terminology
+ * Events are part of the Symfony framework: they allow for different components
+ * of the system to interact and communicate with each other. Each event has a
+ * unique string name. One system component dispatches the event at an
+ * appropriate time; many events are dispatched by Drupal core and the Symfony
+ * framework in every request. Other system components can register as event
+ * subscribers; when an event is dispatched, a method is called on each
+ * registered subscriber, allowing each one to react. For more on the general
+ * concept of events, see
+ * http://symfony.com/doc/current/components/event_dispatcher/introduction.html
+ *
+ * @section sec_dispatch Dispatching events
+ * To dispatch an event, call the
+ * \Symfony\Component\EventDispatcher\EventDispatcherInterface::dispatch()
+ * method on the 'event_dispatcher' service (see the
+ * @link container Services topic @endlink for more information about how to
+ * interact with services). The first argument is the unique event name, which
+ * you should normally define as a constant in a separate static class (see
+ * \Symfony\Component\HttpKernel\KernelEvents and
+ * \Drupal\Core\Config\ConfigEvents for examples). The second argument is a
+ * \Symfony\Component\EventDispatcher\Event object; normally you will need to
+ * extend this class, so that your event class can provide data to the event
+ * subscribers.
+ *
+ * @section sec_subscribe Registering event subscribers
+ * Here are the steps to register an event subscriber:
+ * - Define a service in your module, tagged with 'event_subscriber' (see the
+ *   @link container Services topic @endlink for instructions).
+ * - Define a class for your subscriber service that implements
+ *   \Symfony\Component\EventDispatcher\EventSubscriberInterface
+ * - In your class, the getSubscribedEvents method returns a list of the events
+ *   this class is subscribed to, and which methods on the class should be
+ *   called for each one. Example:
+ *   @code
+ *   public static function getSubscribedEvents() {
+ *     // Subscribe to kernel terminate with priority 100.
+ *     $events[KernelEvents::TERMINATE][] = array('onTerminate', 100);
+ *     // Subscribe to kernel request with default priority of 0.
+ *     $events[KernelEvents::REQUEST][] = array('onRequest');
+ *     return $events;
+ *   }
+ *   @endcode
+ * - Write the methods that respond to the events; each one receives the
+ *   event object provided in the dispatch as its one argument. In the above
+ *   example, you would need to write onTerminate() and onRequest() methods.
+ *
+ * Note that in your getSubscribedEvents() method, you can optionally set the
+ * priority of your event subscriber (see terminate example above). Event
+ * subscribers with higher priority numbers get executed first; the default
+ * priority is zero. If two event subscribers for the same event have the same
+ * priority, the one defined in a module with a lower module weight will fire
+ * first. Subscribers defined in the same services file are fired in
+ * definition order. If order matters defining a priority is strongly advised
+ * instead of relying on these two tie breaker rules as they might change in a
+ * minor release.
+ * @}
+ */
+
+/**
+ * @defgroup session Sessions
+ * @{
+ * Store and retrieve data associated with a user's browsing session.
+ *
+ * @section sec_intro Overview
+ * The Drupal session management subsystem is built on top of the Symfony
+ * session component. It is optimized in order to minimize the impact of
+ * anonymous sessions on caching proxies. A session is only started if necessary
+ * and the session cookie is removed from the browser as soon as the session
+ * has no data. For this reason it is important for contributed and custom
+ * code to remove session data if it is not used anymore.
+ *
+ * @section sec_usage Usage
+ * Session data is accessed via the
+ * \Symfony\Component\HttpFoundation\Request::getSession()
+ * method, which returns an instance of
+ * \Symfony\Component\HttpFoundation\Session\SessionInterface. The most
+ * important methods on SessionInterface are set(), get(), and remove().
+ *
+ * The following code fragment shows the implementation of a counter controller
+ * relying on the session:
+ * @code
+ * public function counter(Request $request) {
+ *   $session = $request->getSession();
+ *   $count = $session->get('mymodule.counter', 0) + 1;
+ *   $session->set('mymodule.counter', $count);
+ *
+ *   return [
+ *     '#markup' => $this->t('Page Views: @count', ['@count' => $count]),
+ *     '#cache' => [
+ *       'max-age' => 0,
+ *     ],
+ *   ];
+ * }
+ *
+ * public function reset(Request $request) {
+ *   $session = $request->getSession();
+ *   $session->remove('mymodule.counter');
+ * }
+ * @endcode
+ *
+ * It is important to keep the amount of data stored inside the session to a
+ * minimum, as the complete session is loaded on every request. Also third
+ * party session storage backends do not necessarily allow objects of unlimited
+ * size. If it is necessary to collect a non-trivial amount of data specific to
+ * a user's session, use the Key/Value store to save the serialized data and
+ * only store the key to the entry in the session.
+ *
+ * @section sec_reserved Reserved attributes and namespacing
+ * Contributed modules relying on the session are encouraged to namespace
+ * session attributes by prefixing them with their project name or an
+ * abbreviation thereof.
+ *
+ * Some attributes are reserved for Drupal core and must not be accessed from
+ * within contributed and custom code. Reserved attributes include:
+ * - uid: The user ID for an authenticated user. The value of this attribute
+ *   cannot be modified.
+ *
+ * @section sec_custom_session_bags Custom session bags
+ * Modules can register custom session bags in order to provide type safe
+ * interfaces on module specific session data. A session bag must implement
+ * \Symfony\Component\HttpFoundation\Session\SessionBagInterface. Custom session
+ * bags are registered using a service entry tagged with the session_bag service
+ * tag. Custom session bags can be accessed through the session retrieved from
+ * the request object.
+ *
+ * Example service definition:
+ * @code
+ * session_test.session_bag:
+ *   class: Drupal\session_test\Session\TestSessionBag
+ *   tags:
+ *     - { name: session_bag }
+ * @endcode
+ *
+ * Example of accessing a custom session bag:
+ * @code
+ * $bag = $request->getSession()->getBag(TestSessionBag::BAG_NAME);
+ * $bag->setFlag();
+ * @endcode
+ * Session data must be deleted from custom session bags as soon as it is no
+ * longer needed (see @ref sec_intro above).
+ * @}
+ */

+ 1036 - 0
web/core/core.libraries.yml

@@ -0,0 +1,1036 @@
+# All libraries are defined in alphabetical order.
+
+backbone:
+  remote: https://github.com/jashkenas/backbone
+  version: "1.4.0"
+  license:
+    name: MIT
+    url: https://github.com/jashkenas/backbone/blob/1.4.0/LICENSE
+    gpl-compatible: true
+  js:
+    assets/vendor/backbone/backbone-min.js: { weight: -19, minified: true }
+  dependencies:
+    - core/underscore
+
+classList:
+  remote: https://github.com/eligrey/classList.js
+  version: "2014-12-13"
+  license:
+    name: Public Domain
+    url: https://github.com/eligrey/classList.js/blob/2014-12-13/LICENSE.md
+    gpl-compatible: true
+  js:
+    assets/vendor/classList/classList.min.js: { weight: -21, browsers: { IE: 'lte IE 9', '!IE': false }, minified: true }
+  deprecated: The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the the native browser implementation instead. See https://www.drupal.org/node/3089511
+
+ckeditor:
+  remote: https://github.com/ckeditor/ckeditor-dev
+  version: "4.14.0"
+  license:
+    name: GNU-GPL-2.0-or-later
+    url: https://github.com/ckeditor/ckeditor4/blob/4.14.0/LICENSE.md
+    gpl-compatible: true
+  js:
+    assets/vendor/ckeditor/ckeditor.js: { preprocess: false, minified: true }
+
+domready:
+  remote: https://github.com/ded/domready
+  version: "1.0.8"
+  license:
+    name: MIT
+    url: https://github.com/ded/domready/blob/v1.0.8/LICENSE
+    gpl-compatible: true
+  js:
+    assets/vendor/domready/ready.min.js: { weight: -21, minified: true }
+  deprecated: The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3086669
+
+drupal:
+  version: VERSION
+  js:
+    misc/drupal.js: { weight: -18 }
+    misc/drupal.init.js: { weight: -17 }
+  dependencies:
+    - core/drupalSettings
+  drupalSettings:
+    suppressDeprecationErrors: true
+
+drupalSettings:
+  version: VERSION
+  js:
+    # Need to specify a negative weight like drupal.js until
+    # https://www.drupal.org/node/1945262 is resolved.
+    misc/drupalSettingsLoader.js: { weight: -18 }
+  drupalSettings:
+    # These placeholder values will be set by system_js_settings_alter().
+    path:
+      baseUrl: null
+      scriptPath: null
+      pathPrefix: null
+      currentPath: null
+      currentPathIsAdmin: null
+      isFront: null
+      currentLanguage: null
+    pluralDelimiter: null
+
+drupal.active-link:
+  version: VERSION
+  js:
+    misc/active-link.js: {}
+  dependencies:
+    - core/drupal
+    - core/drupalSettings
+    - core/classList
+
+drupal.ajax:
+  version: VERSION
+  js:
+    misc/ajax.js: {}
+  drupalSettings:
+    # These placeholder values will be set by system_js_settings_alter().
+    ajaxPageState:
+      libraries: null
+      theme: null
+      theme_token: null
+    ajaxTrustedUrl: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
+    - core/drupal.progress
+    - core/jquery.once
+
+drupal.announce:
+  version: VERSION
+  js:
+    misc/announce.js: {}
+  dependencies:
+    - core/drupal
+    - core/drupal.debounce
+
+drupal.autocomplete:
+  version: VERSION
+  js:
+    misc/autocomplete.js: { weight: -1 }
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
+    - core/drupal.ajax
+    - core/jquery.ui.autocomplete
+
+drupal.batch:
+  version: VERSION
+  js:
+    misc/batch.js: { cache: false }
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
+    - core/drupal.ajax
+    - core/drupal.progress
+    - core/jquery.once
+
+drupal.checkbox:
+  version: VERSION
+  js:
+    misc/checkbox.js: {}
+  dependencies:
+    - core/drupal
+
+drupal.collapse:
+  version: VERSION
+  js:
+    misc/details-aria.js: {}
+    misc/collapse.js: {}
+  dependencies:
+    - core/jquery
+    - core/modernizr
+    - core/drupal
+    - core/drupal.form
+    - core/jquery.once
+
+drupal.date:
+  version: VERSION
+  js:
+    misc/date.js: {}
+  dependencies:
+    - core/drupal
+    - core/modernizr
+    - core/jquery.once
+    - core/jquery.ui.datepicker
+
+drupal.debounce:
+  version: VERSION
+  js:
+    misc/debounce.js: {}
+  dependencies:
+    # @todo Remove Drupal dependency.
+    - core/drupal
+
+drupal.dialog:
+  version: VERSION
+  js:
+    misc/dialog/dialog.js: {}
+    misc/dialog/dialog.position.js: {}
+    misc/dialog/dialog.jquery-ui.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
+    - core/drupal.debounce
+    - core/drupal.displace
+    - core/jquery.ui.dialog
+
+drupal.dialog.ajax:
+  version: VERSION
+  js:
+    misc/dialog/dialog.ajax.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
+    - core/drupal.ajax
+    - core/drupal.dialog
+
+drupal.displace:
+  version: VERSION
+  js:
+    misc/displace.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupal.debounce
+
+drupal.dropbutton:
+  version: VERSION
+  js:
+    misc/dropbutton/dropbutton.js: {}
+  css:
+    component:
+      misc/dropbutton/dropbutton.css: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
+    - core/jquery.once
+
+drupal.entity-form:
+  version: VERSION
+  js:
+    misc/entity-form.js: {}
+  dependencies:
+    - core/drupal.form
+
+drupal.form:
+  version: VERSION
+  js:
+    misc/form.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupal.debounce
+    - core/jquery.once
+
+drupal.machine-name:
+  version: VERSION
+  js:
+    misc/machine-name.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - core/drupal
+    - core/drupalSettings
+    - core/drupal.form
+
+drupal.message:
+  version: VERSION
+  js:
+    misc/message.js: {}
+  dependencies:
+    - core/drupal
+    - core/drupal.announce
+
+drupal.object.assign:
+  version: VERSION
+  js:
+    misc/polyfills/object.assign.js: { weight: -20 }
+
+drupal.progress:
+  version: VERSION
+  js:
+    misc/progress.js: {}
+  dependencies:
+    - core/drupal
+    - core/jquery
+    - core/drupalSettings
+
+drupal.states:
+  version: VERSION
+  js:
+    misc/states.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
+    - core/jquery.once
+
+drupal.tabbingmanager:
+  version: VERSION
+  js:
+    misc/tabbingmanager.js: {}
+  dependencies:
+    - core/jquery
+    # Supplies the ':tabbable' pseudo selector.
+    - core/jquery.ui
+    - core/drupal
+
+drupal.tabledrag:
+  version: VERSION
+  js:
+    misc/tabledrag.js: { weight: -1 }
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
+    - core/jquery.once
+
+drupal.tableheader:
+  version: VERSION
+  js:
+    misc/tableheader.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupalSettings
+    - core/jquery.once
+    - core/drupal.displace
+
+drupal.tableresponsive:
+  version: VERSION
+  js:
+    misc/tableresponsive.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/jquery.once
+
+drupal.tableselect:
+  version: VERSION
+  js:
+    misc/tableselect.js: {}
+  dependencies:
+    - core/drupal
+    - core/drupal.checkbox
+    - core/jquery
+    - core/jquery.once
+
+drupal.timezone:
+  version: VERSION
+  js:
+    misc/timezone.js: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - core/drupal
+
+drupal.vertical-tabs:
+  version: VERSION
+  js:
+    # Load before core/drupal.collapse.
+    misc/vertical-tabs.js: { weight: -1 }
+  css:
+    component:
+      misc/vertical-tabs.css: {}
+  dependencies:
+    - core/jquery
+    - core/jquery.once
+    - core/drupal
+    - core/drupalSettings
+    - core/drupal.form
+    - core/matchmedia
+
+html5shiv:
+  # Block the page from being loaded until html5shiv is initialized.
+  header: true
+  remote: https://github.com/aFarkas/html5shiv
+  version: "3.7.3"
+  license:
+    name: GNU-GPL-2.0-or-later
+    url: http://www.gnu.org/licenses/gpl-2.0.html
+    gpl-compatible: true
+  js:
+    assets/vendor/html5shiv/html5shiv.min.js: { weight: -22, browsers: { IE: 'lte IE 8', '!IE': false }, minified: true }
+  deprecated: The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3086383
+
+jquery:
+  remote: https://github.com/jquery/jquery
+  version: "3.5.1"
+  license:
+    name: MIT
+    url: https://github.com/jquery/jquery/blob/3.5.1/LICENSE.txt
+    gpl-compatible: true
+  js:
+    assets/vendor/jquery/jquery.min.js: { minified: true, weight: -20 }
+
+jquery.cookie:
+  version: VERSION
+  js:
+    misc/jquery.cookie.shim.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/js-cookie
+    - core/drupal.object.assign
+
+jquery.farbtastic:
+  remote: https://github.com/mattfarina/farbtastic
+  # @todo Ping @robloach or @mattfarina to retroactively create this release.
+  version: "1.2"
+  license:
+    name: GNU-GPL-2.0-or-later
+    url: https://github.com/mattfarina/farbtastic/blob/master/LICENSE.txt
+    gpl-compatible: true
+  js:
+    assets/vendor/farbtastic/farbtastic.js: { minified: true }
+  css:
+    component:
+      assets/vendor/farbtastic/farbtastic.css: {}
+  dependencies:
+    - core/jquery
+
+jquery.form:
+  remote: https://github.com/jquery-form/form
+  version: "4.22"
+  license:
+    name: GNU-GPL-2.0-or-later
+    url: https://raw.githubusercontent.com/jquery-form/form/master/LICENSE-LGPLv3
+    gpl-compatible: true
+  js:
+    assets/vendor/jquery-form/jquery.form.min.js: { minified: true }
+  dependencies:
+    - core/jquery
+
+jquery.joyride:
+  remote: https://github.com/zurb/joyride
+  # We rely on a fix (commit c2b3866) that occurred after 2.1.0 was released.
+  # @see https://www.drupal.org/node/2898808.
+  # @todo Update to 2.1.1 or later when that's released.
+  # Version is set at 2.1.0.1 allowing version_compare to consider this lower
+  # than 2.1.1 however greater than 2.1.0
+  version: "2.1.0.1"
+  license:
+    name: MIT
+    url: https://github.com/zurb/joyride/blob/v2.1.0/README.markdown
+    gpl-compatible: true
+  js:
+    assets/vendor/jquery-joyride/jquery.joyride-2.1.js: { }
+  dependencies:
+    - core/jquery
+
+jquery.once:
+  remote: https://github.com/RobLoach/jquery-once
+  version: "2.2.3"
+  license:
+    name: GNU-GPL-2.0-or-later
+    url: https://github.com/RobLoach/jquery-once/blob/2.2.3/LICENSE.md
+    gpl-compatible: true
+  js:
+    assets/vendor/jquery-once/jquery.once.min.js: { weight: -19, minified: true }
+  dependencies:
+    - core/jquery
+
+jquery.ui:
+  remote: https://github.com/jquery/jquery-ui
+  version: &jquery_ui_version 1.12.1
+  license: &jquery_ui_license
+    name: Public Domain
+    url: https://github.com/jquery/jquery-ui/blob/1.12.1/LICENSE.txt
+    gpl-compatible: true
+  js:
+    assets/vendor/jquery.ui/ui/data-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/disable-selection-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/form-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/labels-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/jquery-1-7-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/scroll-parent-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/tabbable-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/unique-id-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/version-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/escape-selector-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/focusable-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/ie-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/keycode-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/plugin-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/safe-active-element-min.js: { weight: -11, minified: true }
+    assets/vendor/jquery.ui/ui/safe-blur-min.js: { weight: -11, minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/core.css: {}
+    theme:
+      assets/vendor/jquery.ui/themes/base/theme.css: {}
+  dependencies:
+    - core/jquery
+
+jquery.ui.accordion:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/accordion-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/accordion.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.widget
+  deprecated: &jquery_ui_unused_deprecated The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3067969
+
+jquery.ui.autocomplete:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/autocomplete-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/autocomplete.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.widget
+    - core/jquery.ui.position
+    - core/jquery.ui.menu
+
+jquery.ui.button:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/button-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/button.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.widget
+    - core/jquery.ui.checkboxradio
+    - core/jquery.ui.controlgroup
+
+jquery.ui.checkboxradio:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/form-reset-mixin-min.js: { minified: true }
+    assets/vendor/jquery.ui/ui/widgets/checkboxradio-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/checkboxradio.css: {}
+      assets/vendor/jquery.ui/themes/base/button.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.widget
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.controlgroup:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/controlgroup-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/controlgroup.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.widget
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.datepicker:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/datepicker-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/datepicker.css: {}
+  dependencies:
+    - core/jquery.ui
+  deprecated: The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3081864
+
+jquery.ui.dialog:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/dialog-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/dialog.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.widget
+    - core/jquery.ui.button
+    - core/jquery.ui.draggable
+    - core/jquery.ui.mouse
+    - core/jquery.ui.position
+    - core/jquery.ui.resizable
+
+jquery.ui.draggable:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/draggable-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.mouse
+    - core/jquery.ui.widget
+
+jquery.ui.droppable:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/droppable-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.widget
+    - core/jquery.ui.mouse
+    - core/jquery.ui.draggable
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.core:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effect-min.js: { weight: -9, minified: true }
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.blind:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-blind-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.bounce:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-bounce-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.clip:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-clip-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.drop:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-drop-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.explode:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-explode-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.fade:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-fade-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.fold:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-fold-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.highlight:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-highlight-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.puff:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-puff-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+    - core/jquery.ui.effects.scale
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.pulsate:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-pulsate-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.scale:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-scale-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+    - core/jquery.ui.effects.size
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.shake:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-shake-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.size:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-size-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.slide:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-slide-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.effects.transfer:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/effects/effect-transfer-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.effects.core
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.menu:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/menu-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/menu.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.position
+    - core/jquery.ui.widget
+
+jquery.ui.mouse:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/ie-min.js: { minified: true }
+    assets/vendor/jquery.ui/ui/widgets/mouse-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui.widget
+
+jquery.ui.position:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/position-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui
+
+jquery.ui.progressbar:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/progressbar-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/progressbar.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.widget
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.resizable:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/resizable-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/resizable.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.widget
+    - core/jquery.ui.mouse
+
+jquery.ui.selectable:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/selectable-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/selectable.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.mouse
+    - core/jquery.ui.widget
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.selectmenu:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/form-reset-mixin-min.js: { minified: true }
+    assets/vendor/jquery.ui/ui/widgets/selectmenu-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/selectmenu.css: {}
+      assets/vendor/jquery.ui/themes/base/button.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.menu
+    - core/jquery.ui.position
+    - core/jquery.ui.widget
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.slider:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/slider-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/slider.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.mouse
+    - core/jquery.ui.widget
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.sortable:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/sortable-min.js: { minified: true }
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.mouse
+    - core/jquery.ui.widget
+  deprecated: The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3084730
+
+jquery.ui.spinner:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/spinner-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/spinner.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.widget
+    - core/jquery.ui.button
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.tabs:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/tabs-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/tabs.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.widget
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.tooltip:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widgets/tooltip-min.js: { minified: true }
+  css:
+    component:
+      assets/vendor/jquery.ui/themes/base/tooltip.css: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.widget
+    - core/jquery.ui.position
+  deprecated: *jquery_ui_unused_deprecated
+
+jquery.ui.touch-punch:
+  remote: https://github.com/furf/jquery-ui-touch-punch
+  version: "0.2.3"
+  license:
+    name: GNU-GPL-2.0-or-later
+    url: https://github.com/furf/jquery-ui-touch-punch
+    gpl-compatible: true
+  js:
+    assets/vendor/jquery-ui-touch-punch/jquery.ui.touch-punch.js: {}
+  dependencies:
+    - core/jquery.ui
+    - core/jquery.ui.mouse
+    - core/jquery.ui.widget
+  deprecated: The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3084730
+
+jquery.ui.widget:
+  version: *jquery_ui_version
+  license: *jquery_ui_license
+  js:
+    assets/vendor/jquery.ui/ui/widget-min.js: { weight: -10, minified: true }
+  dependencies:
+    - core/jquery.ui
+
+matchmedia:
+  remote: https://github.com/paulirish/matchMedia.js
+  version: &matchmedia_version 0.2.0
+  license: &matchmedia_license
+    name: MIT
+    url: https://github.com/paulirish/matchMedia.js/blob/0.2.0/LICENSE.txt
+    gpl-compatible: true
+  js:
+    assets/vendor/matchMedia/matchMedia.min.js: { minified: true }
+  deprecated: &matchmedia_deprecated The "%library_id%" asset library is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. See https://www.drupal.org/node/3086653
+
+matchmedia.addListener:
+  version: *matchmedia_version
+  license: *matchmedia_license
+  js:
+    assets/vendor/matchMedia/matchMedia.addListener.min.js: { minified: true }
+  dependencies:
+    - core/matchmedia
+  deprecated: *matchmedia_deprecated
+
+modernizr:
+  # Block the page from being loaded until Modernizr is initialized.
+  header: true
+  remote: https://github.com/Modernizr/Modernizr
+  license:
+    name: MIT
+    url: http://modernizr.com/license/
+    gpl-compatible: true
+  version: "v3.3.1"
+  js:
+    assets/vendor/modernizr/modernizr.min.js: { preprocess: 0, weight: -21, minified: true }
+
+normalize:
+  remote: https://github.com/necolas/normalize.css
+  version: "3.0.3"
+  license:
+    name: MIT
+    url: https://github.com/necolas/normalize.css/blob/master/LICENSE.md
+    gpl-compatible: true
+  css:
+    base:
+      assets/vendor/normalize-css/normalize.css: { weight: -20 }
+      misc/normalize-fixes.css: { weight: -19 }
+
+picturefill:
+  remote: https://github.com/scottjehl/picturefill
+  version: "3.0.3"
+  license:
+    name: MIT
+    url: https://github.com/scottjehl/picturefill/blob/3.0.3/LICENSE
+    gpl-compatible: true
+  js:
+    assets/vendor/picturefill/picturefill.min.js: { weight: -10, minified: true }
+  dependencies:
+    - core/matchmedia
+
+popperjs:
+  version: "1.16.0"
+  license:
+    name: MIT
+    url: https://github.com/popperjs/popper.js/blob/v1.16.0/LICENSE.md
+    gpl-compatible: true
+  js:
+    assets/vendor/popperjs/popper.min.js: { minified: true }
+
+sortable:
+  remote: https://github.com/SortableJS/Sortable
+  version: "1.10.2"
+  license:
+    name: MIT
+    url: https://github.com/SortableJS/Sortable/tree/1.10.2#mit-license
+    gpl-compatible: true
+  js:
+    assets/vendor/sortable/Sortable.min.js: { minified: true }
+
+underscore:
+  remote: https://github.com/jashkenas/underscore
+  version: "1.9.1"
+  license:
+    name: MIT
+    url: https://github.com/jashkenas/underscore/blob/1.9.1/LICENSE
+    gpl-compatible: true
+  js:
+    assets/vendor/underscore/underscore-min.js: { weight: -20, minified: true }
+
+drupal.dialog.off_canvas:
+  version: VERSION
+  js:
+    misc/dialog/off-canvas.js: {}
+  css:
+    base:
+      misc/dialog/off-canvas.reset.css: {}
+      misc/dialog/off-canvas.base.css: {}
+      misc/dialog/off-canvas.css: {}
+      # Add group setting to make sure this CSS load before any jQuery UI Dialog
+      # CSS.
+      misc/dialog/off-canvas.theme.css: { group: 200 }
+    component:
+      misc/dialog/off-canvas.motion.css: {}
+      misc/dialog/off-canvas.button.css: {}
+      misc/dialog/off-canvas.form.css: {}
+      misc/dialog/off-canvas.table.css: {}
+      misc/dialog/off-canvas.details.css: {}
+      misc/dialog/off-canvas.tabledrag.css: {}
+      misc/dialog/off-canvas.dropbutton.css: {}
+      misc/dialog/off-canvas.layout.css: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupal.ajax
+    - core/drupal.announce
+    - core/drupal.dialog
+    - core/drupal.dialog.ajax
+
+js-cookie:
+  remote: https://github.com/js-cookie/js-cookie
+  version: "v3.0.0-rc0"
+  license:
+    name: MIT
+    url: https://github.com/js-cookie/js-cookie/blob/v3.0.0-rc.0/LICENSE
+    gpl-compatible: true
+  js:
+    assets/vendor/js-cookie/js.cookie.min.js: {}

File diff suppressed because it is too large
+ 195 - 0
web/core/core.link_relation_types.yml


+ 1766 - 0
web/core/core.services.yml

@@ -0,0 +1,1766 @@
+parameters:
+  session.storage.options:
+    gc_probability: 1
+    gc_divisor: 100
+    gc_maxlifetime: 200000
+    cookie_lifetime: 2000000
+  twig.config:
+    debug: false
+    auto_reload: null
+    cache: true
+  renderer.config:
+    required_cache_contexts: ['languages:language_interface', 'theme', 'user.permissions']
+    auto_placeholder_conditions:
+      max-age: 0
+      contexts: ['session', 'user']
+      tags: []
+  factory.keyvalue:
+    default: keyvalue.database
+  http.response.debug_cacheability_headers: false
+  factory.keyvalue.expirable:
+    default: keyvalue.expirable.database
+  filter_protocols:
+    - http
+    - https
+    - ftp
+    - news
+    - nntp
+    - tel
+    - telnet
+    - mailto
+    - irc
+    - ssh
+    - sftp
+    - webcal
+    - rtsp
+  cors.config:
+    enabled: false
+    allowedHeaders: []
+    allowedMethods: []
+    allowedOrigins: ['*']
+    exposedHeaders: false
+    maxAge: false
+    supportsCredentials: false
+  tempstore.expire: 604800
+services:
+  # Simple cache contexts, directly derived from the request context.
+  cache_context.ip:
+    class: Drupal\Core\Cache\Context\IpCacheContext
+    arguments: ['@request_stack']
+    tags:
+      - { name: cache.context }
+  cache_context.protocol_version:
+    class: Drupal\Core\Cache\Context\ProtocolVersionCacheContext
+    arguments: ['@request_stack']
+    tags:
+      - { name: cache.context }
+  cache_context.headers:
+    class: Drupal\Core\Cache\Context\HeadersCacheContext
+    arguments: ['@request_stack']
+    tags:
+      - { name: cache.context }
+  cache_context.cookies:
+    class: Drupal\Core\Cache\Context\CookiesCacheContext
+    arguments: ['@request_stack']
+    tags:
+      - { name: cache.context }
+  cache_context.session:
+    class: Drupal\Core\Cache\Context\SessionCacheContext
+    arguments: ['@request_stack']
+    tags:
+      - { name: cache.context}
+  cache_context.session.exists:
+    class: Drupal\Core\Cache\Context\SessionExistsCacheContext
+    arguments: ['@session_configuration', '@request_stack']
+    tags:
+      - { name: cache.context}
+  cache_context.request_format:
+    class: Drupal\Core\Cache\Context\RequestFormatCacheContext
+    arguments: ['@request_stack']
+    tags:
+      - { name: cache.context }
+  cache_context.url:
+    class: Drupal\Core\Cache\Context\UrlCacheContext
+    arguments: ['@request_stack']
+    tags:
+      - { name: cache.context }
+  cache_context.url.site:
+    class: Drupal\Core\Cache\Context\SiteCacheContext
+    arguments: ['@request_stack']
+    tags:
+      - { name: cache.context }
+  cache_context.url.path:
+    class: Drupal\Core\Cache\Context\PathCacheContext
+    arguments: ['@request_stack']
+    tags:
+      - { name: cache.context }
+  cache_context.url.path.parent:
+    class: Drupal\Core\Cache\Context\PathParentCacheContext
+    arguments: ['@request_stack']
+    tags:
+      - { name: cache.context }
+  cache_context.url.path.is_front:
+    class: Drupal\Core\Cache\Context\IsFrontPathCacheContext
+    arguments: ['@path.matcher']
+    tags:
+      - { name: cache.context }
+  cache_context.url.query_args:
+    class: Drupal\Core\Cache\Context\QueryArgsCacheContext
+    arguments: ['@request_stack']
+    tags:
+      - { name: cache.context }
+  cache_context.url.query_args.pagers:
+    class: Drupal\Core\Cache\Context\PagersCacheContext
+    arguments: ['@pager.parameters']
+    tags:
+      - { name: cache.context }
+
+  # Complex cache contexts, that depend on the routing system.
+  cache_context.route:
+    class: Drupal\Core\Cache\Context\RouteCacheContext
+    arguments: ['@current_route_match']
+    tags:
+      - { name: cache.context }
+  cache_context.route.name:
+    class: Drupal\Core\Cache\Context\RouteNameCacheContext
+    arguments: ['@current_route_match']
+    tags:
+      - { name: cache.context }
+  cache_context.route.menu_active_trails:
+    class: Drupal\Core\Cache\Context\MenuActiveTrailsCacheContext
+    calls:
+      - [setContainer, ['@service_container']]
+    tags:
+      - { name: cache.context }
+
+  # Complex cache contexts, that may be calculated from a combination of
+  # multiple aspects of the request context plus additional logic. Hence they
+  # are their own roots.
+  cache_context.user:
+    class: Drupal\Core\Cache\Context\UserCacheContext
+    arguments: ['@current_user']
+    tags:
+      - { name: cache.context}
+  cache_context.user.permissions:
+    class: Drupal\Core\Cache\Context\AccountPermissionsCacheContext
+    arguments: ['@current_user', '@user_permissions_hash_generator']
+    tags:
+      - { name: cache.context}
+  cache_context.user.roles:
+    class: Drupal\Core\Cache\Context\UserRolesCacheContext
+    arguments: ['@current_user']
+    tags:
+      - { name: cache.context}
+  cache_context.user.is_super_user:
+    class: Drupal\Core\Cache\Context\IsSuperUserCacheContext
+    arguments: ['@current_user']
+    tags:
+      - { name: cache.context}
+  cache_context.languages:
+    class: Drupal\Core\Cache\Context\LanguagesCacheContext
+    arguments: ['@language_manager']
+    tags:
+      - { name: cache.context}
+  cache_context.theme:
+    class: Drupal\Core\Cache\Context\ThemeCacheContext
+    arguments: ['@theme.manager']
+    tags:
+      - { name: cache.context}
+  cache_context.timezone:
+    class: Drupal\Core\Cache\Context\TimeZoneCacheContext
+    tags:
+      - { name: cache.context}
+
+  cache_factory:
+    class: Drupal\Core\Cache\CacheFactory
+    arguments: ['@settings', '%cache_default_bin_backends%']
+    calls:
+      - [setContainer, ['@service_container']]
+  cache_contexts_manager:
+    class: Drupal\Core\Cache\Context\CacheContextsManager
+    arguments: ['@service_container', '%cache_contexts%' ]
+  cache_tags.invalidator:
+    parent: container.trait
+    class: Drupal\Core\Cache\CacheTagsInvalidator
+    calls:
+      - [setContainer, ['@service_container']]
+    tags:
+      - { name: service_collector, call: addInvalidator, tag: cache_tags_invalidator }
+  cache_tags.invalidator.checksum:
+    class: Drupal\Core\Cache\DatabaseCacheTagsChecksum
+    arguments: ['@database']
+    tags:
+      - { name: cache_tags_invalidator}
+  cache.backend.chainedfast:
+    class: Drupal\Core\Cache\ChainedFastBackendFactory
+    arguments: ['@settings']
+    calls:
+      - [setContainer, ['@service_container']]
+  cache.backend.database:
+    class: Drupal\Core\Cache\DatabaseBackendFactory
+    arguments: ['@database', '@cache_tags.invalidator.checksum', '@settings']
+  cache.backend.apcu:
+    class: Drupal\Core\Cache\ApcuBackendFactory
+    arguments: ['@app.root', '@site.path', '@cache_tags.invalidator.checksum']
+  cache.backend.php:
+    class: Drupal\Core\Cache\PhpBackendFactory
+    arguments: ['@cache_tags.invalidator.checksum']
+  cache.backend.memory:
+    class: Drupal\Core\Cache\MemoryBackendFactory
+  # A special cache bin that does not persist beyond the length of the request.
+  cache.static:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin, default_backend: cache.backend.memory }
+    factory: cache_factory:get
+    arguments: [static]
+  cache.bootstrap:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin, default_backend: cache.backend.chainedfast }
+    factory: cache_factory:get
+    arguments: [bootstrap]
+  cache.config:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin, default_backend: cache.backend.chainedfast }
+    factory: cache_factory:get
+    arguments: [config]
+  cache.default:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin }
+    factory: cache_factory:get
+    arguments: [default]
+  cache.entity:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin }
+    factory: cache_factory:get
+    arguments: [entity]
+  cache.menu:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin }
+    factory: cache_factory:get
+    arguments: [menu]
+  cache.render:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin }
+    factory: cache_factory:get
+    arguments: [render]
+  cache.data:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin }
+    factory: cache_factory:get
+    arguments: [data]
+  cache.discovery:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin, default_backend: cache.backend.chainedfast }
+    factory: cache_factory:get
+    arguments: [discovery]
+  cache_router_rebuild_subscriber:
+    class: Drupal\Core\EventSubscriber\CacheRouterRebuildSubscriber
+    tags:
+      - { name: event_subscriber }
+  page_cache_request_policy:
+    class: Drupal\Core\PageCache\DefaultRequestPolicy
+    arguments: ['@session_configuration']
+    tags:
+      - { name: service_collector, tag: page_cache_request_policy, call: addPolicy}
+  page_cache_response_policy:
+    class: Drupal\Core\PageCache\ChainResponsePolicy
+    tags:
+      - { name: service_collector, tag: page_cache_response_policy, call: addPolicy}
+    lazy: true
+  page_cache_kill_switch:
+    class: Drupal\Core\PageCache\ResponsePolicy\KillSwitch
+    tags:
+      - { name: page_cache_response_policy }
+      - { name: dynamic_page_cache_response_policy }
+  page_cache_no_cache_routes:
+    class: Drupal\Core\PageCache\ResponsePolicy\DenyNoCacheRoutes
+    arguments: ['@current_route_match']
+    public: false
+    tags:
+      - { name: page_cache_response_policy }
+      - { name: dynamic_page_cache_response_policy }
+  page_cache_no_server_error:
+    class: Drupal\Core\PageCache\ResponsePolicy\NoServerError
+    public: false
+    tags:
+      - { name: page_cache_response_policy }
+      - { name: dynamic_page_cache_response_policy }
+  config.manager:
+    class: Drupal\Core\Config\ConfigManager
+    arguments: ['@entity_type.manager', '@config.factory', '@config.typed', '@string_translation', '@config.storage', '@event_dispatcher', '@entity.repository']
+  config.factory:
+    class: Drupal\Core\Config\ConfigFactory
+    tags:
+      - { name: event_subscriber }
+      - { name: service_collector, tag: 'config.factory.override', call: addOverride }
+    arguments: ['@config.storage', '@event_dispatcher', '@config.typed']
+  config.importer_subscriber:
+    class: Drupal\Core\Config\Importer\FinalMissingContentSubscriber
+    tags:
+      - { name: event_subscriber }
+  config.installer:
+    class: Drupal\Core\Config\ConfigInstaller
+    arguments: ['@config.factory', '@config.storage', '@config.typed', '@config.manager', '@event_dispatcher', '%install_profile%']
+    lazy: true
+  config.storage:
+    class: Drupal\Core\Config\CachedStorage
+    arguments: ['@config.storage.active', '@cache.config']
+  config.storage.active:
+    class: Drupal\Core\Config\DatabaseStorage
+    arguments: ['@database', 'config']
+    public: false
+    tags:
+      - { name: backend_overridable }
+  config.import_transformer:
+    class: Drupal\Core\Config\ImportStorageTransformer
+    arguments: ['@event_dispatcher', '@database', '@lock', '@lock.persistent']
+  config.storage.export:
+    class: Drupal\Core\Config\ManagedStorage
+    arguments: ['@config.storage.export.manager']
+  config.storage.export.manager:
+    class: Drupal\Core\Config\ExportStorageManager
+    arguments: ['@config.storage', '@database', '@event_dispatcher', '@lock']
+    public: false
+  # @deprecated in Drupal 8.0.x and will be removed before 9.0.0. Use
+  #   config.storage.sync instead.
+  # @see https://www.drupal.org/node/2574957
+  config.storage.staging:
+    class: Drupal\Core\Config\FileStorage
+    factory: Drupal\Core\Config\FileStorageFactory::getSync
+  config.storage.sync:
+    alias: config.storage.staging
+  config.storage.snapshot:
+    class: Drupal\Core\Config\DatabaseStorage
+    arguments: ['@database', config_snapshot]
+    tags:
+      - { name: backend_overridable }
+  config.storage.schema:
+    class: Drupal\Core\Config\ExtensionInstallStorage
+    arguments: ['@config.storage', 'config/schema', '', true, '%install_profile%']
+  config.typed:
+    class: Drupal\Core\Config\TypedConfigManager
+    arguments: ['@config.storage', '@config.storage.schema', '@cache.discovery', '@module_handler', '@class_resolver']
+    tags:
+      - { name: plugin_manager_cache_clear }
+    calls:
+      - [setValidationConstraintManager, ['@validation.constraint']]
+  context.handler:
+    class: Drupal\Core\Plugin\Context\ContextHandler
+  context.repository:
+    class: Drupal\Core\Plugin\Context\LazyContextRepository
+    arguments: ['@service_container']
+  cron:
+    class: Drupal\Core\Cron
+    arguments: ['@module_handler', '@lock', '@queue', '@state', '@account_switcher', '@logger.channel.cron', '@plugin.manager.queue_worker', '@datetime.time']
+    lazy: true
+  diff.formatter:
+    class: Drupal\Core\Diff\DiffFormatter
+    arguments: ['@config.factory']
+  database:
+    class: Drupal\Core\Database\Connection
+    factory: Drupal\Core\Database\Database::getConnection
+    arguments: [default]
+  database.replica:
+    class: Drupal\Core\Database\Connection
+    factory: Drupal\Core\Database\Database::getConnection
+    arguments: [replica]
+  database.replica_kill_switch:
+    class: Drupal\Core\Database\ReplicaKillSwitch
+    arguments: ['@settings', '@datetime.time', '@session']
+    tags:
+      - { name: event_subscriber }
+  datetime.time:
+    class: Drupal\Component\Datetime\Time
+    arguments: ['@request_stack']
+  file_system:
+    class: Drupal\Core\File\FileSystem
+    arguments: ['@stream_wrapper_manager', '@settings', '@logger.channel.file']
+  form_builder:
+    class: Drupal\Core\Form\FormBuilder
+    arguments: ['@form_validator', '@form_submitter', '@form_cache', '@module_handler', '@event_dispatcher', '@request_stack', '@class_resolver', '@element_info', '@theme.manager', '@?csrf_token']
+  form_validator:
+    class: Drupal\Core\Form\FormValidator
+    arguments: ['@request_stack', '@string_translation', '@csrf_token', '@logger.channel.form', '@form_error_handler']
+  form_submitter:
+    class: Drupal\Core\Form\FormSubmitter
+    arguments: ['@request_stack', '@url_generator']
+  form_error_handler:
+    class: Drupal\Core\Form\FormErrorHandler
+  form_cache:
+    class: Drupal\Core\Form\FormCache
+    arguments: ['@app.root', '@keyvalue.expirable', '@module_handler', '@current_user', '@csrf_token', '@logger.channel.form', '@request_stack', '@page_cache_request_policy']
+    public: false  # Private to form_builder
+  keyvalue:
+    class: Drupal\Core\KeyValueStore\KeyValueFactory
+    arguments: ['@service_container', '%factory.keyvalue%']
+  keyvalue.database:
+    class: Drupal\Core\KeyValueStore\KeyValueDatabaseFactory
+    arguments: ['@serialization.phpserialize', '@database']
+  keyvalue.expirable:
+    class: Drupal\Core\KeyValueStore\KeyValueExpirableFactory
+    arguments: ['@service_container', '%factory.keyvalue.expirable%']
+  keyvalue.expirable.database:
+    class: Drupal\Core\KeyValueStore\KeyValueDatabaseExpirableFactory
+    arguments: ['@serialization.phpserialize', '@database']
+  logger.factory:
+    class: Drupal\Core\Logger\LoggerChannelFactory
+    parent: container.trait
+    tags:
+      - { name: service_collector, tag: logger, call: addLogger }
+  logger.channel_base:
+    abstract: true
+    class: Drupal\Core\Logger\LoggerChannel
+    factory: logger.factory:get
+  logger.channel.default:
+    parent: logger.channel_base
+    arguments: ['system']
+  logger.channel.php:
+    parent: logger.channel_base
+    arguments: ['php']
+  logger.channel.image:
+    parent: logger.channel_base
+    arguments: ['image']
+  logger.channel.cron:
+    parent: logger.channel_base
+    arguments: ['cron']
+  logger.channel.file:
+    class: Drupal\Core\Logger\LoggerChannel
+    factory: logger.factory:get
+    arguments: ['file']
+  logger.channel.form:
+    parent: logger.channel_base
+    arguments: ['form']
+  logger.channel.security:
+    parent: logger.channel_base
+    arguments: ['security']
+  logger.log_message_parser:
+    class: Drupal\Core\Logger\LogMessageParser
+
+  serialization.json:
+    class: Drupal\Component\Serialization\Json
+  serialization.phpserialize:
+    class: Drupal\Component\Serialization\PhpSerialize
+  serialization.yaml:
+    class: Drupal\Component\Serialization\Yaml
+
+  settings:
+    class: Drupal\Core\Site\Settings
+    factory: Drupal\Core\Site\Settings::getInstance
+  state:
+    class: Drupal\Core\State\State
+    arguments: ['@keyvalue']
+  queue:
+    class: Drupal\Core\Queue\QueueFactory
+    arguments: ['@settings']
+    calls:
+      - [setContainer, ['@service_container']]
+  queue.database:
+    class: Drupal\Core\Queue\QueueDatabaseFactory
+    arguments: ['@database']
+  path.alias_whitelist:
+    alias: path_alias.whitelist
+    deprecated: 'The "%service_id%" service is deprecated. Use "path_alias.whitelist" instead. See https://drupal.org/node/3092086'
+  path.alias_manager:
+    class: Drupal\Core\Path\AliasManager
+    arguments: ['@path_alias.repository', '@path_alias.whitelist', '@language_manager', '@cache.data']
+    deprecated: 'The "%service_id%" service is deprecated. Use "path_alias.manager" instead. See https://drupal.org/node/3092086'
+  path.current:
+    class: Drupal\Core\Path\CurrentPathStack
+    arguments: ['@request_stack']
+  http_handler_stack:
+    class: GuzzleHttp\HandlerStack
+    public: false
+    factory: GuzzleHttp\HandlerStack::create
+    configurator: ['@http_handler_stack_configurator', configure]
+  http_handler_stack_configurator:
+    class: Drupal\Core\Http\HandlerStackConfigurator
+    public: false
+    arguments: ['@service_container']
+  http_client:
+    class: GuzzleHttp\Client
+    factory: http_client_factory:fromOptions
+  http_client_factory:
+    class: Drupal\Core\Http\ClientFactory
+    arguments: ['@http_handler_stack']
+  plugin.manager.link_relation_type:
+    class: \Drupal\Core\Http\LinkRelationTypeManager
+    arguments: ['@app.root', '@module_handler', '@cache.discovery']
+  theme.negotiator:
+    class: Drupal\Core\Theme\ThemeNegotiator
+    arguments: ['@access_check.theme', '@class_resolver']
+    tags:
+      - { name: service_id_collector, tag: theme_negotiator }
+  theme.negotiator.default:
+    class: Drupal\Core\Theme\DefaultNegotiator
+    arguments: ['@config.factory']
+    tags:
+      - { name: theme_negotiator, priority: -100 }
+  theme.negotiator.ajax_base_page:
+    class: Drupal\Core\Theme\AjaxBasePageNegotiator
+    arguments: ['@csrf_token', '@config.factory', '@request_stack']
+    tags:
+      - { name: theme_negotiator, priority: 1000 }
+  container.namespaces:
+    class: ArrayObject
+    arguments: [ '%container.namespaces%' ]
+  container.trait:
+    abstract: true
+    calls:
+      - [setContainer, ['@service_container']]
+  default_plugin_manager:
+    abstract: true
+    arguments: ['@container.namespaces', '@cache.discovery', '@module_handler']
+  module_handler:
+    class: Drupal\Core\Extension\ModuleHandler
+    arguments: ['@app.root', '%container.modules%', '@cache.bootstrap']
+  module_installer:
+    class: Drupal\Core\Extension\ModuleInstaller
+    tags:
+      - { name: service_collector, tag: 'module_install.uninstall_validator', call: addUninstallValidator }
+    arguments: ['@app.root', '@module_handler', '@kernel']
+    lazy: true
+  extension.list.module:
+    class: Drupal\Core\Extension\ModuleExtensionList
+    arguments: ['@app.root', 'module', '@cache.default', '@info_parser', '@module_handler', '@state', '@config.factory', '@extension.list.profile', '%install_profile%', '%container.modules%']
+  extension.list.profile:
+    class: Drupal\Core\Extension\ProfileExtensionList
+    arguments: ['@app.root', 'profile', '@cache.default', '@info_parser', '@module_handler', '@state', '%install_profile%']
+  extension.list.theme:
+    class: Drupal\Core\Extension\ThemeExtensionList
+    arguments: ['@app.root', 'theme', '@cache.default', '@info_parser', '@module_handler', '@state', '@config.factory', '@extension.list.theme_engine', '%install_profile%']
+  extension.list.theme_engine:
+    class: Drupal\Core\Extension\ThemeEngineExtensionList
+    arguments: ['@app.root', 'theme_engine', '@cache.default', '@info_parser', '@module_handler', '@state', '%install_profile%']
+  content_uninstall_validator:
+    class: Drupal\Core\Entity\ContentUninstallValidator
+    tags:
+      - { name: module_install.uninstall_validator }
+    arguments: ['@entity_type.manager', '@string_translation']
+    lazy: true
+  required_module_uninstall_validator:
+    class: Drupal\Core\Extension\RequiredModuleUninstallValidator
+    tags:
+      - { name: module_install.uninstall_validator }
+    arguments: ['@string_translation', '@extension.list.module']
+    lazy: true
+  module_required_by_themes_uninstall_validator:
+    class: Drupal\Core\Extension\ModuleRequiredByThemesUninstallValidator
+    tags:
+      - { name: module_install.uninstall_validator }
+    arguments: ['@string_translation', '@extension.list.module', '@extension.list.theme']
+    lazy: true
+  theme_handler:
+    class: Drupal\Core\Extension\ThemeHandler
+    arguments: ['@app.root', '@config.factory', '@extension.list.theme']
+  theme_installer:
+    class: Drupal\Core\Extension\ThemeInstaller
+    arguments: ['@theme_handler', '@config.factory', '@config.installer', '@module_handler', '@config.manager', '@asset.css.collection_optimizer', '@router.builder', '@logger.channel.default', '@state', '@extension.list.module']
+  # @deprecated in Drupal 8.0.x and will be removed before 9.0.0. Use the other
+  #   entity* services instead.
+  entity.manager:
+    class: Drupal\Core\Entity\EntityManager
+    parent: container.trait
+    deprecated: The "%service_id%" service is deprecated. You should use the 'entity_type.manager' service instead.
+  entity.memory_cache:
+    class: Drupal\Core\Cache\MemoryCache\MemoryCache
+  entity_type.manager:
+    class: Drupal\Core\Entity\EntityTypeManager
+    arguments: ['@container.namespaces', '@module_handler', '@cache.discovery', '@string_translation', '@class_resolver', '@entity.last_installed_schema.repository']
+    parent: container.trait
+    tags:
+      - { name: plugin_manager_cache_clear }
+  entity_type.repository:
+    class: Drupal\Core\Entity\EntityTypeRepository
+    arguments: ['@entity_type.manager']
+  entity_type.bundle.info:
+    class: Drupal\Core\Entity\EntityTypeBundleInfo
+    arguments: ['@entity_type.manager', '@language_manager', '@module_handler', '@typed_data_manager', '@cache.discovery']
+  entity.repository:
+    class: Drupal\Core\Entity\EntityRepository
+    arguments: ['@entity_type.manager', '@language_manager', '@context.repository']
+  entity_display.repository:
+    class: Drupal\Core\Entity\EntityDisplayRepository
+    arguments: ['@entity_type.manager', '@module_handler', '@cache.discovery', '@language_manager']
+  entity_field.manager:
+    class: Drupal\Core\Entity\EntityFieldManager
+    arguments: ['@entity_type.manager', '@entity_type.bundle.info', '@entity_display.repository', '@typed_data_manager', '@language_manager', '@keyvalue', '@module_handler', '@cache.discovery']
+  entity_type.listener:
+    class: Drupal\Core\Entity\EntityTypeListener
+    arguments: ['@entity_type.manager', '@entity_field.manager', '@event_dispatcher', '@entity.last_installed_schema.repository']
+  entity_bundle.listener:
+    class: Drupal\Core\Entity\EntityBundleListener
+    arguments: ['@entity_type.manager', '@entity_type.bundle.info', '@entity_field.manager', '@module_handler']
+  entity_route_subscriber:
+    class: Drupal\Core\EventSubscriber\EntityRouteProviderSubscriber
+    arguments: ['@entity_type.manager']
+    tags:
+      - { name: event_subscriber }
+  entity.definition_update_manager:
+    class: Drupal\Core\Entity\EntityDefinitionUpdateManager
+    arguments: ['@entity_type.manager', '@entity.last_installed_schema.repository', '@entity_field.manager', '@entity_type.listener', '@field_storage_definition.listener']
+  entity.last_installed_schema.repository:
+    class: Drupal\Core\Entity\EntityLastInstalledSchemaRepository
+    arguments: ['@keyvalue']
+  entity_field.deleted_fields_repository:
+    class: Drupal\Core\Field\DeletedFieldsRepository
+    arguments: ['@state']
+  field_storage_definition.listener:
+    class: Drupal\Core\Field\FieldStorageDefinitionListener
+    arguments: ['@entity_type.manager', '@event_dispatcher', '@entity.last_installed_schema.repository', '@entity_field.manager', '@entity_field.deleted_fields_repository']
+  field_definition.listener:
+    class: Drupal\Core\Field\FieldDefinitionListener
+    arguments: ['@entity_type.manager', '@entity_field.manager', '@keyvalue', '@cache.discovery']
+  entity.form_builder:
+    class: Drupal\Core\Entity\EntityFormBuilder
+    arguments: ['@entity_type.manager', '@form_builder']
+  entity.bundle_config_import_validator:
+    class: Drupal\Core\Entity\Event\BundleConfigImportValidate
+    arguments: ['@config.manager', '@entity_type.manager']
+    tags:
+      - { name: event_subscriber }
+  entity.autocomplete_matcher:
+    class: Drupal\Core\Entity\EntityAutocompleteMatcher
+    arguments: ['@plugin.manager.entity_reference_selection']
+  plugin_form.factory:
+    class: Drupal\Core\Plugin\PluginFormFactory
+    arguments: ['@class_resolver']
+  plugin.manager.entity_reference_selection:
+    class: Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager
+    parent: default_plugin_manager
+  plugin.manager.block:
+    class: Drupal\Core\Block\BlockManager
+    parent: default_plugin_manager
+    arguments: ['@logger.channel.default']
+  plugin.manager.field.field_type:
+    class: Drupal\Core\Field\FieldTypePluginManager
+    arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@typed_data_manager']
+  plugin.manager.field.widget:
+    class: Drupal\Core\Field\WidgetPluginManager
+    arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@plugin.manager.field.field_type']
+  plugin.manager.field.formatter:
+    class: Drupal\Core\Field\FormatterPluginManager
+    arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@plugin.manager.field.field_type']
+  plugin.manager.archiver:
+    class: Drupal\Core\Archiver\ArchiverManager
+    parent: default_plugin_manager
+    arguments: ['@file_system']
+  plugin.manager.action:
+    class: Drupal\Core\Action\ActionManager
+    arguments: ['@container.namespaces', '@cache.discovery', '@module_handler']
+  plugin.manager.menu.link:
+    class: Drupal\Core\Menu\MenuLinkManager
+    arguments: ['@menu.tree_storage', '@menu_link.static.overrides', '@module_handler']
+  menu.link_tree:
+    class: Drupal\Core\Menu\MenuLinkTree
+    arguments: ['@menu.tree_storage', '@plugin.manager.menu.link', '@router.route_provider', '@menu.active_trail', '@controller_resolver']
+  menu.default_tree_manipulators:
+    class: Drupal\Core\Menu\DefaultMenuLinkTreeManipulators
+    arguments: ['@access_manager', '@current_user', '@entity_type.manager']
+  menu.active_trail:
+    class: Drupal\Core\Menu\MenuActiveTrail
+    arguments: ['@plugin.manager.menu.link', '@current_route_match', '@cache.menu', '@lock']
+    tags:
+      - { name: needs_destruction }
+  menu.parent_form_selector:
+    class: Drupal\Core\Menu\MenuParentFormSelector
+    arguments: ['@menu.link_tree', '@entity_type.manager', '@string_translation']
+  plugin.manager.menu.local_action:
+    class: Drupal\Core\Menu\LocalActionManager
+    arguments: ['@http_kernel.controller.argument_resolver', '@request_stack', '@current_route_match', '@router.route_provider', '@module_handler', '@cache.discovery', '@language_manager', '@access_manager', '@current_user']
+  plugin.manager.menu.local_task:
+    class: Drupal\Core\Menu\LocalTaskManager
+    arguments: ['@http_kernel.controller.argument_resolver', '@request_stack', '@current_route_match', '@router.route_provider', '@module_handler', '@cache.discovery', '@language_manager', '@access_manager', '@current_user']
+  plugin.manager.menu.contextual_link:
+    class: Drupal\Core\Menu\ContextualLinkManager
+    arguments: ['@controller_resolver', '@module_handler', '@cache.discovery', '@language_manager', '@access_manager', '@current_user', '@request_stack']
+  plugin.manager.display_variant:
+    class: Drupal\Core\Display\VariantManager
+    parent: default_plugin_manager
+  plugin.manager.queue_worker:
+    class: Drupal\Core\Queue\QueueWorkerManager
+    parent: default_plugin_manager
+  plugin.cache_clearer:
+    class: Drupal\Core\Plugin\CachedDiscoveryClearer
+    lazy: true
+  paramconverter.menu_link:
+    class: Drupal\Core\ParamConverter\MenuLinkPluginConverter
+    tags:
+      - { name: paramconverter }
+    arguments: ['@plugin.manager.menu.link']
+    lazy: true
+  menu.tree_storage:
+    class: Drupal\Core\Menu\MenuTreeStorage
+    arguments: ['@database', '@cache.menu', '@cache_tags.invalidator', 'menu_tree']
+    public: false  # Private to plugin.manager.menu.link and menu.link_tree
+    tags:
+      - { name: backend_overridable }
+  menu_link.static.overrides:
+    class: Drupal\Core\Menu\StaticMenuLinkOverrides
+    arguments: ['@config.factory']
+  request_stack:
+    class: Symfony\Component\HttpFoundation\RequestStack
+    tags:
+      - { name: persist }
+  current_route_match:
+     class: Drupal\Core\Routing\CurrentRouteMatch
+     arguments: ['@request_stack']
+  event_dispatcher:
+    class: Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher
+    arguments: ['@service_container']
+  app.root:
+    class: SplString
+    factory: app.root.factory:get
+    tags:
+      - { name: parameter_service }
+  app.root.factory:
+    class: Drupal\Core\AppRootFactory
+    arguments: ['@kernel']
+    public: false
+  site.path:
+    class: SplString
+    factory: site.path.factory:get
+    tags:
+      - { name: parameter_service }
+  site.path.factory:
+    class: Drupal\Core\SitePathFactory
+    arguments: ['@kernel']
+    public: false
+  controller_resolver:
+    class: Drupal\Core\Controller\ControllerResolver
+    arguments: ['@psr7.http_message_factory', '@class_resolver']
+  class_resolver:
+    class: Drupal\Core\DependencyInjection\ClassResolver
+    calls:
+      - [setContainer, ['@service_container']]
+  title_resolver:
+    class: Drupal\Core\Controller\TitleResolver
+    arguments: ['@controller_resolver', '@string_translation', '@http_kernel.controller.argument_resolver']
+  http_kernel:
+    class: Stack\StackedHttpKernel
+  http_kernel.basic:
+    class: Symfony\Component\HttpKernel\HttpKernel
+    arguments: ['@event_dispatcher', '@controller_resolver', '@request_stack', '@http_kernel.controller.argument_resolver']
+  http_kernel.controller.argument_resolver:
+    class: Symfony\Component\HttpKernel\Controller\ArgumentResolver
+    arguments: ['@http_kernel.controller.argument_metadata_factory', ['@argument_resolver.request_attribute', '@argument_resolver.request', '@argument_resolver.psr7_request', '@argument_resolver.route_match', '@argument_resolver.default']]
+  http_kernel.controller.argument_metadata_factory:
+    class: Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory
+    public: false
+  argument_resolver.request_attribute:
+    class: Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestAttributeValueResolver
+    public: false
+  argument_resolver.request:
+    class: Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver
+    public: false
+  argument_resolver.psr7_request:
+    class: Drupal\Core\Controller\ArgumentResolver\Psr7RequestValueResolver
+    arguments: ['@psr7.http_message_factory']
+    public: false
+  argument_resolver.route_match:
+    class: Drupal\Core\Controller\ArgumentResolver\RouteMatchValueResolver
+    public: false
+  argument_resolver.default:
+    class: Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver
+    public: false
+  http_middleware.negotiation:
+    class: Drupal\Core\StackMiddleware\NegotiationMiddleware
+    tags:
+      - { name: http_middleware, priority: 400 }
+  http_middleware.reverse_proxy:
+    class: Drupal\Core\StackMiddleware\ReverseProxyMiddleware
+    arguments: ['@settings']
+    tags:
+      - { name: http_middleware, priority: 300 }
+  http_middleware.kernel_pre_handle:
+    class: Drupal\Core\StackMiddleware\KernelPreHandle
+    arguments: ['@kernel']
+    tags:
+      - { name: http_middleware, priority: 100 }
+  http_middleware.session:
+    class: Drupal\Core\StackMiddleware\Session
+    tags:
+      - { name: http_middleware, priority: 50 }
+    calls:
+      - [setContainer, ['@service_container']]
+  http_middleware.cors:
+     class: Asm89\Stack\Cors
+     arguments: ['%cors.config%']
+     tags:
+       - { name: http_middleware, priority: 250 }
+  psr7.http_foundation_factory:
+    class: Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory
+  psr7.http_message_factory:
+    class: Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory
+  language_manager:
+    class: Drupal\Core\Language\LanguageManager
+    arguments: ['@language.default']
+  language.current_language_context:
+    class: Drupal\Core\Language\ContextProvider\CurrentLanguageContext
+    arguments: ['@language_manager']
+    tags:
+      - { name: 'context_provider' }
+  language.default:
+    class: Drupal\Core\Language\LanguageDefault
+    arguments: ['%language.default_values%']
+  string_translator.custom_strings:
+    class: Drupal\Core\StringTranslation\Translator\CustomStrings
+    arguments: ['@settings']
+    tags:
+      - { name: string_translator, priority: 30 }
+  string_translation:
+    class: Drupal\Core\StringTranslation\TranslationManager
+    arguments: ['@language.default']
+    tags:
+      - { name: service_collector, tag: string_translator, call: addTranslator }
+  typed_data_manager:
+    class: Drupal\Core\TypedData\TypedDataManager
+    arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@class_resolver']
+    calls:
+      - [setValidationConstraintManager, ['@validation.constraint']]
+    tags:
+      - { name: plugin_manager_cache_clear }
+  validation.constraint:
+    class: Drupal\Core\Validation\ConstraintManager
+    parent: default_plugin_manager
+    tags:
+      - { name: plugin_manager_cache_clear }
+  lock:
+    class: Drupal\Core\Lock\DatabaseLockBackend
+    arguments: ['@database']
+    tags:
+      - { name: backend_overridable }
+    lazy: true
+  lock.persistent:
+    class: Drupal\Core\Lock\PersistentDatabaseLockBackend
+    arguments: ['@database']
+    tags:
+      - { name: backend_overridable }
+    lazy: true
+  router.request_context:
+    class: Drupal\Core\Routing\RequestContext
+    tags:
+      - { name: persist }
+    calls:
+      - [fromRequestStack, ['@request_stack']]
+  router.admin_context:
+    class: Drupal\Core\Routing\AdminContext
+    arguments: ['@current_route_match']
+  router.route_provider:
+    class: Drupal\Core\Routing\RouteProvider
+    arguments: ['@database', '@state', '@path.current', '@cache.data', '@path_processor_manager', '@cache_tags.invalidator', 'router', '@language_manager']
+    tags:
+      - { name: event_subscriber }
+      - { name: backend_overridable }
+  router.route_provider.lazy_builder:
+    class: Drupal\Core\Routing\RouteProviderLazyBuilder
+    arguments: ['@router.route_provider', '@router.builder']
+    tags:
+      - { name: event_subscriber }
+  router.route_preloader:
+    class: Drupal\Core\Routing\RoutePreloader
+    arguments: ['@router.route_provider', '@state', '@cache.bootstrap']
+    tags:
+      - { name: 'event_subscriber' }
+  router.matcher.final_matcher:
+    class: Drupal\Core\Routing\UrlMatcher
+    arguments: ['@path.current']
+    deprecated: The "%service_id%" service is deprecated. You should use the 'router.no_access_checks' service instead.
+  router.matcher:
+    class: Symfony\Cmf\Component\Routing\NestedMatcher\NestedMatcher
+    arguments: ['@router.route_provider']
+    calls:
+      - [setFinalMatcher, ['@router.matcher.final_matcher']]
+    tags:
+      - { name: service_collector, tag: non_lazy_route_filter, call: addRouteFilter }
+    deprecated: The "%service_id%" service is deprecated. You should use the 'router.no_access_checks' service instead.
+  url_generator.non_bubbling:
+    class: Drupal\Core\Routing\UrlGenerator
+    arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@request_stack', '%filter_protocols%']
+    public: false
+    calls:
+      - [setContext, ['@?router.request_context']]
+  url_generator:
+    class: Drupal\Core\Render\MetadataBubblingUrlGenerator
+    arguments: ['@url_generator.non_bubbling', '@renderer']
+    calls:
+      - [setContext, ['@?router.request_context']]
+  redirect.destination:
+    class: Drupal\Core\Routing\RedirectDestination
+    arguments: ['@request_stack', '@url_generator']
+  unrouted_url_assembler:
+    class: Drupal\Core\Utility\UnroutedUrlAssembler
+    arguments: ['@request_stack', '@path_processor_manager', '%filter_protocols%']
+  link_generator:
+    class: Drupal\Core\Utility\LinkGenerator
+    arguments: ['@url_generator', '@module_handler', '@renderer']
+  router:
+    class: Drupal\Core\Routing\AccessAwareRouter
+    arguments: ['@router.no_access_checks', '@access_manager', '@current_user']
+  router.dynamic:
+    class: Symfony\Cmf\Component\Routing\DynamicRouter
+    arguments: ['@router.request_context', '@router.matcher', '@url_generator']
+    tags:
+      - { name: service_collector, tag: non_lazy_route_enhancer, call: addRouteEnhancer }
+    deprecated: The "%service_id%" service is deprecated. You should use the 'router.no_access_checks' service instead.
+  router.no_access_checks:
+    class: \Drupal\Core\Routing\Router
+    arguments: ['@router.route_provider', '@path.current', '@url_generator']
+    tags:
+      # @todo Try to combine those tags together, see https://www.drupal.org/node/2915772.
+      - { name: service_collector, tag: non_lazy_route_enhancer, call: addRouteEnhancer }
+      - { name: service_collector, tag: route_enhancer, call: addRouteEnhancer  }
+      - { name: service_collector, tag: non_lazy_route_filter, call: addRouteFilter }
+      - { name: service_collector, tag: route_filter, call: addRouteFilter }
+    calls:
+      - [setContext, ['@router.request_context']]
+  router.path_roots_subscriber:
+    class: Drupal\Core\EventSubscriber\PathRootsSubscriber
+    arguments: ['@state']
+    tags:
+      - { name: event_subscriber }
+  entity.query:
+    class: Drupal\Core\Entity\Query\QueryFactory
+    arguments: ['@entity_type.manager']
+    calls:
+      - [setContainer, ['@service_container']]
+    deprecated: The "%service_id%" service is deprecated. Use the 'entity_type.manager' service to get an entity type's storage object and then call \Drupal\Core\Entity\EntityStorageInterface::getQuery() or \Drupal\Core\Entity\EntityStorageInterface::getAggregateQuery() instead. See https://www.drupal.org/node/2849874
+  entity.query.config:
+    class: Drupal\Core\Config\Entity\Query\QueryFactory
+    arguments: ['@config.factory', '@keyvalue', '@config.manager']
+    tags:
+      - { name: event_subscriber }
+  entity.query.sql:
+    class: Drupal\Core\Entity\Query\Sql\QueryFactory
+    arguments: ['@database']
+    tags:
+      - { name: backend_overridable }
+  pgsql.entity.query.sql:
+    class: Drupal\Core\Entity\Query\Sql\pgsql\QueryFactory
+    arguments: ['@database']
+  entity.query.null:
+    class: Drupal\Core\Entity\Query\Null\QueryFactory
+  entity.query.keyvalue:
+    class: Drupal\Core\Entity\KeyValueStore\Query\QueryFactory
+    arguments: ['@keyvalue']
+  router.dumper:
+    class: Drupal\Core\Routing\MatcherDumper
+    arguments: ['@database', '@state']
+    tags:
+      - { name: backend_overridable }
+    lazy: true
+  router.builder:
+    class: Drupal\Core\Routing\RouteBuilder
+    arguments: ['@router.dumper', '@lock', '@event_dispatcher', '@module_handler', '@controller_resolver', '@access_manager.check_provider']
+    lazy: true
+    tags:
+      - { name: needs_destruction }
+  menu.rebuild_subscriber:
+    class: Drupal\Core\EventSubscriber\MenuRouterRebuildSubscriber
+    arguments: ['@lock', '@plugin.manager.menu.link', '@database', '@database.replica_kill_switch']
+    tags:
+      - { name: event_subscriber }
+  path.alias_storage:
+    class: Drupal\Core\Path\AliasStorage
+    arguments: ['@database', '@module_handler', '@entity_type.manager']
+    tags:
+      - { name: backend_overridable }
+    deprecated: The "%service_id%" service is deprecated. Use the "path_alias.repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865
+  path.matcher:
+    class: Drupal\Core\Path\PathMatcher
+    arguments: ['@config.factory', '@current_route_match']
+  path.validator:
+    class: Drupal\Core\Path\PathValidator
+    arguments: ['@router', '@router.no_access_checks', '@current_user', '@path_processor_manager']
+
+# The argument to the hashing service defined in services.yml, to the
+# constructor of PhpassHashedPassword is the log2 number of iterations for
+# password stretching.
+# @todo increase by 1 every Drupal version in order to counteract increases in
+# the speed and power of computers available to crack the hashes. The current
+# password hashing method was introduced in Drupal 7 with a log2 count of 15.
+  password:
+    class: Drupal\Core\Password\PhpassHashedPassword
+    arguments: [16]
+  request_format_route_filter:
+    class: Drupal\Core\Routing\RequestFormatRouteFilter
+    tags:
+      - { name: route_filter }
+  method_filter:
+    class: Drupal\Core\Routing\MethodFilter
+    tags:
+      # The HTTP method route filter must run very early: it removes any routes
+      # whose requirements do not allow the HTTP method of the current request.
+      # Throws a 405 if no routes match the current request's HTTP method.
+      # (If it runs before content_type_header_matcher, it can ensure that only
+      # receives routes which can have a Content-Type request header.)
+      - { name: route_filter, priority: 10 }
+  content_type_header_matcher:
+    class: Drupal\Core\Routing\ContentTypeHeaderMatcher
+    tags:
+      # The Content-Type request header route filter must run early: it removes
+      # any routes whose requirements do not allow the Content-Type request
+      # header of the current request.
+      # Throws a 415 if no routes match the Content-Type request header of the
+      # current request, or if it has no Content-Type request header.
+      # Note it does nothing for GET requests.
+      - { name: route_filter, priority: 5 }
+  paramconverter_manager:
+    class: Drupal\Core\ParamConverter\ParamConverterManager
+    tags:
+      - { name: service_collector, tag: paramconverter, call: addConverter }
+  paramconverter_subscriber:
+    class: Drupal\Core\EventSubscriber\ParamConverterSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@paramconverter_manager']
+  paramconverter.entity:
+    class: Drupal\Core\ParamConverter\EntityConverter
+    tags:
+      - { name: paramconverter }
+    arguments: ['@entity_type.manager', '@entity.repository']
+  paramconverter.entity_revision:
+    class: Drupal\Core\ParamConverter\EntityRevisionParamConverter
+    tags:
+      - { name: paramconverter }
+    arguments: ['@entity_type.manager', '@entity.repository']
+  paramconverter.configentity_admin:
+    class: Drupal\Core\ParamConverter\AdminPathConfigEntityConverter
+    tags:
+      # Use a higher priority than EntityConverter, see the class for details.
+      - { name: paramconverter, priority: 5 }
+    arguments: ['@entity_type.manager', '@config.factory', '@router.admin_context', '@entity.repository']
+    lazy: true
+  route_subscriber.module:
+    class: Drupal\Core\EventSubscriber\ModuleRouteSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@module_handler']
+  resolver_manager.entity:
+    class: Drupal\Core\Entity\EntityResolverManager
+    arguments: ['@entity_type.manager', '@class_resolver']
+  route_subscriber.entity:
+    class: Drupal\Core\EventSubscriber\EntityRouteAlterSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@resolver_manager.entity']
+  ajax_response.subscriber:
+    class: Drupal\Core\EventSubscriber\AjaxResponseSubscriber
+    arguments: ['@ajax_response.attachments_processor']
+    tags:
+      - { name: event_subscriber }
+  form_ajax_subscriber:
+    class: Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber
+    arguments: ['@form_ajax_response_builder', '@string_translation', '@messenger']
+    tags:
+      - { name: event_subscriber }
+  route_enhancer.param_conversion:
+    class: Drupal\Core\Routing\Enhancer\ParamConversionEnhancer
+    arguments: ['@paramconverter_manager']
+    tags:
+      - { name: route_enhancer, priority: 5000 }
+      - { name: event_subscriber }
+  route_enhancer.form:
+    class: Drupal\Core\Routing\Enhancer\FormRouteEnhancer
+    tags:
+      - { name: route_enhancer }
+  route_enhancer.entity:
+    class: Drupal\Core\Entity\Enhancer\EntityRouteEnhancer
+    tags:
+      - { name: route_enhancer, priority: 20 }
+  route_enhancer.entity_revision:
+    class: Drupal\Core\Routing\Enhancer\EntityRevisionRouteEnhancer
+    tags:
+      - { name: route_enhancer }
+  route_special_attributes_subscriber:
+    class: Drupal\Core\EventSubscriber\SpecialAttributesRouteSubscriber
+    tags:
+      - { name: event_subscriber }
+  route_http_method_subscriber:
+    class: Drupal\Core\EventSubscriber\RouteMethodSubscriber
+    tags:
+      - { name: event_subscriber }
+  psr_response_view_subscriber:
+    class: Drupal\Core\EventSubscriber\PsrResponseSubscriber
+    arguments: ['@psr7.http_foundation_factory']
+    tags:
+      - { name: event_subscriber }
+
+  # Main content view subscriber plus the renderers it uses.
+  main_content_view_subscriber:
+    class: Drupal\Core\EventSubscriber\MainContentViewSubscriber
+    arguments: ['@class_resolver', '@current_route_match', '%main_content_renderers%']
+    tags:
+      - { name: event_subscriber }
+  renderer_non_html:
+    class: Drupal\Core\EventSubscriber\RenderArrayNonHtmlSubscriber
+    tags:
+      - { name: event_subscriber }
+  main_content_renderer.html:
+    class: Drupal\Core\Render\MainContent\HtmlRenderer
+    arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@module_handler', '@renderer', '@render_cache', '%renderer.config%']
+    tags:
+      - { name: render.main_content_renderer, format: html }
+  main_content_renderer.ajax:
+    class: Drupal\Core\Render\MainContent\AjaxRenderer
+    arguments: ['@element_info', '@renderer']
+    tags:
+      - { name: render.main_content_renderer, format: drupal_ajax }
+      - { name: render.main_content_renderer, format: iframeupload }
+  main_content_renderer.dialog:
+    class: Drupal\Core\Render\MainContent\DialogRenderer
+    arguments: ['@title_resolver', '@renderer']
+    tags:
+      - { name: render.main_content_renderer, format: drupal_dialog }
+  main_content_renderer.off_canvas:
+    class: Drupal\Core\Render\MainContent\OffCanvasRenderer
+    arguments: ['@title_resolver', '@renderer']
+    tags:
+      - { name: render.main_content_renderer, format: drupal_dialog.off_canvas }
+  main_content_renderer.off_canvas_top:
+    class: Drupal\Core\Render\MainContent\OffCanvasRenderer
+    arguments: ['@title_resolver', '@renderer', 'top']
+    tags:
+      - { name: render.main_content_renderer, format: drupal_dialog.off_canvas_top }
+  main_content_renderer.modal:
+    class: Drupal\Core\Render\MainContent\ModalRenderer
+    arguments: ['@title_resolver', '@renderer']
+    tags:
+      - { name: render.main_content_renderer, format: drupal_modal }
+  controller.form:
+    class: Drupal\Core\Controller\HtmlFormController
+    arguments: ['@http_kernel.controller.argument_resolver', '@form_builder', '@class_resolver']
+  controller.entity_form:
+    class: Drupal\Core\Entity\HtmlEntityFormController
+    arguments: ['@http_kernel.controller.argument_resolver', '@form_builder', '@entity_type.manager']
+  form_ajax_response_builder:
+    class: Drupal\Core\Form\FormAjaxResponseBuilder
+    arguments: ['@main_content_renderer.ajax', '@current_route_match']
+  router_listener:
+    class: Symfony\Component\HttpKernel\EventListener\RouterListener
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@router', '@request_stack', '@router.request_context', NULL]
+  options_request_listener:
+    class: Drupal\Core\EventSubscriber\OptionsRequestSubscriber
+    arguments: ['@router.route_provider']
+    tags:
+      - { name: event_subscriber }
+  bare_html_page_renderer:
+    class: Drupal\Core\Render\BareHtmlPageRenderer
+    arguments: ['@renderer', '@html_response.attachments_processor']
+    lazy: true
+  private_key:
+    class: Drupal\Core\PrivateKey
+    arguments: ['@state']
+  csrf_token:
+    class: Drupal\Core\Access\CsrfTokenGenerator
+    arguments: ['@private_key', '@session_manager.metadata_bag']
+  access_arguments_resolver_factory:
+    class: Drupal\Core\Access\AccessArgumentsResolverFactory
+  access_manager:
+    class: Drupal\Core\Access\AccessManager
+    arguments: ['@router.route_provider', '@paramconverter_manager', '@access_arguments_resolver_factory', '@current_user', '@access_manager.check_provider']
+  access_manager.check_provider:
+    class: Drupal\Core\Access\CheckProvider
+    calls:
+      - [setContainer, ['@service_container']]
+    public: false
+  access_check.default:
+    class: Drupal\Core\Access\DefaultAccessCheck
+    tags:
+      - { name: access_check, applies_to: _access }
+  access_check.entity:
+    class: Drupal\Core\Entity\EntityAccessCheck
+    tags:
+      - { name: access_check, applies_to: _entity_access }
+  access_check.entity_bundles:
+    class: Drupal\Core\Entity\EntityBundleAccessCheck
+    tags:
+      - { name: access_check, applies_to: _entity_bundles }
+  access_check.entity_create:
+    class: Drupal\Core\Entity\EntityCreateAccessCheck
+    arguments: ['@entity_type.manager']
+    tags:
+      - { name: access_check, applies_to: _entity_create_access }
+  access_check.entity_create_any:
+    class: Drupal\Core\Entity\EntityCreateAnyAccessCheck
+    arguments: ['@entity_type.manager', '@entity_type.bundle.info']
+    tags:
+      - { name: access_check, applies_to: _entity_create_any_access }
+  access_check.entity_delete_multiple:
+    class: Drupal\Core\Entity\EntityDeleteMultipleAccessCheck
+    arguments: ['@entity_type.manager', '@tempstore.private', '@request_stack']
+    tags:
+      - { name: access_check, applies_to: _entity_delete_multiple_access }
+  access_check.theme:
+    class: Drupal\Core\Theme\ThemeAccessCheck
+    arguments: ['@theme_handler']
+    tags:
+      - { name: access_check, applies_to: _access_theme }
+  access_check.custom:
+    class: Drupal\Core\Access\CustomAccessCheck
+    arguments: ['@controller_resolver', '@access_arguments_resolver_factory']
+    tags:
+      - { name: access_check, applies_to: _custom_access }
+  access_check.csrf:
+    class: Drupal\Core\Access\CsrfAccessCheck
+    tags:
+      - { name: access_check, applies_to: _csrf_token, needs_incoming_request: TRUE }
+    arguments: ['@csrf_token']
+  access_check.header.csrf:
+    class: Drupal\Core\Access\CsrfRequestHeaderAccessCheck
+    arguments: ['@session_configuration', '@csrf_token']
+    tags:
+      - { name: access_check, needs_incoming_request: TRUE }
+  maintenance_mode:
+    class: Drupal\Core\Site\MaintenanceMode
+    arguments: ['@state']
+  maintenance_mode_subscriber:
+    class: Drupal\Core\EventSubscriber\MaintenanceModeSubscriber
+    arguments: ['@maintenance_mode', '@config.factory', '@string_translation', '@url_generator', '@current_user', '@bare_html_page_renderer', '@messenger']
+    tags:
+      - { name: event_subscriber }
+  path_subscriber:
+    class: Drupal\Core\EventSubscriber\PathSubscriber
+    arguments: ['@path_alias.manager', '@path.current']
+    deprecated: 'The "%service_id%" service is deprecated. Use "path_alias.subscriber" instead. See https://drupal.org/node/3092086'
+  route_access_response_subscriber:
+    class: Drupal\Core\EventSubscriber\RouteAccessResponseSubscriber
+    tags:
+      - { name: event_subscriber }
+  client_error_response_subscriber:
+    class: Drupal\Core\EventSubscriber\ClientErrorResponseSubscriber
+    tags:
+      - { name: event_subscriber }
+  anonymous_user_response_subscriber:
+    class: Drupal\Core\EventSubscriber\AnonymousUserResponseSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@current_user']
+  ajax_response.attachments_processor:
+    class: Drupal\Core\Ajax\AjaxResponseAttachmentsProcessor
+    tags:
+    arguments: ['@asset.resolver', '@config.factory', '@asset.css.collection_renderer', '@asset.js.collection_renderer', '@request_stack', '@renderer', '@module_handler']
+  html_response.attachments_processor:
+    class: Drupal\Core\Render\HtmlResponseAttachmentsProcessor
+    tags:
+    arguments: ['@asset.resolver', '@config.factory', '@asset.css.collection_renderer', '@asset.js.collection_renderer', '@request_stack', '@renderer', '@module_handler']
+  html_response.subscriber:
+    class: Drupal\Core\EventSubscriber\HtmlResponseSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@html_response.attachments_processor']
+  finish_response_subscriber:
+    class: Drupal\Core\EventSubscriber\FinishResponseSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@language_manager', '@config.factory', '@page_cache_request_policy', '@page_cache_response_policy', '@cache_contexts_manager', '%http.response.debug_cacheability_headers%']
+  response_generator_subscriber:
+    class: Drupal\Core\EventSubscriber\ResponseGeneratorSubscriber
+    tags:
+      - { name: event_subscriber }
+  redirect_response_subscriber:
+    class: Drupal\Core\EventSubscriber\RedirectResponseSubscriber
+    arguments: ['@unrouted_url_assembler', '@router.request_context']
+    tags:
+      - { name: event_subscriber }
+  redirect_leading_slashes_subscriber:
+    class: Drupal\Core\EventSubscriber\RedirectLeadingSlashesSubscriber
+    tags:
+      - { name: event_subscriber }
+  request_close_subscriber:
+    class: Drupal\Core\EventSubscriber\RequestCloseSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@module_handler']
+  config_import_subscriber:
+    class: Drupal\Core\EventSubscriber\ConfigImportSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@theme_handler', '@extension.list.module']
+  config_snapshot_subscriber:
+    class: Drupal\Core\EventSubscriber\ConfigSnapshotSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@config.manager', '@config.storage', '@config.storage.snapshot']
+  config_exclude_modules_subscriber:
+    class: Drupal\Core\EventSubscriber\ExcludedModulesEventSubscriber
+    arguments: ['@config.storage', '@settings', '@config.manager']
+    tags:
+      - { name: event_subscriber }
+  exception.needs_installer:
+    class: Drupal\Core\EventSubscriber\ExceptionDetectNeedsInstallSubscriber
+    arguments: ['@database']
+    tags:
+      - { name: event_subscriber }
+  exception.default_json:
+    class: Drupal\Core\EventSubscriber\ExceptionJsonSubscriber
+    tags:
+      - { name: event_subscriber }
+  exception.default_html:
+    class: Drupal\Core\EventSubscriber\DefaultExceptionHtmlSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@http_kernel', '@logger.channel.php', '@redirect.destination', '@router.no_access_checks']
+  exception.final:
+    class: Drupal\Core\EventSubscriber\FinalExceptionSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@config.factory']
+  exception.logger:
+    class: Drupal\Core\EventSubscriber\ExceptionLoggingSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@logger.factory']
+  exception.custom_page_html:
+    class: Drupal\Core\EventSubscriber\CustomPageExceptionHtmlSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@config.factory', '@http_kernel', '@logger.channel.php', '@redirect.destination', '@router.no_access_checks', '@access_manager']
+  exception.fast_404_html:
+    class: Drupal\Core\EventSubscriber\Fast404ExceptionHtmlSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@config.factory', '@http_kernel']
+  exception.test_site:
+    class: Drupal\Core\EventSubscriber\ExceptionTestSiteSubscriber
+    tags:
+      - { name: event_subscriber }
+  exception.enforced_form_response:
+    class: Drupal\Core\EventSubscriber\EnforcedFormResponseSubscriber
+    tags:
+      - { name: event_subscriber }
+  route_processor_manager:
+    class: Drupal\Core\RouteProcessor\RouteProcessorManager
+    tags:
+      - { name: service_collector, tag: route_processor_outbound, call: addOutbound }
+  path_processor_manager:
+    class: Drupal\Core\PathProcessor\PathProcessorManager
+    tags:
+      - { name: service_collector, tag: path_processor_inbound, call: addInbound }
+      - { name: service_collector, tag: path_processor_outbound, call: addOutbound }
+  path_processor_decode:
+    class: Drupal\Core\PathProcessor\PathProcessorDecode
+    tags:
+      - { name: path_processor_inbound, priority: 1000 }
+  path_processor_front:
+    class: Drupal\Core\PathProcessor\PathProcessorFront
+    tags:
+      - { name: path_processor_inbound, priority: 200 }
+      - { name: path_processor_outbound, priority: 200 }
+    arguments: ['@config.factory']
+  route_processor_current:
+    class: Drupal\Core\RouteProcessor\RouteProcessorCurrent
+    arguments: ['@current_route_match']
+    tags:
+      - { name: route_processor_outbound, priority: 200 }
+  path_processor_alias:
+    class: Drupal\Core\PathProcessor\PathProcessorAlias
+    arguments: ['@path_alias.manager']
+    deprecated: 'The "%service_id%" service is deprecated. Use "path_alias.path_processor" instead. See https://drupal.org/node/3092086'
+  route_processor_csrf:
+    class: Drupal\Core\Access\RouteProcessorCsrf
+    tags:
+      - { name: route_processor_outbound }
+    arguments: ['@csrf_token']
+  transliteration:
+    class: Drupal\Core\Transliteration\PhpTransliteration
+    arguments: [null, '@module_handler']
+  flood:
+    class: Drupal\Core\Flood\DatabaseBackend
+    arguments: ['@database', '@request_stack']
+    tags:
+      - { name: backend_overridable }
+  plugin.manager.mail:
+    class: Drupal\Core\Mail\MailManager
+    arguments: ['@container.namespaces', '@cache.discovery', '@module_handler', '@config.factory', '@logger.factory', '@string_translation', '@renderer']
+  plugin.manager.condition:
+    class: Drupal\Core\Condition\ConditionManager
+    parent: default_plugin_manager
+  plugin.manager.element_info:
+    class: Drupal\Core\Render\ElementInfoManager
+    arguments: ['@container.namespaces', '@cache.discovery', '@cache_tags.invalidator', '@module_handler', '@theme.manager']
+  stream_wrapper_manager:
+    class: Drupal\Core\StreamWrapper\StreamWrapperManager
+    calls:
+      - [setContainer, ['@service_container']]
+  stream_wrapper.public:
+    class: Drupal\Core\StreamWrapper\PublicStream
+    tags:
+      - { name: stream_wrapper, scheme: public }
+  stream_wrapper.temporary:
+    class: Drupal\Core\StreamWrapper\TemporaryStream
+    tags:
+      - { name: stream_wrapper, scheme: temporary }
+  kernel_destruct_subscriber:
+    class: Drupal\Core\EventSubscriber\KernelDestructionSubscriber
+    tags:
+      - { name: event_subscriber }
+    calls:
+      - [setContainer, ['@service_container']]
+  image.toolkit.manager:
+    class: Drupal\Core\ImageToolkit\ImageToolkitManager
+    arguments: ['@config.factory']
+    parent: default_plugin_manager
+    tags:
+      - { name: plugin_manager_cache_clear }
+  image.toolkit.operation.manager:
+    class: Drupal\Core\ImageToolkit\ImageToolkitOperationManager
+    arguments: ['@logger.channel.image', '@image.toolkit.manager']
+    parent: default_plugin_manager
+    tags:
+      - { name: plugin_manager_cache_clear }
+  image.factory:
+    class: Drupal\Core\Image\ImageFactory
+    arguments: ['@image.toolkit.manager']
+  breadcrumb:
+    class: Drupal\Core\Breadcrumb\BreadcrumbManager
+    arguments: ['@module_handler']
+    tags:
+      - { name: service_collector, tag: breadcrumb_builder, call: addBuilder }
+  token:
+    class: Drupal\Core\Utility\Token
+    arguments: ['@module_handler', '@cache.default', '@language_manager', '@cache_tags.invalidator', '@renderer']
+  batch.storage:
+    class: Drupal\Core\Batch\BatchStorage
+    arguments: ['@database', '@session', '@csrf_token']
+    tags:
+      - { name: backend_overridable }
+    lazy: true
+  country_manager:
+    class: Drupal\Core\Locale\CountryManager
+    arguments: ['@module_handler']
+  date.formatter:
+    class: Drupal\Core\Datetime\DateFormatter
+    arguments: ['@entity_type.manager', '@language_manager', '@string_translation', '@config.factory', '@request_stack']
+  feed.bridge.reader:
+    class: Drupal\Component\Bridge\ZfExtensionManagerSfContainer
+    calls:
+      - [setContainer, ['@service_container']]
+      - [setStandalone, ['\Laminas\Feed\Reader\StandaloneExtensionManager']]
+    arguments: ['feed.reader.']
+  feed.bridge.writer:
+    class: Drupal\Component\Bridge\ZfExtensionManagerSfContainer
+    calls:
+      - [setContainer, ['@service_container']]
+      - [setStandalone, ['\Laminas\Feed\Writer\StandaloneExtensionManager']]
+    arguments: ['feed.writer.']
+# Laminas Feed reader plugins. Plugin instances should not be shared.
+  feed.reader.dublincoreentry:
+    class: Laminas\Feed\Reader\Extension\DublinCore\Entry
+    shared: false
+  feed.reader.dublincorefeed:
+    class: Laminas\Feed\Reader\Extension\DublinCore\Feed
+    shared: false
+  feed.reader.contententry:
+    class: Laminas\Feed\Reader\Extension\Content\Entry
+    shared: false
+  feed.reader.atomentry:
+    class: Laminas\Feed\Reader\Extension\Atom\Entry
+    shared: false
+  feed.reader.atomfeed:
+    class: Laminas\Feed\Reader\Extension\Atom\Feed
+    shared: false
+  feed.reader.slashentry:
+    class: Laminas\Feed\Reader\Extension\Slash\Entry
+    shared: false
+  feed.reader.wellformedwebentry:
+    class: Laminas\Feed\Reader\Extension\WellFormedWeb\Entry
+    shared: false
+  feed.reader.threadentry:
+    class: Laminas\Feed\Reader\Extension\Thread\Entry
+    shared: false
+  feed.reader.podcastentry:
+    class: Laminas\Feed\Reader\Extension\Podcast\Entry
+    shared: false
+  feed.reader.podcastfeed:
+    class: Laminas\Feed\Reader\Extension\Podcast\Feed
+    shared: false
+# Laminas Feed writer plugins. Plugins should be set as prototype scope.
+  feed.writer.atomrendererfeed:
+    class: Laminas\Feed\Writer\Extension\Atom\Renderer\Feed
+    shared: false
+  feed.writer.contentrendererentry:
+    class: Laminas\Feed\Writer\Extension\Content\Renderer\Entry
+    shared: false
+  feed.writer.dublincorerendererentry:
+    class: Laminas\Feed\Writer\Extension\DublinCore\Renderer\Entry
+    shared: false
+  feed.writer.dublincorerendererfeed:
+    class: Laminas\Feed\Writer\Extension\DublinCore\Renderer\Feed
+    shared: false
+  feed.writer.itunesentry:
+    class: Laminas\Feed\Writer\Extension\ITunes\Entry
+    shared: false
+  feed.writer.itunesfeed:
+    class: Laminas\Feed\Writer\Extension\ITunes\Feed
+    shared: false
+  feed.writer.itunesrendererentry:
+    class: Laminas\Feed\Writer\Extension\ITunes\Renderer\Entry
+    shared: false
+  feed.writer.itunesrendererfeed:
+    class: Laminas\Feed\Writer\Extension\ITunes\Renderer\Feed
+    shared: false
+  feed.writer.slashrendererentry:
+    class: Laminas\Feed\Writer\Extension\Slash\Renderer\Entry
+    shared: false
+  feed.writer.threadingrendererentry:
+    class: Laminas\Feed\Writer\Extension\Threading\Renderer\Entry
+    shared: false
+  feed.writer.wellformedwebrendererentry:
+    class: Laminas\Feed\Writer\Extension\WellFormedWeb\Renderer\Entry
+    shared: false
+  theme.manager:
+    class: Drupal\Core\Theme\ThemeManager
+    arguments: ['@app.root', '@theme.negotiator', '@theme.initialization', '@module_handler']
+    calls:
+      - [setThemeRegistry, ['@theme.registry']]
+  theme.initialization:
+    class: Drupal\Core\Theme\ThemeInitialization
+    arguments: ['@app.root', '@theme_handler', '@cache.bootstrap', '@module_handler']
+  theme.registry:
+    class: Drupal\Core\Theme\Registry
+    arguments: ['@app.root', '@cache.default', '@lock', '@module_handler', '@theme_handler', '@theme.initialization', null, '@cache.bootstrap']
+    tags:
+      - { name: needs_destruction }
+    calls:
+      - [setThemeManager, ['@theme.manager']]
+  authentication:
+    class: Drupal\Core\Authentication\AuthenticationManager
+    arguments: ['@authentication_collector']
+  authentication_collector:
+    class: Drupal\Core\Authentication\AuthenticationCollector
+    tags:
+      - { name: service_collector, tag: authentication_provider, call: addProvider }
+  authentication_subscriber:
+    class: Drupal\Core\EventSubscriber\AuthenticationSubscriber
+    arguments: ['@authentication', '@current_user']
+    tags:
+      - { name: event_subscriber }
+  account_switcher:
+    class: Drupal\Core\Session\AccountSwitcher
+    arguments: ['@current_user', '@session_handler.write_safe']
+  user_permissions_hash_generator:
+    class: Drupal\Core\Session\PermissionsHashGenerator
+    arguments: ['@private_key', '@cache.bootstrap', '@cache.static']
+  current_user:
+    class: Drupal\Core\Session\AccountProxy
+    arguments: ['@event_dispatcher']
+  session_configuration:
+    class: Drupal\Core\Session\SessionConfiguration
+    arguments: ['%session.storage.options%']
+  session:
+    class: Symfony\Component\HttpFoundation\Session\Session
+    arguments: ['@session_manager', '@session.attribute_bag', '@session.flash_bag']
+    tags:
+      - { name: service_collector, tag: session_bag, call: registerBag }
+  session.flash_bag:
+    class: Symfony\Component\HttpFoundation\Session\Flash\FlashBag
+    public: false
+  session.attribute_bag:
+    class: Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag
+    public: false
+  session_handler:
+    alias: session_handler.storage
+  session_handler.storage:
+    class: Drupal\Core\Session\SessionHandler
+    arguments: ['@request_stack', '@database']
+    tags:
+      - { name: backend_overridable }
+  session_handler.write_safe:
+    class: Drupal\Core\Session\WriteSafeSessionHandler
+    tags:
+      - { name: session_handler_proxy, priority: 150 }
+  session_manager:
+    class: Drupal\Core\Session\SessionManager
+    arguments: ['@request_stack', '@database', '@session_manager.metadata_bag', '@session_configuration', '@session_handler']
+    tags:
+      - { name: backend_overridable }
+    calls:
+      - [setWriteSafeHandler, ['@session_handler.write_safe']]
+  session_manager.metadata_bag:
+    class: Drupal\Core\Session\MetadataBag
+    arguments: ['@settings']
+  asset.css.collection_renderer:
+    class: Drupal\Core\Asset\CssCollectionRenderer
+    arguments: [ '@state' ]
+  asset.css.collection_optimizer:
+    class: Drupal\Core\Asset\CssCollectionOptimizer
+    arguments: [ '@asset.css.collection_grouper', '@asset.css.optimizer', '@asset.css.dumper', '@state', '@file_system']
+  asset.css.optimizer:
+    class: Drupal\Core\Asset\CssOptimizer
+  asset.css.collection_grouper:
+    class: Drupal\Core\Asset\CssCollectionGrouper
+  asset.css.dumper:
+    class: Drupal\Core\Asset\AssetDumper
+    arguments: ['@file_system']
+  asset.js.collection_renderer:
+    class: Drupal\Core\Asset\JsCollectionRenderer
+    arguments: [ '@state' ]
+  asset.js.collection_optimizer:
+    class: Drupal\Core\Asset\JsCollectionOptimizer
+    arguments: [ '@asset.js.collection_grouper', '@asset.js.optimizer', '@asset.js.dumper', '@state', '@file_system']
+  asset.js.optimizer:
+    class: Drupal\Core\Asset\JsOptimizer
+  asset.js.collection_grouper:
+    class: Drupal\Core\Asset\JsCollectionGrouper
+  asset.js.dumper:
+    class: Drupal\Core\Asset\AssetDumper
+    arguments: ['@file_system']
+  library.discovery:
+    class: Drupal\Core\Asset\LibraryDiscovery
+    arguments: ['@library.discovery.collector']
+  library.discovery.collector:
+    class: Drupal\Core\Asset\LibraryDiscoveryCollector
+    arguments: ['@cache.discovery', '@lock', '@library.discovery.parser', '@theme.manager']
+    tags:
+      - { name: needs_destruction }
+  library.discovery.parser:
+    class: Drupal\Core\Asset\LibraryDiscoveryParser
+    arguments: ['@app.root', '@module_handler', '@theme.manager', '@stream_wrapper_manager', '@library.libraries_directory_file_finder']
+  library.libraries_directory_file_finder:
+    class: Drupal\Core\Asset\LibrariesDirectoryFileFinder
+    arguments: ['@app.root', '@site.path', '@extension.list.profile', '%install_profile%']
+  library.dependency_resolver:
+    class: Drupal\Core\Asset\LibraryDependencyResolver
+    arguments: ['@library.discovery']
+  asset.resolver:
+    class: Drupal\Core\Asset\AssetResolver
+    arguments: ['@library.discovery', '@library.dependency_resolver', '@module_handler', '@theme.manager', '@language_manager', '@cache.data']
+  info_parser:
+    class: Drupal\Core\Extension\InfoParser
+    arguments: ['@app.root']
+  twig:
+    class: Drupal\Core\Template\TwigEnvironment
+    arguments: ['@app.root', '@cache.default', '%twig_extension_hash%', '@state', '@twig.loader', '%twig.config%']
+    tags:
+      - { name: service_collector, tag: 'twig.extension', call: addExtension }
+  twig.extension:
+    class: Drupal\Core\Template\TwigExtension
+    arguments: ['@renderer', '@url_generator', '@theme.manager', '@date.formatter']
+    tags:
+      - { name: twig.extension, priority: 100 }
+  # @todo Figure out what to do about debugging functions.
+  # @see https://www.drupal.org/node/1804998
+  twig.extension.debug:
+    class: Twig_Extension_Debug
+    tags:
+      - { name: twig.extension }
+  twig.loader:
+    class: Twig_Loader_Chain
+    public: false
+    tags:
+      - { name: service_collector, tag: twig.loader, call: addLoader, required: TRUE }
+  twig.loader.filesystem:
+    class: Drupal\Core\Template\Loader\FilesystemLoader
+    # We use '.' instead of '@app.root' as the path for non-namespaced template
+    # files so that they match the relative paths of templates loaded via the
+    # theme registry or via Twig namespaces.
+    arguments: ['.', '@module_handler', '@theme_handler']
+    tags:
+      - { name: twig.loader, priority: 100 }
+  twig.loader.theme_registry:
+    class: Drupal\Core\Template\Loader\ThemeRegistryLoader
+    arguments: ['@theme.registry']
+    tags:
+      - { name: twig.loader, priority: 0 }
+  twig.loader.string:
+    class: Drupal\Core\Template\Loader\StringLoader
+    tags:
+      - { name: twig.loader, priority: -100 }
+  element_info:
+    alias: plugin.manager.element_info
+  file.htaccess_writer:
+    class: Drupal\Core\File\HtaccessWriter
+    arguments: ['@logger.channel.security', '@stream_wrapper_manager']
+  file.mime_type.guesser:
+    class: Drupal\Core\File\MimeType\MimeTypeGuesser
+    arguments: ['@stream_wrapper_manager']
+    tags:
+      - { name: service_collector, tag: mime_type_guesser, call: addGuesser }
+    lazy: true
+  file.mime_type.guesser.extension:
+    class: Drupal\Core\File\MimeType\ExtensionMimeTypeGuesser
+    arguments: ['@module_handler']
+    tags:
+      - { name: mime_type_guesser }
+    lazy: true
+  # Currently needs to be public as it is called by
+  # \Drupal\Core\Render\Element\StatusMessages.
+  # @todo Consider making this service private again after
+  #   https://www.drupal.org/node/2367555 lands.
+  render_placeholder_generator:
+    class: Drupal\Core\Render\PlaceholderGenerator
+    arguments: ['%renderer.config%']
+  render_cache:
+    class: Drupal\Core\Render\PlaceholderingRenderCache
+    arguments: ['@request_stack', '@cache_factory', '@cache_contexts_manager', '@render_placeholder_generator']
+  renderer:
+    class: Drupal\Core\Render\Renderer
+    arguments: ['@controller_resolver', '@theme.manager', '@plugin.manager.element_info', '@render_placeholder_generator', '@render_cache', '@request_stack', '%renderer.config%']
+  early_rendering_controller_wrapper_subscriber:
+    class: Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber
+    arguments: ['@http_kernel.controller.argument_resolver', '@renderer']
+    tags:
+      - { name: event_subscriber }
+  # Placeholder strategies for rendering placeholders.
+  html_response.placeholder_strategy_subscriber:
+    class: Drupal\Core\EventSubscriber\HtmlResponsePlaceholderStrategySubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@placeholder_strategy']
+  placeholder_strategy:
+    class: Drupal\Core\Render\Placeholder\ChainedPlaceholderStrategy
+    tags:
+      - { name: service_collector, tag: placeholder_strategy, call: addPlaceholderStrategy }
+  placeholder_strategy.single_flush:
+    class: Drupal\Core\Render\Placeholder\SingleFlushStrategy
+    tags:
+      - { name: placeholder_strategy, priority: -1000 }
+  email.validator:
+    class: Drupal\Component\Utility\EmailValidator
+  update.post_update_registry:
+    class: Drupal\Core\Update\UpdateRegistry
+    factory: ['@update.post_update_registry_factory', create]
+  update.post_update_registry_factory:
+    class: Drupal\Core\Update\UpdateRegistryFactory
+    parent: container.trait
+  uuid:
+    class: Drupal\Component\Uuid\Php
+  response_filter.active_link:
+    class: Drupal\Core\EventSubscriber\ActiveLinkResponseFilter
+    arguments: ['@current_user', '@path.current', '@path.matcher', '@language_manager']
+    tags:
+      - { name: event_subscriber }
+  response_filter.rss.relative_url:
+    class: Drupal\Core\EventSubscriber\RssResponseRelativeUrlFilter
+    tags:
+      - { name: event_subscriber }
+  messenger:
+    class: Drupal\Core\Messenger\Messenger
+    arguments: ['@session.flash_bag', '@page_cache_kill_switch']
+  tempstore.private:
+    class: Drupal\Core\TempStore\PrivateTempStoreFactory
+    arguments: ['@keyvalue.expirable', '@lock', '@current_user', '@request_stack', '%tempstore.expire%']
+    tags:
+      - { name: backend_overridable }
+  tempstore.shared:
+    class: Drupal\Core\TempStore\SharedTempStoreFactory
+    arguments: ['@keyvalue.expirable', '@lock', '@request_stack', '%tempstore.expire%']
+    tags:
+      - { name: backend_overridable }
+  pager.manager:
+    class: Drupal\Core\Pager\PagerManager
+    arguments: ['@pager.parameters']
+  pager.parameters:
+    class: Drupal\Core\Pager\PagerParameters
+    arguments: ['@request_stack']

+ 57 - 0
web/core/drupalci.yml

@@ -0,0 +1,57 @@
+# This is the DrupalCI testbot build file for Drupal core.
+# Learn to make one for your own drupal.org project:
+# https://www.drupal.org/drupalorg/docs/drupal-ci/customizing-drupalci-testing
+build:
+  assessment:
+    validate_codebase:
+      phplint:
+      csslint:
+        halt-on-fail: false
+      eslint:
+        # A test must pass eslinting standards check in order to continue processing.
+        halt-on-fail: false
+      phpcs:
+        # phpcs will use core's specified version of Coder.
+        sniff-all-files: false
+        halt-on-fail: false
+    testing:
+      # run_tests task is executed several times in order of performance speeds.
+      # halt-on-fail can be set on the run_tests tasks in order to fail fast.
+      # suppress-deprecations is false in order to be alerted to usages of
+      # deprecated code.
+      run_tests.phpunit:
+        types: 'PHPUnit-Unit'
+        testgroups: '--all'
+        suppress-deprecations: false
+        halt-on-fail: false
+      run_tests.kernel:
+        types: 'PHPUnit-Kernel'
+        testgroups: '--all'
+        suppress-deprecations: false
+        halt-on-fail: false
+      run_tests.simpletest:
+         types: 'Simpletest'
+         testgroups: '--all'
+         suppress-deprecations: false
+         halt-on-fail: false
+      run_tests.build:
+        # Limit concurrency due to disk space concerns.
+        concurrency: 15
+        types: 'PHPUnit-Build'
+        testgroups: '--all'
+        suppress-deprecations: false
+        halt-on-fail: false
+      run_tests.functional:
+        types: 'PHPUnit-Functional'
+        testgroups: '--all'
+        suppress-deprecations: false
+        halt-on-fail: false
+      run_tests.javascript:
+        concurrency: 15
+        types: 'PHPUnit-FunctionalJavascript'
+        testgroups: '--all'
+        suppress-deprecations: false
+        halt-on-fail: false
+      # Run nightwatch testing.
+      # @see https://www.drupal.org/project/drupal/issues/2869825
+      nightwatchjs:

+ 135 - 0
web/core/globals.api.php

@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * @file
+ * These are the global variables that Drupal uses.
+ */
+
+use Drupal\Component\Utility\DeprecatedArray;
+
+/**
+ * The insecure base URL of the Drupal installation.
+ *
+ * @see \Drupal\Core\DrupalKernel::initializeRequestGlobals()
+ */
+global $base_insecure_url;
+
+/**
+ * The base path of the Drupal installation.
+ *
+ * This will at least default to '/'.
+ *
+ * @see \Drupal\Core\DrupalKernel::initializeRequestGlobals()
+ */
+global $base_path;
+
+/**
+ * The root URL of the host, excluding the path.
+ *
+ * @see \Drupal\Core\DrupalKernel::initializeRequestGlobals()
+ */
+global $base_root;
+
+/**
+ * The secure base URL of the Drupal installation.
+ *
+ * @see \Drupal\Core\DrupalKernel::initializeRequestGlobals()
+ */
+global $base_secure_url;
+
+/**
+ * The base URL of the Drupal installation.
+ *
+ * @see \Drupal\Core\DrupalKernel::initializeRequestGlobals()
+ */
+global $base_url;
+
+/**
+ * Allows defining of site-specific service providers for the Drupal kernel.
+ *
+ * To define a site-specific service provider class, use code like this:
+ * @code
+ * $GLOBALS['conf']['container_service_providers']['MyClassName'] = 'Drupal\My\Namespace\MyClassName';
+ * @endcode
+ *
+ * @see \Drupal\Core\DrupalKernel::$serviceProviderClasses
+ */
+global $conf;
+
+/**
+ * Array of configuration overrides from the settings.php file.
+ */
+global $config;
+
+/**
+ * The location of file system directories used for site configuration data.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Site\Settings::get('config_sync_directory') instead.
+ *
+ * @see https://www.drupal.org/node/3018145
+ */
+global $config_directories;
+
+/**
+ * Store settings and profile information during installation process.
+ *
+ * @see install_drupal()
+ */
+global $install_state;
+
+/**
+ * Array of the number of items per page for each pager.
+ *
+ * The array index is the pager element index (0 by default).
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Do not
+ *   directly set or get values from this array. Use the pager.manager service
+ *   instead.
+ *
+ * @see https://www.drupal.org/node/2779457
+ * @see \Drupal\Core\Pager\PagerManagerInterface
+ */
+$GLOBALS['pager_limits'] = new DeprecatedArray([], 'Global variable $pager_limits is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface instead. See https://www.drupal.org/node/2779457');
+
+/**
+ * Array of current page numbers for each pager.
+ *
+ * The array index is the pager element index (0 by default).
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Do not
+ *   directly set or get values from this array. Use the pager.manager service
+ *   instead.
+ *
+ * @see https://www.drupal.org/node/2779457
+ * @see \Drupal\Core\Pager\PagerManagerInterface
+ */
+$GLOBALS['pager_page_array'] = new DeprecatedArray([], 'Global variable $pager_page_array is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface instead. See https://www.drupal.org/node/2779457');
+
+/**
+ * Array of the total number of pages for each pager.
+ *
+ * The array index is the pager element index (0 by default).
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Do not
+ *   directly set or get values from this array. Use the pager.manager service
+ *   instead.
+ *
+ * @see https://www.drupal.org/node/2779457
+ * @see \Drupal\Core\Pager\PagerManagerInterface
+ */
+$GLOBALS['pager_total'] = new DeprecatedArray([], 'Global variable $pager_total is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface instead. See https://www.drupal.org/node/2779457');
+
+/**
+ * Array of the total number of items for each pager.
+ *
+ * The array index is the pager element index (0 by default).
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Do not
+ *   directly set or get values from this array. Use the pager.manager service
+ *   instead.
+ *
+ * @see https://www.drupal.org/node/2779457
+ * @see \Drupal\Core\Pager\PagerManagerInterface
+ */
+$GLOBALS['pager_total_items'] = new DeprecatedArray([], 'Global variable $pager_total_items is deprecated in drupal:8.8.0 and is removed in drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface instead. See https://www.drupal.org/node/2779457');

+ 557 - 0
web/core/includes/batch.inc

@@ -0,0 +1,557 @@
+<?php
+
+/**
+ * @file
+ * Batch processing API for processes to run in multiple HTTP requests.
+ *
+ * Note that batches are usually invoked by form submissions, which is
+ * why the core interaction functions of the batch processing API live in
+ * form.inc.
+ *
+ * @see form.inc
+ * @see batch_set()
+ * @see batch_process()
+ * @see batch_get()
+ */
+
+use Drupal\Component\Utility\Timer;
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Batch\Percentage;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Url;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+/**
+ * Renders the batch processing page based on the current state of the batch.
+ *
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ *   The current request object.
+ *
+ * @see _batch_shutdown()
+ */
+function _batch_page(Request $request) {
+  $batch = &batch_get();
+
+  if (!($request_id = $request->query->get('id'))) {
+    return FALSE;
+  }
+
+  // Retrieve the current state of the batch.
+  if (!$batch) {
+    $batch = \Drupal::service('batch.storage')->load($request_id);
+    if (!$batch) {
+      \Drupal::messenger()->addError(t('No active batch.'));
+      return new RedirectResponse(Url::fromRoute('<front>', [], ['absolute' => TRUE])->toString());
+    }
+  }
+
+  // We need to store the updated batch information in the batch storage after
+  // processing the batch. In order for the error page to work correctly this
+  // needs to be done even in case of a PHP fatal error in which case the end of
+  // this function is never reached. Therefore we register a shutdown function
+  // to handle this case. Because with FastCGI and fastcgi_finish_request()
+  // shutdown functions are called after the HTTP connection is closed, updating
+  // the batch information in a shutdown function would lead to race conditions
+  // between consecutive requests if the batch processing continues. In case of
+  // a fatal error the processing stops anyway, so it works even with FastCGI.
+  // However, we must ensure to only update in the shutdown phase in this
+  // particular case we track whether the batch information still needs to be
+  // updated.
+  // @see _batch_shutdown()
+  // @see \Symfony\Component\HttpFoundation\Response::send()
+  drupal_register_shutdown_function('_batch_shutdown');
+  _batch_needs_update(TRUE);
+
+  $build = [];
+
+  // Add batch-specific libraries.
+  foreach ($batch['sets'] as $batch_set) {
+    if (isset($batch_set['library'])) {
+      foreach ($batch_set['library'] as $library) {
+        $build['#attached']['library'][] = $library;
+      }
+    }
+  }
+
+  $op = $request->query->get('op', '');
+  switch ($op) {
+    case 'start':
+    case 'do_nojs':
+      // Display the full progress page on startup and on each additional
+      // non-JavaScript iteration.
+      $current_set = _batch_current_set();
+      $build['#title'] = $current_set['title'];
+      $build['content'] = _batch_progress_page();
+
+      $response = $build;
+      break;
+
+    case 'do':
+      // JavaScript-based progress page callback.
+      $response = _batch_do();
+      break;
+
+    case 'finished':
+      // _batch_finished() returns a RedirectResponse.
+      $response = _batch_finished();
+      break;
+  }
+
+  if ($batch) {
+    \Drupal::service('batch.storage')->update($batch);
+  }
+  _batch_needs_update(FALSE);
+
+  return $response;
+}
+
+/**
+ * Checks whether the batch information needs to be updated in the storage.
+ *
+ * @param bool $new_value
+ *   (optional) A new value to set.
+ *
+ * @return bool
+ *   TRUE if the batch information needs to be updated; FALSE otherwise.
+ */
+function _batch_needs_update($new_value = NULL) {
+  $needs_update = &drupal_static(__FUNCTION__, FALSE);
+
+  if (isset($new_value)) {
+    $needs_update = $new_value;
+  }
+
+  return $needs_update;
+}
+
+/**
+ * Does one execution pass with JavaScript and returns progress to the browser.
+ *
+ * @see _batch_progress_page_js()
+ * @see _batch_process()
+ */
+function _batch_do() {
+  // Perform actual processing.
+  list($percentage, $message, $label) = _batch_process();
+
+  return new JsonResponse(['status' => TRUE, 'percentage' => $percentage, 'message' => $message, 'label' => $label]);
+}
+
+/**
+ * Outputs a batch processing page.
+ *
+ * @see _batch_process()
+ */
+function _batch_progress_page() {
+  $batch = &batch_get();
+
+  $current_set = _batch_current_set();
+
+  $new_op = 'do_nojs';
+
+  if (!isset($batch['running'])) {
+    // This is the first page so we return some output immediately.
+    $percentage       = 0;
+    $message          = $current_set['init_message'];
+    $label            = '';
+    $batch['running'] = TRUE;
+  }
+  else {
+    // This is one of the later requests; do some processing first.
+
+    // Error handling: if PHP dies due to a fatal error (e.g. a nonexistent
+    // function), it will output whatever is in the output buffer, followed by
+    // the error message.
+    ob_start();
+    $fallback = $current_set['error_message'] . '<br />' . $batch['error_message'];
+
+    // We strip the end of the page using a marker in the template, so any
+    // additional HTML output by PHP shows up inside the page rather than below
+    // it. While this causes invalid HTML, the same would be true if we didn't,
+    // as content is not allowed to appear after </html> anyway.
+    $bare_html_page_renderer = \Drupal::service('bare_html_page_renderer');
+    $response = $bare_html_page_renderer->renderBarePage(['#markup' => $fallback], $current_set['title'], 'maintenance_page', [
+      '#show_messages' => FALSE,
+    ]);
+
+    // Just use the content of the response.
+    $fallback = $response->getContent();
+
+    list($fallback) = explode('<!--partial-->', $fallback);
+    print $fallback;
+
+    // Perform actual processing.
+    list($percentage, $message, $label) = _batch_process($batch);
+    if ($percentage == 100) {
+      $new_op = 'finished';
+    }
+
+    // PHP did not die; remove the fallback output.
+    ob_end_clean();
+  }
+
+  // Merge required query parameters for batch processing into those provided by
+  // batch_set() or hook_batch_alter().
+  $query_options = $batch['url']->getOption('query');
+  $query_options['id'] = $batch['id'];
+  $query_options['op'] = $new_op;
+  $batch['url']->setOption('query', $query_options);
+
+  $url = $batch['url']->toString(TRUE)->getGeneratedUrl();
+
+  $build = [
+    '#theme' => 'progress_bar',
+    '#percent' => $percentage,
+    '#message' => ['#markup' => $message],
+    '#label' => $label,
+    '#attached' => [
+      'html_head' => [
+        [
+          [
+            // Redirect through a 'Refresh' meta tag if JavaScript is disabled.
+            '#tag' => 'meta',
+            '#noscript' => TRUE,
+            '#attributes' => [
+              'http-equiv' => 'Refresh',
+              'content' => '0; URL=' . $url,
+            ],
+          ],
+          'batch_progress_meta_refresh',
+        ],
+      ],
+      // Adds JavaScript code and settings for clients where JavaScript is enabled.
+      'drupalSettings' => [
+        'batch' => [
+          'errorMessage' => $current_set['error_message'] . '<br />' . $batch['error_message'],
+          'initMessage' => $current_set['init_message'],
+          'uri' => $url,
+        ],
+      ],
+      'library' => [
+        'core/drupal.batch',
+      ],
+    ],
+  ];
+  return $build;
+}
+
+/**
+ * Processes sets in a batch.
+ *
+ * If the batch was marked for progressive execution (default), this executes as
+ * many operations in batch sets until an execution time of 1 second has been
+ * exceeded. It will continue with the next operation of the same batch set in
+ * the next request.
+ *
+ * @return array
+ *   An array containing a completion value (in percent) and a status message.
+ */
+function _batch_process() {
+  $batch       = &batch_get();
+  $current_set = &_batch_current_set();
+  // Indicate that this batch set needs to be initialized.
+  $set_changed = TRUE;
+  $task_message = '';
+
+  // If this batch was marked for progressive execution (e.g. forms submitted by
+  // \Drupal::formBuilder()->submitForm(), initialize a timer to determine
+  // whether we need to proceed with the same batch phase when a processing time
+  // of 1 second has been exceeded.
+  if ($batch['progressive']) {
+    Timer::start('batch_processing');
+  }
+
+  if (empty($current_set['start'])) {
+    $current_set['start'] = microtime(TRUE);
+  }
+
+  $queue = _batch_queue($current_set);
+
+  while (!$current_set['success']) {
+    // If this is the first time we iterate this batch set in the current
+    // request, we check if it requires an additional file for functions
+    // definitions.
+    if ($set_changed && isset($current_set['file']) && is_file($current_set['file'])) {
+      include_once \Drupal::root() . '/' . $current_set['file'];
+    }
+
+    $task_message = '';
+    // Assume a single pass operation and set the completion level to 1 by
+    // default.
+    $finished = 1;
+
+    if ($item = $queue->claimItem()) {
+      list($callback, $args) = $item->data;
+
+      // Build the 'context' array and execute the function call.
+      $batch_context = [
+        'sandbox'  => &$current_set['sandbox'],
+        'results'  => &$current_set['results'],
+        'finished' => &$finished,
+        'message'  => &$task_message,
+      ];
+      call_user_func_array($callback, array_merge($args, [&$batch_context]));
+
+      if ($finished >= 1) {
+        // Make sure this step is not counted twice when computing $current.
+        $finished = 0;
+        // Remove the processed operation and clear the sandbox.
+        $queue->deleteItem($item);
+        $current_set['count']--;
+        $current_set['sandbox'] = [];
+      }
+    }
+
+    // When all operations in the current batch set are completed, browse
+    // through the remaining sets, marking them 'successfully processed'
+    // along the way, until we find a set that contains operations.
+    // _batch_next_set() executes form submit handlers stored in 'control'
+    // sets (see \Drupal::service('form_submitter')), which can in turn add new
+    // sets to the batch.
+    $set_changed = FALSE;
+    $old_set = $current_set;
+    while (empty($current_set['count']) && ($current_set['success'] = TRUE) && _batch_next_set()) {
+      $current_set = &_batch_current_set();
+      $current_set['start'] = microtime(TRUE);
+      $set_changed = TRUE;
+    }
+
+    // At this point, either $current_set contains operations that need to be
+    // processed or all sets have been completed.
+    $queue = _batch_queue($current_set);
+
+    // If we are in progressive mode, break processing after 1 second.
+    if ($batch['progressive'] && Timer::read('batch_processing') > 1000) {
+      // Record elapsed wall clock time.
+      $current_set['elapsed'] = round((microtime(TRUE) - $current_set['start']) * 1000, 2);
+      break;
+    }
+  }
+
+  if ($batch['progressive']) {
+    // Gather progress information.
+
+    // Reporting 100% progress will cause the whole batch to be considered
+    // processed. If processing was paused right after moving to a new set,
+    // we have to use the info from the new (unprocessed) set.
+    if ($set_changed && isset($current_set['queue'])) {
+      // Processing will continue with a fresh batch set.
+      $remaining        = $current_set['count'];
+      $total            = $current_set['total'];
+      $progress_message = $current_set['init_message'];
+      $task_message     = '';
+    }
+    else {
+      // Processing will continue with the current batch set.
+      $remaining        = $old_set['count'];
+      $total            = $old_set['total'];
+      $progress_message = $old_set['progress_message'];
+    }
+
+    // Total progress is the number of operations that have fully run plus the
+    // completion level of the current operation.
+    $current    = $total - $remaining + $finished;
+    $percentage = _batch_api_percentage($total, $current);
+    $elapsed    = isset($current_set['elapsed']) ? $current_set['elapsed'] : 0;
+    $values     = [
+      '@remaining'  => $remaining,
+      '@total'      => $total,
+      '@current'    => floor($current),
+      '@percentage' => $percentage,
+      '@elapsed'    => \Drupal::service('date.formatter')->formatInterval($elapsed / 1000),
+      // If possible, estimate remaining processing time.
+      '@estimate'   => ($current > 0) ? \Drupal::service('date.formatter')->formatInterval(($elapsed * ($total - $current) / $current) / 1000) : '-',
+    ];
+    $message    = strtr($progress_message, $values);
+
+    return [$percentage, $message, $task_message];
+  }
+  else {
+    // If we are not in progressive mode, the entire batch has been processed.
+    return _batch_finished();
+  }
+}
+
+/**
+ * Formats the percent completion for a batch set.
+ *
+ * @param int $total
+ *   The total number of operations.
+ * @param int|float $current
+ *   The number of the current operation. This may be a floating point number
+ *   rather than an integer in the case of a multi-step operation that is not
+ *   yet complete; in that case, the fractional part of $current represents the
+ *   fraction of the operation that has been completed.
+ *
+ * @return string
+ *   The properly formatted percentage, as a string. We output percentages
+ *   using the correct number of decimal places so that we never print "100%"
+ *   until we are finished, but we also never print more decimal places than
+ *   are meaningful.
+ *
+ * @see _batch_process()
+ */
+function _batch_api_percentage($total, $current) {
+  return Percentage::format($total, $current);
+}
+
+/**
+ * Returns the batch set being currently processed.
+ */
+function &_batch_current_set() {
+  $batch = &batch_get();
+  return $batch['sets'][$batch['current_set']];
+}
+
+/**
+ * Retrieves the next set in a batch.
+ *
+ * If there is a subsequent set in this batch, assign it as the new set to
+ * process and execute its form submit handler (if defined), which may add
+ * further sets to this batch.
+ *
+ * @return true|null
+ *   TRUE if a subsequent set was found in the batch; no value will be returned
+ *   if no subsequent set was found.
+ */
+function _batch_next_set() {
+  $batch = &batch_get();
+  $set_indexes = array_keys($batch['sets']);
+  $current_set_index_key = array_search($batch['current_set'], $set_indexes);
+  if (isset($set_indexes[$current_set_index_key + 1])) {
+    $batch['current_set'] = $set_indexes[$current_set_index_key + 1];
+    $current_set = &_batch_current_set();
+    if (isset($current_set['form_submit']) && ($callback = $current_set['form_submit']) && is_callable($callback)) {
+      // We use our stored copies of $form and $form_state to account for
+      // possible alterations by previous form submit handlers.
+      $complete_form = &$batch['form_state']->getCompleteForm();
+      call_user_func_array($callback, [&$complete_form, &$batch['form_state']]);
+    }
+    return TRUE;
+  }
+}
+
+/**
+ * Ends the batch processing.
+ *
+ * Call the 'finished' callback of each batch set to allow custom handling of
+ * the results and resolve page redirection.
+ */
+function _batch_finished() {
+  $batch = &batch_get();
+  $batch_finished_redirect = NULL;
+
+  // Execute the 'finished' callbacks for each batch set, if defined.
+  foreach ($batch['sets'] as $batch_set) {
+    if (isset($batch_set['finished'])) {
+      // Check if the set requires an additional file for function definitions.
+      if (isset($batch_set['file']) && is_file($batch_set['file'])) {
+        include_once \Drupal::root() . '/' . $batch_set['file'];
+      }
+      if (is_callable($batch_set['finished'])) {
+        $queue = _batch_queue($batch_set);
+        $operations = $queue->getAllItems();
+        $batch_set_result = call_user_func_array($batch_set['finished'], [$batch_set['success'], $batch_set['results'], $operations, \Drupal::service('date.formatter')->formatInterval($batch_set['elapsed'] / 1000)]);
+        // If a batch 'finished' callback requested a redirect after the batch
+        // is complete, save that for later use. If more than one batch set
+        // returned a redirect, the last one is used.
+        if ($batch_set_result instanceof RedirectResponse) {
+          $batch_finished_redirect = $batch_set_result;
+        }
+      }
+    }
+  }
+
+  // Clean up the batch table and unset the static $batch variable.
+  if ($batch['progressive']) {
+    \Drupal::service('batch.storage')->delete($batch['id']);
+    foreach ($batch['sets'] as $batch_set) {
+      if ($queue = _batch_queue($batch_set)) {
+        $queue->deleteQueue();
+      }
+    }
+    // Clean-up the session. Not needed for CLI updates.
+    if (isset($_SESSION)) {
+      unset($_SESSION['batches'][$batch['id']]);
+      if (empty($_SESSION['batches'])) {
+        unset($_SESSION['batches']);
+      }
+    }
+  }
+  $_batch = $batch;
+  $batch = NULL;
+
+  // Redirect if needed.
+  if ($_batch['progressive']) {
+    // Revert the 'destination' that was saved in batch_process().
+    if (isset($_batch['destination'])) {
+      \Drupal::request()->query->set('destination', $_batch['destination']);
+    }
+
+    // Determine the target path to redirect to. If a batch 'finished' callback
+    // returned a redirect response object, use that. Otherwise, fall back on
+    // the form redirection.
+    if (isset($batch_finished_redirect)) {
+      return $batch_finished_redirect;
+    }
+    elseif (!isset($_batch['form_state'])) {
+      $_batch['form_state'] = new FormState();
+    }
+    if ($_batch['form_state']->getRedirect() === NULL) {
+      $redirect = $_batch['batch_redirect'] ?: $_batch['source_url'];
+      // Any path with a scheme does not correspond to a route.
+      if (!$redirect instanceof Url) {
+        $options = UrlHelper::parse($redirect);
+        if (parse_url($options['path'], PHP_URL_SCHEME)) {
+          $redirect = Url::fromUri($options['path'], $options);
+        }
+        else {
+          $redirect = \Drupal::pathValidator()->getUrlIfValid($options['path']);
+          if (!$redirect) {
+            // Stay on the same page if the redirect was invalid.
+            $redirect = Url::fromRoute('<current>');
+          }
+          $redirect->setOptions($options);
+        }
+      }
+      $_batch['form_state']->setRedirectUrl($redirect);
+    }
+
+    // Use \Drupal\Core\Form\FormSubmitterInterface::redirectForm() to handle
+    // the redirection logic.
+    $redirect = \Drupal::service('form_submitter')->redirectForm($_batch['form_state']);
+    if (is_object($redirect)) {
+      return $redirect;
+    }
+
+    // If no redirection happened, redirect to the originating page. In case the
+    // form needs to be rebuilt, save the final $form_state for
+    // \Drupal\Core\Form\FormBuilderInterface::buildForm().
+    if ($_batch['form_state']->isRebuilding()) {
+      $_SESSION['batch_form_state'] = $_batch['form_state'];
+    }
+    $callback = $_batch['redirect_callback'];
+    $_batch['source_url']->mergeOptions(['query' => ['op' => 'finish', 'id' => $_batch['id']]]);
+    if (is_callable($callback)) {
+      $callback($_batch['source_url'], $_batch['source_url']->getOption('query'));
+    }
+    elseif ($callback === NULL) {
+      // Default to RedirectResponse objects when nothing specified.
+      return new RedirectResponse($_batch['source_url']->setAbsolute()->toString());
+    }
+  }
+}
+
+/**
+ * Shutdown function: Stores the current batch data for the next request.
+ *
+ * @see _batch_page()
+ * @see drupal_register_shutdown_function()
+ */
+function _batch_shutdown() {
+  if (($batch = batch_get()) && _batch_needs_update()) {
+    \Drupal::service('batch.storage')->update($batch);
+  }
+}

File diff suppressed because it is too large
+ 795 - 0
web/core/includes/bootstrap.inc


+ 1255 - 0
web/core/includes/common.inc

@@ -0,0 +1,1255 @@
+<?php
+
+/**
+ * @file
+ * Common functions that many Drupal modules will need to reference.
+ *
+ * The functions that are critical and need to be available even when serving
+ * a cached page are instead located in bootstrap.inc.
+ */
+
+use Drupal\Component\Gettext\PoItem;
+use Drupal\Component\Utility\Bytes;
+use Drupal\Component\Utility\Environment;
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\SortArray;
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Form\FormHelper;
+use Drupal\Core\Render\Element;
+use Drupal\Core\Render\Element\Link;
+use Drupal\Core\Render\HtmlResponseAttachmentsProcessor;
+use Drupal\Core\Render\Markup;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
+/**
+ * @defgroup php_wrappers PHP wrapper functions
+ * @{
+ * Functions that are wrappers or custom implementations of PHP functions.
+ *
+ * Certain PHP functions should not be used in Drupal. Instead, Drupal's
+ * replacement functions should be used.
+ *
+ * For example, for improved or more secure UTF8-handling, or RFC-compliant
+ * handling of URLs in Drupal.
+ *
+ * For ease of use and memorizing, all these wrapper functions use the same name
+ * as the original PHP function, but prefixed with "drupal_". Beware, however,
+ * that not all wrapper functions support the same arguments as the original
+ * functions.
+ *
+ * You should always use these wrapper functions in your code.
+ *
+ * Wrong:
+ * @code
+ *   $my_substring = substr($original_string, 0, 5);
+ * @endcode
+ *
+ * Correct:
+ * @code
+ *   $my_substring = mb_substr($original_string, 0, 5);
+ * @endcode
+ *
+ * @}
+ */
+
+/**
+ * Return status for saving which involved creating a new item.
+ */
+const SAVED_NEW = 1;
+
+/**
+ * Return status for saving which involved an update to an existing item.
+ */
+const SAVED_UPDATED = 2;
+
+/**
+ * Return status for saving which deleted an existing item.
+ */
+const SAVED_DELETED = 3;
+
+/**
+ * The default aggregation group for CSS files added to the page.
+ */
+const CSS_AGGREGATE_DEFAULT = 0;
+
+/**
+ * The default aggregation group for theme CSS files added to the page.
+ */
+const CSS_AGGREGATE_THEME = 100;
+
+/**
+ * The default weight for CSS rules that style HTML elements ("base" styles).
+ */
+const CSS_BASE = -200;
+
+/**
+ * The default weight for CSS rules that layout a page.
+ */
+const CSS_LAYOUT = -100;
+
+/**
+ * The default weight for CSS rules that style design components (and their associated states and themes.)
+ */
+const CSS_COMPONENT = 0;
+
+/**
+ * The default weight for CSS rules that style states and are not included with components.
+ */
+const CSS_STATE = 100;
+
+/**
+ * The default weight for CSS rules that style themes and are not included with components.
+ */
+const CSS_THEME = 200;
+
+/**
+ * The default group for JavaScript settings added to the page.
+ */
+const JS_SETTING = -200;
+
+/**
+ * The default group for JavaScript and jQuery libraries added to the page.
+ */
+const JS_LIBRARY = -100;
+
+/**
+ * The default group for module JavaScript code added to the page.
+ */
+const JS_DEFAULT = 0;
+
+/**
+ * The default group for theme JavaScript code added to the page.
+ */
+const JS_THEME = 100;
+
+/**
+ * The delimiter used to split plural strings.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use Drupal\Component\Gettext\PoItem::DELIMITER instead.
+ */
+const LOCALE_PLURAL_DELIMITER = PoItem::DELIMITER;
+
+/**
+ * Prepares a 'destination' URL query parameter.
+ *
+ * Used to direct the user back to the referring page after completing a form.
+ * By default the current URL is returned. If a destination exists in the
+ * previous request, that destination is returned. As such, a destination can
+ * persist across multiple pages.
+ *
+ * @return array
+ *   An associative array containing the key:
+ *   - destination: The value of the current request's 'destination' query
+ *     parameter, if present. This can be either a relative or absolute URL.
+ *     However, for security, redirection to external URLs is not performed.
+ *     If the query parameter isn't present, then the URL of the current
+ *     request is returned.
+ *
+ * @ingroup form_api
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use the redirect.destination service.
+ *
+ * @see \Drupal\Core\EventSubscriber\RedirectResponseSubscriber::checkRedirectUrl()
+ * @see https://www.drupal.org/node/2448603
+ */
+function drupal_get_destination() {
+  return \Drupal::destination()->getAsArray();
+}
+
+/**
+ * @defgroup validation Input validation
+ * @{
+ * Functions to validate user input.
+ */
+
+/**
+ * Verifies the syntax of the given email address.
+ *
+ * @param string $mail
+ *   A string containing an email address.
+ *
+ * @return bool
+ *   TRUE if the address is in a valid format.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal::service('email.validator')->isValid().
+ *
+ * @see https://www.drupal.org/node/2912661
+ */
+function valid_email_address($mail) {
+  return \Drupal::service('email.validator')->isValid($mail);
+}
+
+/**
+ * @} End of "defgroup validation".
+ */
+
+/**
+ * @defgroup sanitization Sanitization functions
+ * @{
+ * Functions to sanitize values.
+ *
+ * See https://www.drupal.org/writing-secure-code for information
+ * on writing secure code.
+ */
+
+/**
+ * Strips dangerous protocols from a URI and encodes it for output to HTML.
+ *
+ * @param $uri
+ *   A plain-text URI that might contain dangerous protocols.
+ *
+ * @return string
+ *   A URI stripped of dangerous protocols and encoded for output to an HTML
+ *   attribute value. Because it is already encoded, it should not be set as a
+ *   value within a $attributes array passed to Drupal\Core\Template\Attribute,
+ *   because Drupal\Core\Template\Attribute expects those values to be
+ *   plain-text strings. To pass a filtered URI to
+ *   Drupal\Core\Template\Attribute, call
+ *   \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() instead.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use UrlHelper::stripDangerousProtocols() or UrlHelper::filterBadProtocol()
+ *   instead. UrlHelper::stripDangerousProtocols() can be used in conjunction
+ *   with \Drupal\Component\Render\FormattableMarkup and an @variable
+ *   placeholder which will perform the necessary escaping.
+ *   UrlHelper::filterBadProtocol() is functionality equivalent to check_url()
+ *   apart from the fact it is protected from double escaping bugs. Note that
+ *   this method no longer marks its output as safe.
+ *
+ * @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
+ * @see \Drupal\Component\Utility\UrlHelper::filterBadProtocol()
+ * @see https://www.drupal.org/node/2560027
+ */
+function check_url($uri) {
+  @trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use UrlHelper::stripDangerousProtocols() or UrlHelper::filterBadProtocol() instead. See https://www.drupal.org/node/2560027', E_USER_DEPRECATED);
+  return Html::escape(UrlHelper::stripDangerousProtocols($uri));
+}
+
+/**
+ * @} End of "defgroup sanitization".
+ */
+
+/**
+ * @defgroup format Formatting
+ * @{
+ * Functions to format numbers, strings, dates, etc.
+ */
+
+/**
+ * Generates a string representation for the given byte count.
+ *
+ * @param $size
+ *   A size in bytes.
+ * @param $langcode
+ *   Optional language code to translate to a language other than what is used
+ *   to display the page.
+ *
+ * @return \Drupal\Core\StringTranslation\TranslatableMarkup
+ *   A translated string representation of the size.
+ */
+function format_size($size, $langcode = NULL) {
+  $absolute_size = abs($size);
+  if ($absolute_size < Bytes::KILOBYTE) {
+    return \Drupal::translation()->formatPlural($size, '1 byte', '@count bytes', [], ['langcode' => $langcode]);
+  }
+  // Create a multiplier to preserve the sign of $size.
+  $sign = $absolute_size / $size;
+  foreach (['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] as $unit) {
+    $absolute_size /= Bytes::KILOBYTE;
+    $rounded_size = round($absolute_size, 2);
+    if ($rounded_size < Bytes::KILOBYTE) {
+      break;
+    }
+  }
+  $args = ['@size' => $rounded_size * $sign];
+  $options = ['langcode' => $langcode];
+  switch ($unit) {
+    case 'KB':
+      return new TranslatableMarkup('@size KB', $args, $options);
+
+    case 'MB':
+      return new TranslatableMarkup('@size MB', $args, $options);
+
+    case 'GB':
+      return new TranslatableMarkup('@size GB', $args, $options);
+
+    case 'TB':
+      return new TranslatableMarkup('@size TB', $args, $options);
+
+    case 'PB':
+      return new TranslatableMarkup('@size PB', $args, $options);
+
+    case 'EB':
+      return new TranslatableMarkup('@size EB', $args, $options);
+
+    case 'ZB':
+      return new TranslatableMarkup('@size ZB', $args, $options);
+
+    case 'YB':
+      return new TranslatableMarkup('@size YB', $args, $options);
+  }
+}
+
+/**
+ * Formats a date, using a date type or a custom date format string.
+ *
+ * @param $timestamp
+ *   A UNIX timestamp to format.
+ * @param $type
+ *   (optional) The format to use, one of:
+ *   - One of the built-in formats: 'short', 'medium',
+ *     'long', 'html_datetime', 'html_date', 'html_time',
+ *     'html_yearless_date', 'html_week', 'html_month', 'html_year'.
+ *   - The name of a date type defined by a date format config entity.
+ *   - The machine name of an administrator-defined date format.
+ *   - 'custom', to use $format.
+ *   Defaults to 'medium'.
+ * @param $format
+ *   (optional) If $type is 'custom', a PHP date format string suitable for
+ *   input to date(). Use a backslash to escape ordinary text, so it does not
+ *   get interpreted as date format characters.
+ * @param $timezone
+ *   (optional) Time zone identifier, as described at
+ *   http://php.net/manual/timezones.php Defaults to the time zone used to
+ *   display the page.
+ * @param $langcode
+ *   (optional) Language code to translate to. Defaults to the language used to
+ *   display the page.
+ *
+ * @return
+ *   A translated date string in the requested format.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal::service('date.formatter')->format().
+ *
+ * @see \Drupal\Core\Datetime\DateFormatter::format()
+ * @see https://www.drupal.org/node/1876852
+ */
+function format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) {
+  @trigger_error("format_date() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal::service('date.formatter')->format() instead. See https://www.drupal.org/node/1876852", E_USER_DEPRECATED);
+  return \Drupal::service('date.formatter')->format($timestamp, $type, $format, $timezone, $langcode);
+}
+
+/**
+ * Returns an ISO8601 formatted date based on the given date.
+ *
+ * @param string $date
+ *   A UNIX timestamp.
+ *
+ * @return string
+ *   An ISO8601 formatted date.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
+ *   date('c', $date) instead.
+ *
+ * @see https://www.drupal.org/node/2999991
+ */
+function date_iso8601($date) {
+  @trigger_error('date_iso8601() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use date("c", $date) instead. See https://www.drupal.org/node/2999991.', E_USER_DEPRECATED);
+  // The DATE_ISO8601 constant cannot be used here because it does not match
+  // date('c') and produces invalid RDF markup.
+  return date('c', $date);
+}
+
+/**
+ * @} End of "defgroup format".
+ */
+
+/**
+ * Formats an attribute string for an HTTP header.
+ *
+ * @param array $attributes
+ *   An associative array of attributes such as 'rel'.
+ *
+ * @return string
+ *   A ; separated string ready for insertion in a HTTP header. No escaping is
+ *   performed for HTML entities, so this string is not safe to be printed.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Render\HtmlResponseAttachmentsProcessor::formatHttpHeaderAttributes()
+ *   instead.
+ *
+ * @see https://www.drupal.org/node/3000051
+ */
+function drupal_http_header_attributes(array $attributes = []) {
+  @trigger_error("drupal_http_header_attributes() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Render\HtmlResponseAttachmentsProcessor::formatHttpHeaderAttributes() instead. See https://www.drupal.org/node/3000051", E_USER_DEPRECATED);
+  return HtmlResponseAttachmentsProcessor::formatHttpHeaderAttributes($attributes);
+}
+
+/**
+ * Attempts to set the PHP maximum execution time.
+ *
+ * This function is a wrapper around the PHP function set_time_limit().
+ * When called, set_time_limit() restarts the timeout counter from zero.
+ * In other words, if the timeout is the default 30 seconds, and 25 seconds
+ * into script execution a call such as set_time_limit(20) is made, the
+ * script will run for a total of 45 seconds before timing out.
+ *
+ * If the current time limit is not unlimited it is possible to decrease the
+ * total time limit if the sum of the new time limit and the current time spent
+ * running the script is inferior to the original time limit. It is inherent to
+ * the way set_time_limit() works, it should rather be called with an
+ * appropriate value every time you need to allocate a certain amount of time
+ * to execute a task than only once at the beginning of the script.
+ *
+ * Before calling set_time_limit(), we check if this function is available
+ * because it could be disabled by the server administrator. We also hide all
+ * the errors that could occur when calling set_time_limit(), because it is
+ * not possible to reliably ensure that PHP or a security extension will
+ * not issue a warning/error if they prevent the use of this function.
+ *
+ * @param $time_limit
+ *   An integer specifying the new time limit, in seconds. A value of 0
+ *   indicates unlimited execution time.
+ *
+ * @ingroup php_wrappers
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Component\Utility\Environment::setTimeLimit() instead.
+ *
+ * @see https://www.drupal.org/node/3000058
+ */
+function drupal_set_time_limit($time_limit) {
+  @trigger_error('drupal_set_time_limit() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Component\Utility\Environment::setTimeLimit() instead. See https://www.drupal.org/node/3000058.', E_USER_DEPRECATED);
+  Environment::setTimeLimit($time_limit);
+}
+
+/**
+ * Returns the base URL path (i.e., directory) of the Drupal installation.
+ *
+ * Function base_path() adds a "/" to the beginning and end of the returned path
+ * if the path is not empty. At the very least, this will return "/".
+ *
+ * Examples:
+ * - http://example.com returns "/" because the path is empty.
+ * - http://example.com/drupal/folder returns "/drupal/folder/".
+ */
+function base_path() {
+  return $GLOBALS['base_path'];
+}
+
+/**
+ * Deletes old cached CSS files.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\Asset\AssetCollectionOptimizerInterface::deleteAll().
+ *
+ * @see https://www.drupal.org/node/2317841
+ */
+function drupal_clear_css_cache() {
+  \Drupal::service('asset.css.collection_optimizer')->deleteAll();
+}
+
+/**
+ * Constructs an array of the defaults that are used for JavaScript assets.
+ *
+ * @param $data
+ *   (optional) The default data parameter for the JavaScript asset array.
+ *
+ * @see hook_js_alter()
+ */
+function drupal_js_defaults($data = NULL) {
+  return [
+    'type' => 'file',
+    'group' => JS_DEFAULT,
+    'weight' => 0,
+    'scope' => 'header',
+    'cache' => TRUE,
+    'preprocess' => TRUE,
+    'attributes' => [],
+    'version' => NULL,
+    'data' => $data,
+    'browsers' => [],
+  ];
+}
+
+/**
+ * Adds JavaScript to change the state of an element based on another element.
+ *
+ * A "state" means a certain property on a DOM element, such as "visible" or
+ * "checked". A state can be applied to an element, depending on the state of
+ * another element on the page. In general, states depend on HTML attributes and
+ * DOM element properties, which change due to user interaction.
+ *
+ * Since states are driven by JavaScript only, it is important to understand
+ * that all states are applied on presentation only, none of the states force
+ * any server-side logic, and that they will not be applied for site visitors
+ * without JavaScript support. All modules implementing states have to make
+ * sure that the intended logic also works without JavaScript being enabled.
+ *
+ * #states is an associative array in the form of:
+ * @code
+ * array(
+ *   STATE1 => CONDITIONS_ARRAY1,
+ *   STATE2 => CONDITIONS_ARRAY2,
+ *   ...
+ * )
+ * @endcode
+ * Each key is the name of a state to apply to the element, such as 'visible'.
+ * Each value is a list of conditions that denote when the state should be
+ * applied.
+ *
+ * Multiple different states may be specified to act on complex conditions:
+ * @code
+ * array(
+ *   'visible' => CONDITIONS,
+ *   'checked' => OTHER_CONDITIONS,
+ * )
+ * @endcode
+ *
+ * Every condition is a key/value pair, whose key is a jQuery selector that
+ * denotes another element on the page, and whose value is an array of
+ * conditions, which must be met on that element:
+ * @code
+ * array(
+ *   'visible' => array(
+ *     JQUERY_SELECTOR => REMOTE_CONDITIONS,
+ *     JQUERY_SELECTOR => REMOTE_CONDITIONS,
+ *     ...
+ *   ),
+ * )
+ * @endcode
+ * All conditions must be met for the state to be applied.
+ *
+ * Each remote condition is a key/value pair specifying conditions on the other
+ * element that need to be met to apply the state to the element:
+ * @code
+ * array(
+ *   'visible' => array(
+ *     ':input[name="remote_checkbox"]' => array('checked' => TRUE),
+ *   ),
+ * )
+ * @endcode
+ *
+ * For example, to show a textfield only when a checkbox is checked:
+ * @code
+ * $form['toggle_me'] = array(
+ *   '#type' => 'checkbox',
+ *   '#title' => t('Tick this box to type'),
+ * );
+ * $form['settings'] = array(
+ *   '#type' => 'textfield',
+ *   '#states' => array(
+ *     // Only show this field when the 'toggle_me' checkbox is enabled.
+ *     'visible' => array(
+ *       ':input[name="toggle_me"]' => array('checked' => TRUE),
+ *     ),
+ *   ),
+ * );
+ * @endcode
+ *
+ * The following states may be applied to an element:
+ * - enabled
+ * - disabled
+ * - required
+ * - optional
+ * - visible
+ * - invisible
+ * - checked
+ * - unchecked
+ * - expanded
+ * - collapsed
+ *
+ * The following states may be used in remote conditions:
+ * - empty
+ * - filled
+ * - checked
+ * - unchecked
+ * - expanded
+ * - collapsed
+ * - value
+ *
+ * The following states exist for both elements and remote conditions, but are
+ * not fully implemented and may not change anything on the element:
+ * - relevant
+ * - irrelevant
+ * - valid
+ * - invalid
+ * - touched
+ * - untouched
+ * - readwrite
+ * - readonly
+ *
+ * When referencing select lists and radio buttons in remote conditions, a
+ * 'value' condition must be used:
+ * @code
+ *   '#states' => array(
+ *     // Show the settings if 'bar' has been selected for 'foo'.
+ *     'visible' => array(
+ *       ':input[name="foo"]' => array('value' => 'bar'),
+ *     ),
+ *   ),
+ * @endcode
+ *
+ * @param $elements
+ *   A renderable array element having a #states property as described above.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Form\FormHelper::processStates() instead.
+ *
+ * @see https://www.drupal.org/node/3000069
+ * @see \Drupal\Core\Form\FormHelper::processStates()
+ */
+function drupal_process_states(&$elements) {
+  @trigger_error('drupal_process_states() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Form\FormHelper::processStates() instead. See https://www.drupal.org/node/3000069', E_USER_DEPRECATED);
+  FormHelper::processStates($elements);
+}
+
+/**
+ * Assists in attaching the tableDrag JavaScript behavior to a themed table.
+ *
+ * Draggable tables should be used wherever an outline or list of sortable items
+ * needs to be arranged by an end-user. Draggable tables are very flexible and
+ * can manipulate the value of form elements placed within individual columns.
+ *
+ * To set up a table to use drag and drop in place of weight select-lists or in
+ * place of a form that contains parent relationships, the form must be themed
+ * into a table. The table must have an ID attribute set and it
+ * may be set as follows:
+ * @code
+ * $table = array(
+ *   '#type' => 'table',
+ *   '#header' => $header,
+ *   '#rows' => $rows,
+ *   '#attributes' => array(
+ *     'id' => 'my-module-table',
+ *   ),
+ * );
+ * return \Drupal::service('renderer')->render($table);
+ * @endcode
+ *
+ * In the theme function for the form, a special class must be added to each
+ * form element within the same column, "grouping" them together.
+ *
+ * In a situation where a single weight column is being sorted in the table, the
+ * classes could be added like this (in the theme function):
+ * @code
+ * $form['my_elements'][$delta]['weight']['#attributes']['class'] = array('my-elements-weight');
+ * @endcode
+ *
+ * Each row of the table must also have a class of "draggable" in order to
+ * enable the drag handles:
+ * @code
+ * $row = array(...);
+ * $rows[] = array(
+ *   'data' => $row,
+ *   'class' => array('draggable'),
+ * );
+ * @endcode
+ *
+ * When tree relationships are present, the two additional classes
+ * 'tabledrag-leaf' and 'tabledrag-root' can be used to refine the behavior:
+ * - Rows with the 'tabledrag-leaf' class cannot have child rows.
+ * - Rows with the 'tabledrag-root' class cannot be nested under a parent row.
+ *
+ * Calling drupal_attach_tabledrag() would then be written as such:
+ * @code
+ * drupal_attach_tabledrag('my-module-table', array(
+ *   'action' => 'order',
+ *   'relationship' => 'sibling',
+ *   'group' => 'my-elements-weight',
+ * );
+ * @endcode
+ *
+ * In a more complex case where there are several groups in one column (such as
+ * the block regions on the admin/structure/block page), a separate subgroup
+ * class must also be added to differentiate the groups.
+ * @code
+ * $form['my_elements'][$region][$delta]['weight']['#attributes']['class'] = array('my-elements-weight', 'my-elements-weight-' . $region);
+ * @endcode
+ *
+ * The 'group' option is still 'my-element-weight', and the additional
+ * 'subgroup' option will be passed in as 'my-elements-weight-' . $region. This
+ * also means that you'll need to call drupal_attach_tabledrag() once for every
+ * region added.
+ *
+ * @code
+ * foreach ($regions as $region) {
+ *   drupal_attach_tabledrag('my-module-table', array(
+ *     'action' => 'order',
+ *     'relationship' => 'sibling',
+ *     'group' => 'my-elements-weight',
+ *     'subgroup' => 'my-elements-weight-' . $region,
+ *   ));
+ * }
+ * @endcode
+ *
+ * In a situation where tree relationships are present, adding multiple
+ * subgroups is not necessary, because the table will contain indentations that
+ * provide enough information about the sibling and parent relationships. See
+ * MenuForm::BuildOverviewForm for an example creating a table
+ * containing parent relationships.
+ *
+ * @param $element
+ *   A form element to attach the tableDrag behavior to.
+ * @param array $options
+ *   These options are used to generate JavaScript settings necessary to
+ *   configure the tableDrag behavior appropriately for this particular table.
+ *   An associative array containing the following keys:
+ *   - 'table_id': String containing the target table's id attribute.
+ *     If the table does not have an id, one will need to be set,
+ *     such as <table id="my-module-table">.
+ *   - 'action': String describing the action to be done on the form item.
+ *      Either 'match' 'depth', or 'order':
+ *     - 'match' is typically used for parent relationships.
+ *     - 'order' is typically used to set weights on other form elements with
+ *       the same group.
+ *     - 'depth' updates the target element with the current indentation.
+ *   - 'relationship': String describing where the "action" option
+ *     should be performed. Either 'parent', 'sibling', 'group', or 'self':
+ *     - 'parent' will only look for fields up the tree.
+ *     - 'sibling' will look for fields in the same group in rows above and
+ *       below it.
+ *     - 'self' affects the dragged row itself.
+ *     - 'group' affects the dragged row, plus any children below it (the entire
+ *       dragged group).
+ *   - 'group': A class name applied on all related form elements for this action.
+ *   - 'subgroup': (optional) If the group has several subgroups within it, this
+ *     string should contain the class name identifying fields in the same
+ *     subgroup.
+ *   - 'source': (optional) If the $action is 'match', this string should contain
+ *     the classname identifying what field will be used as the source value
+ *     when matching the value in $subgroup.
+ *   - 'hidden': (optional) The column containing the field elements may be
+ *     entirely hidden from view dynamically when the JavaScript is loaded. Set
+ *     to FALSE if the column should not be hidden.
+ *   - 'limit': (optional) Limit the maximum amount of parenting in this table.
+ *
+ * @see MenuForm::BuildOverviewForm()
+ */
+function drupal_attach_tabledrag(&$element, array $options) {
+  // Add default values to elements.
+  $options = $options + [
+    'subgroup' => NULL,
+    'source' => NULL,
+    'hidden' => TRUE,
+    'limit' => 0,
+  ];
+
+  $group = $options['group'];
+
+  $tabledrag_id = &drupal_static(__FUNCTION__);
+  $tabledrag_id = (!isset($tabledrag_id)) ? 0 : $tabledrag_id + 1;
+
+  // If a subgroup or source isn't set, assume it is the same as the group.
+  $target = isset($options['subgroup']) ? $options['subgroup'] : $group;
+  $source = isset($options['source']) ? $options['source'] : $target;
+  $element['#attached']['drupalSettings']['tableDrag'][$options['table_id']][$group][$tabledrag_id] = [
+    'target' => $target,
+    'source' => $source,
+    'relationship' => $options['relationship'],
+    'action' => $options['action'],
+    'hidden' => $options['hidden'],
+    'limit' => $options['limit'],
+  ];
+
+  $element['#attached']['library'][] = 'core/drupal.tabledrag';
+}
+
+/**
+ * Deletes old cached JavaScript files and variables.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\Asset\AssetCollectionOptimizerInterface::deleteAll().
+ *
+ * @see https://www.drupal.org/node/2317841
+ */
+function drupal_clear_js_cache() {
+  \Drupal::service('asset.js.collection_optimizer')->deleteAll();
+}
+
+/**
+ * Pre-render callback: Renders a link into #markup.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\Render\Element\Link::preRenderLink().
+ */
+function drupal_pre_render_link($element) {
+  return Link::preRenderLink($element);
+}
+
+/**
+ * Pre-render callback: Collects child links into a single array.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Render\Element\Link::preRenderLinks() instead.
+ *
+ * @see https://www.drupal.org/node/2966725
+ */
+function drupal_pre_render_links($element) {
+  @trigger_error('drupal_pre_render_links() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Render\Element\Link::preRenderLinks() instead. See https://www.drupal.org/node/2966725', E_USER_DEPRECATED);
+  return Link::preRenderLinks($element);
+}
+
+/**
+ * Renders final HTML given a structured array tree.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Render\RendererInterface::renderRoot() instead.
+ *
+ * @see \Drupal\Core\Render\RendererInterface::renderRoot()
+ * @see https://www.drupal.org/node/2912696
+ */
+function drupal_render_root(&$elements) {
+  @trigger_error('drupal_render_root() is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Render\RendererInterface::renderRoot() instead. See https://www.drupal.org/node/2912696', E_USER_DEPRECATED);
+  return \Drupal::service('renderer')->renderRoot($elements);
+}
+
+/**
+ * Renders HTML given a structured array tree.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
+ *   'renderer' service instead.
+ *
+ * @see \Drupal\Core\Render\RendererInterface::render()
+ * @see https://www.drupal.org/node/2912696
+ */
+function drupal_render(&$elements, $is_recursive_call = FALSE) {
+  return \Drupal::service('renderer')->render($elements, $is_recursive_call);
+}
+
+/**
+ * Renders children of an element and concatenates them.
+ *
+ * @param array $element
+ *   The structured array whose children shall be rendered.
+ * @param array $children_keys
+ *   (optional) If the keys of the element's children are already known, they
+ *   can be passed in to save another run of
+ *   \Drupal\Core\Render\Element::children().
+ *
+ * @return string|\Drupal\Component\Render\MarkupInterface
+ *   The rendered HTML of all children of the element.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Avoid early
+ *   rendering when possible or loop through the elements and render them as
+ *   they are available.
+ *
+ * @see \Drupal\Core\Render\RendererInterface::render()
+ * @see https://www.drupal.org/node/2912757
+ */
+function drupal_render_children(&$element, $children_keys = NULL) {
+  if ($children_keys === NULL) {
+    $children_keys = Element::children($element);
+  }
+  $output = '';
+  foreach ($children_keys as $key) {
+    if (!empty($element[$key])) {
+      $output .= \Drupal::service('renderer')->render($element[$key]);
+    }
+  }
+  return Markup::create($output);
+}
+
+/**
+ * Renders an element.
+ *
+ * This function renders an element. The top level element is shown with show()
+ * before rendering, so it will always be rendered even if hide() had been
+ * previously used on it.
+ *
+ * @param $element
+ *   The element to be rendered.
+ *
+ * @return
+ *   The rendered element.
+ *
+ * @see \Drupal\Core\Render\RendererInterface
+ * @see show()
+ * @see hide()
+ */
+function render(&$element) {
+  if (!$element && $element !== 0) {
+    return NULL;
+  }
+  if (is_array($element)) {
+    // Early return if this element was pre-rendered (no need to re-render).
+    if (isset($element['#printed']) && $element['#printed'] == TRUE && isset($element['#markup']) && strlen($element['#markup']) > 0) {
+      return $element['#markup'];
+    }
+    show($element);
+    return \Drupal::service('renderer')->render($element);
+  }
+  else {
+    // Safe-guard for inappropriate use of render() on flat variables: return
+    // the variable as-is.
+    return $element;
+  }
+}
+
+/**
+ * Hides an element from later rendering.
+ *
+ * The first time render() or drupal_render() is called on an element tree,
+ * as each element in the tree is rendered, it is marked with a #printed flag
+ * and the rendered children of the element are cached. Subsequent calls to
+ * render() or drupal_render() will not traverse the child tree of this element
+ * again: they will just use the cached children. So if you want to hide an
+ * element, be sure to call hide() on the element before its parent tree is
+ * rendered for the first time, as it will have no effect on subsequent
+ * renderings of the parent tree.
+ *
+ * @param $element
+ *   The element to be hidden.
+ *
+ * @return
+ *   The element.
+ *
+ * @see render()
+ * @see show()
+ */
+function hide(&$element) {
+  $element['#printed'] = TRUE;
+  return $element;
+}
+
+/**
+ * Shows a hidden element for later rendering.
+ *
+ * You can also use render($element), which shows the element while rendering
+ * it.
+ *
+ * The first time render() or drupal_render() is called on an element tree,
+ * as each element in the tree is rendered, it is marked with a #printed flag
+ * and the rendered children of the element are cached. Subsequent calls to
+ * render() or drupal_render() will not traverse the child tree of this element
+ * again: they will just use the cached children. So if you want to show an
+ * element, be sure to call show() on the element before its parent tree is
+ * rendered for the first time, as it will have no effect on subsequent
+ * renderings of the parent tree.
+ *
+ * @param $element
+ *   The element to be shown.
+ *
+ * @return
+ *   The element.
+ *
+ * @see render()
+ * @see hide()
+ */
+function show(&$element) {
+  $element['#printed'] = FALSE;
+  return $element;
+}
+
+/**
+ * Retrieves the default properties for the defined element type.
+ *
+ * @param $type
+ *   An element type as defined by an element plugin.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal::service('element_info')->getInfo() instead.
+ *
+ * @see https://www.drupal.org/node/2235461
+ */
+function element_info($type) {
+  return \Drupal::service('element_info')->getInfo($type);
+}
+
+/**
+ * Retrieves a single property for the defined element type.
+ *
+ * @param $type
+ *   An element type as defined by an element plugin.
+ * @param $property_name
+ *   The property within the element type that should be returned.
+ * @param $default
+ *   (Optional) The value to return if the element type does not specify a
+ *   value for the property. Defaults to NULL.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal::service('element_info')->getInfoProperty() instead.
+ *
+ * @see https://www.drupal.org/node/2235461
+ */
+function element_info_property($type, $property_name, $default = NULL) {
+  return \Drupal::service('element_info')->getInfoProperty($type, $property_name, $default);
+}
+
+/**
+ * Flushes all persistent caches, resets all variables, and rebuilds all data structures.
+ *
+ * At times, it is necessary to re-initialize the entire system to account for
+ * changed or new code. This function:
+ * - Clears all persistent caches:
+ *   - The bootstrap cache bin containing base system, module system, and theme
+ *     system information.
+ *   - The common 'default' cache bin containing arbitrary caches.
+ *   - The page cache.
+ *   - The URL alias path cache.
+ * - Resets all static variables that have been defined via drupal_static().
+ * - Clears asset (JS/CSS) file caches.
+ * - Updates the system with latest information about extensions (modules and
+ *   themes).
+ * - Updates the bootstrap flag for modules implementing bootstrap_hooks().
+ * - Rebuilds the full database schema information (invoking hook_schema()).
+ * - Rebuilds data structures of all modules (invoking hook_rebuild()). In
+ *   core this means
+ *   - blocks, node types, date formats and actions are synchronized with the
+ *     database
+ *   - The 'active' status of fields is refreshed.
+ * - Rebuilds the menu router.
+ *
+ * This means the entire system is reset so all caches and static variables are
+ * effectively empty. After that is guaranteed, information about the currently
+ * active code is updated, and rebuild operations are successively called in
+ * order to synchronize the active system according to the current information
+ * defined in code.
+ *
+ * All modules need to ensure that all of their caches are flushed when
+ * hook_cache_flush() is invoked; any previously known information must no
+ * longer exist. All following hook_rebuild() operations must be based on fresh
+ * and current system data. All modules must be able to rely on this contract.
+ *
+ * @see \Drupal\Core\Cache\CacheHelper::getBins()
+ * @see hook_cache_flush()
+ * @see hook_rebuild()
+ *
+ * This function also resets the theme, which means it is not initialized
+ * anymore and all previously added JavaScript and CSS is gone. Normally, this
+ * function is called as an end-of-POST-request operation that is followed by a
+ * redirect, so this effect is not visible. Since the full reset is the whole
+ * point of this function, callers need to take care for backing up all needed
+ * variables and properly restoring or re-initializing them on their own. For
+ * convenience, this function automatically re-initializes the maintenance theme
+ * if it was initialized before.
+ *
+ * @todo Try to clear page/JS/CSS caches last, so cached pages can still be
+ *   served during this possibly long-running operation. (Conflict on bootstrap
+ *   cache though.)
+ * @todo Add a global lock to ensure that caches are not primed in concurrent
+ *   requests.
+ */
+function drupal_flush_all_caches() {
+  $module_handler = \Drupal::moduleHandler();
+  // Flush all persistent caches.
+  // This is executed based on old/previously known information, which is
+  // sufficient, since new extensions cannot have any primed caches yet.
+  $module_handler->invokeAll('cache_flush');
+  foreach (Cache::getBins() as $service_id => $cache_backend) {
+    $cache_backend->deleteAll();
+  }
+
+  // Flush asset file caches.
+  \Drupal::service('asset.css.collection_optimizer')->deleteAll();
+  \Drupal::service('asset.js.collection_optimizer')->deleteAll();
+  _drupal_flush_css_js();
+
+  // Reset all static caches.
+  drupal_static_reset();
+
+  // Invalidate the container.
+  \Drupal::service('kernel')->invalidateContainer();
+
+  // Wipe the Twig PHP Storage cache.
+  \Drupal::service('twig')->invalidate();
+
+  // Rebuild module and theme data.
+  $module_data = \Drupal::service('extension.list.module')->reset()->getList();
+  /** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */
+  $theme_handler = \Drupal::service('theme_handler');
+  $theme_handler->refreshInfo();
+  // In case the active theme gets requested later in the same request we need
+  // to reset the theme manager.
+  \Drupal::theme()->resetActiveTheme();
+
+  // Rebuild and reboot a new kernel. A simple DrupalKernel reboot is not
+  // sufficient, since the list of enabled modules might have been adjusted
+  // above due to changed code.
+  $files = [];
+  $modules = [];
+  foreach ($module_data as $name => $extension) {
+    if ($extension->status) {
+      $files[$name] = $extension;
+      $modules[$name] = $extension->weight;
+    }
+  }
+  $modules = module_config_sort($modules);
+  \Drupal::service('kernel')->updateModules($modules, $files);
+  // New container, new module handler.
+  $module_handler = \Drupal::moduleHandler();
+
+  // Ensure that all modules that are currently supposed to be enabled are
+  // actually loaded.
+  $module_handler->loadAll();
+
+  // Rebuild all information based on new module data.
+  $module_handler->invokeAll('rebuild');
+
+  // Clear all plugin caches.
+  \Drupal::service('plugin.cache_clearer')->clearCachedDefinitions();
+
+  // Rebuild the menu router based on all rebuilt data.
+  // Important: This rebuild must happen last, so the menu router is guaranteed
+  // to be based on up to date information.
+  \Drupal::service('router.builder')->rebuild();
+
+  // Re-initialize the maintenance theme, if the current request attempted to
+  // use it. Unlike regular usages of this function, the installer and update
+  // scripts need to flush all caches during GET requests/page building.
+  if (function_exists('_drupal_maintenance_theme')) {
+    \Drupal::theme()->resetActiveTheme();
+    drupal_maintenance_theme();
+  }
+}
+
+/**
+ * Changes the dummy query string added to all CSS and JavaScript files.
+ *
+ * Changing the dummy query string appended to CSS and JavaScript files forces
+ * all browsers to reload fresh files.
+ */
+function _drupal_flush_css_js() {
+  // The timestamp is converted to base 36 in order to make it more compact.
+  Drupal::state()->set('system.css_js_query_string', base_convert(REQUEST_TIME, 10, 36));
+}
+
+/**
+ * Outputs debug information.
+ *
+ * The debug information is passed on to trigger_error() after being converted
+ * to a string using _drupal_debug_message().
+ *
+ * @param $data
+ *   Data to be output.
+ * @param $label
+ *   Label to prefix the data.
+ * @param $print_r
+ *   Flag to switch between print_r() and var_export() for data conversion to
+ *   string. Set $print_r to FALSE to use var_export() instead of print_r().
+ *   Passing recursive data structures to var_export() will generate an error.
+ */
+function debug($data, $label = NULL, $print_r = TRUE) {
+  // Print $data contents to string.
+  $string = Html::escape($print_r ? print_r($data, TRUE) : var_export($data, TRUE));
+
+  // Display values with pre-formatting to increase readability.
+  $string = '<pre>' . $string . '</pre>';
+
+  trigger_error(trim($label ? "$label: $string" : $string));
+}
+
+/**
+ * Checks whether a version is compatible with a given dependency.
+ *
+ * @param $v
+ *   A parsed dependency structure e.g. from ModuleHandler::parseDependency().
+ * @param $current_version
+ *   The version to check against (like 4.2).
+ *
+ * @return
+ *   NULL if compatible, otherwise the original dependency version string that
+ *   caused the incompatibility.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Extension\Dependency::isCompatible() instead.
+ *
+ * @see https://www.drupal.org/node/2756875
+ */
+function drupal_check_incompatibility($v, $current_version) {
+  @trigger_error(__FUNCTION__ . '() is deprecated. Use \Drupal\Core\Extension\Dependency::isCompatible() instead. See https://www.drupal.org/node/2756875', E_USER_DEPRECATED);
+  if (!empty($v['versions'])) {
+    foreach ($v['versions'] as $required_version) {
+      if ((isset($required_version['op']) && !version_compare($current_version, $required_version['version'], $required_version['op']))) {
+        return $v['original_version'];
+      }
+    }
+  }
+}
+
+/**
+ * Returns a string of supported archive extensions.
+ *
+ * @return string
+ *   A space-separated string of extensions suitable for use by the file
+ *   validation system.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Archiver\ArchiverManager::getExtensions() instead.
+ *
+ * @see https://www.drupal.org/node/2999951
+ */
+function archiver_get_extensions() {
+  @trigger_error('archiver_get_extensions() is deprecated in Drupal 8.8.0 and will be removed in Drupal 9.0.0. Use \Drupal\Core\Archiver\ArchiverManager::getExtensions() instead. See https://www.drupal.org/node/2999951', E_USER_DEPRECATED);
+  return \Drupal::service('plugin.manager.archiver')->getExtensions();
+}
+
+/**
+ * Creates the appropriate archiver for the specified file.
+ *
+ * @param string $file
+ *   The full path of the archive file. Note that stream wrapper paths are
+ *   supported, but not remote ones.
+ *
+ * @return \Drupal\Core\Archiver\ArchiverInterface|false
+ *   A newly created instance of the archiver class appropriate for the
+ *   specified file, already bound to that file. If no appropriate archiver
+ *   class was found, will return FALSE.
+ *
+ * @throws \Exception
+ *   If a remote stream wrapper path was passed.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Instead,
+ *   get plugin.manager.archiver service from container and call getInstance()
+ *   method on it. For example $archiver->getInstance(['filepath' => $file]);
+ *
+ * @see https://www.drupal.org/node/2999951
+ */
+function archiver_get_archiver($file) {
+  @trigger_error('archiver_get_archiver() is deprecated in Drupal 8.8.0 and will be removed in Drupal 9.0.x. Instead, get plugin.manager.archiver service from container and call getInstance() method on it. For example $archiver->getInstance(["filepath" => $file]); See https://www.drupal.org/node/2999951', E_USER_DEPRECATED);
+  // Archivers can only work on local paths
+  $filepath = \Drupal::service('file_system')->realpath($file);
+  if (!is_file($filepath)) {
+    throw new Exception("Archivers can only operate on local files: '$file' not supported");
+  }
+  return \Drupal::service('plugin.manager.archiver')->getInstance(['filepath' => $filepath]);
+}
+
+/**
+ * Assembles the Drupal Updater registry.
+ *
+ * An Updater is a class that knows how to update various parts of the Drupal
+ * file system, for example to update modules that have newer releases, or to
+ * install a new theme.
+ *
+ * @return array
+ *   The Drupal Updater class registry.
+ *
+ * @see \Drupal\Core\Updater\Updater
+ * @see hook_updater_info()
+ * @see hook_updater_info_alter()
+ */
+function drupal_get_updaters() {
+  $updaters = &drupal_static(__FUNCTION__);
+  if (!isset($updaters)) {
+    $updaters = \Drupal::moduleHandler()->invokeAll('updater_info');
+    \Drupal::moduleHandler()->alter('updater_info', $updaters);
+    uasort($updaters, [SortArray::class, 'sortByWeightElement']);
+  }
+  return $updaters;
+}
+
+/**
+ * Assembles the Drupal FileTransfer registry.
+ *
+ * @return
+ *   The Drupal FileTransfer class registry.
+ *
+ * @see \Drupal\Core\FileTransfer\FileTransfer
+ * @see hook_filetransfer_info()
+ * @see hook_filetransfer_info_alter()
+ */
+function drupal_get_filetransfer_info() {
+  $info = &drupal_static(__FUNCTION__);
+  if (!isset($info)) {
+    $info = \Drupal::moduleHandler()->invokeAll('filetransfer_info');
+    \Drupal::moduleHandler()->alter('filetransfer_info', $info);
+    uasort($info, [SortArray::class, 'sortByWeightElement']);
+  }
+  return $info;
+}

+ 1143 - 0
web/core/includes/database.inc

@@ -0,0 +1,1143 @@
+<?php
+
+/**
+ * @file
+ * Core systems for the database layer.
+ *
+ * Classes required for basic functioning of the database system should be
+ * placed in this file.  All utility functions should also be placed in this
+ * file only, as they cannot auto-load the way classes can.
+ */
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\Database\Query\Condition;
+
+/**
+ * @addtogroup database
+ * @{
+ */
+
+/**
+ * Executes an arbitrary query string against the active database.
+ *
+ * Use this function for SELECT queries if it is just a simple query string.
+ * If the caller or other modules need to change the query, use
+ * \Drupal::database()->select() instead.
+ *
+ * Do not use this function for INSERT, UPDATE, or DELETE queries. Those should
+ * be handled via \Drupal::database()->insert(), \Drupal::database()->update(),
+ * \Drupal::database()->merge()and \Drupal::database()->delete().
+ *
+ * @param string|\Drupal\Core\Database\StatementInterface $query
+ *   The prepared statement query to run. Although it will accept both named and
+ *   unnamed placeholders, named placeholders are strongly preferred as they are
+ *   more self-documenting. If the argument corresponding to a placeholder is
+ *   an array of values to be expanded (for example, with an IN query), the
+ *   placeholder should be named with a trailing bracket like :example[].
+ * @param array $args
+ *   An array of values to substitute into the query. If the query uses named
+ *   placeholders, this is an associative array in any order. If the query uses
+ *   unnamed placeholders (?), this is an indexed array and the order must match
+ *   the order of placeholders in the query string.
+ * @param array $options
+ *   An array of options to control how the query operates.
+ *
+ * @return \Drupal\Core\Database\StatementInterface
+ *   A prepared statement object, already executed.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container
+ *   and call query() on it. For example,
+ *   $injected_database->query($query, $args, $options);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::query()
+ * @see \Drupal\Core\Database\Connection::defaultOptions()
+ */
+function db_query($query, array $args = [], array $options = []) {
+  @trigger_error('db_query() is deprecated in drupal:8.0.0. It will be removed before drupal:9.0.0. Instead, get a database connection injected into your service from the container and call query() on it. For example, $injected_database->query($query, $args, $options). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection(_db_get_target($options))->query($query, $args, $options);
+}
+
+/**
+ * Executes a query against the active database, restricted to a range.
+ *
+ * @param string $query
+ *   The prepared statement query to run. Although it will accept both named and
+ *   unnamed placeholders, named placeholders are strongly preferred as they are
+ *   more self-documenting.
+ * @param $from
+ *   The first record from the result set to return.
+ * @param $count
+ *   The number of records to return from the result set.
+ * @param array $args
+ *   An array of values to substitute into the query. If the query uses named
+ *   placeholders, this is an associative array in any order. If the query uses
+ *   unnamed placeholders (?), this is an indexed array and the order must match
+ *   the order of placeholders in the query string.
+ * @param array $options
+ *   An array of options to control how the query operates.
+ *
+ * @return \Drupal\Core\Database\StatementInterface
+ *   A prepared statement object, already executed.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container
+ *   and call queryRange() on it. For example,
+ *   $injected_database->queryRange($query, $from, $count, $args, $options);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::queryRange()
+ * @see \Drupal\Core\Database\Connection::defaultOptions()
+ */
+function db_query_range($query, $from, $count, array $args = [], array $options = []) {
+  @trigger_error('db_query_range() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call queryRange() on it. For example, $injected_database->queryRange($query, $from, $count, $args, $options). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection(_db_get_target($options))->queryRange($query, $from, $count, $args, $options);
+}
+
+/**
+ * Executes a SELECT query string and saves the result set to a temporary table.
+ *
+ * The execution of the query string happens against the active database.
+ *
+ * @param string $query
+ *   The prepared SELECT statement query to run. Although it will accept both
+ *   named and unnamed placeholders, named placeholders are strongly preferred
+ *   as they are more self-documenting.
+ * @param array $args
+ *   An array of values to substitute into the query. If the query uses named
+ *   placeholders, this is an associative array in any order. If the query uses
+ *   unnamed placeholders (?), this is an indexed array and the order must match
+ *   the order of placeholders in the query string.
+ * @param array $options
+ *   An array of options to control how the query operates.
+ *
+ * @return string
+ *   The name of the temporary table.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container
+ *   and call queryTemporary() on it. For example,
+ *   $injected_database->queryTemporary($query, $args, $options);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::queryTemporary()
+ * @see \Drupal\Core\Database\Connection::defaultOptions()
+ */
+function db_query_temporary($query, array $args = [], array $options = []) {
+  @trigger_error('db_query_temporary() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call queryTemporary() on it. For example, $injected_database->queryTemporary($query, $args, $options). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection(_db_get_target($options))->queryTemporary($query, $args, $options);
+}
+
+/**
+ * Returns a new InsertQuery object for the active database.
+ *
+ * @param string $table
+ *   The table into which to insert.
+ * @param array $options
+ *   An array of options to control how the query operates.
+ *
+ * @return \Drupal\Core\Database\Query\Insert
+ *   A new Insert object for this connection.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get
+ *   a database connection injected into your service from the container and
+ *   call insert() on it. For example,
+ *   $injected_database->insert($table, $options);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::insert()
+ * @see \Drupal\Core\Database\Connection::defaultOptions()
+ */
+function db_insert($table, array $options = []) {
+  @trigger_error('db_insert() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call insert() on it. For example, $injected_database->insert($table, $options). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection(_db_get_target($options, FALSE))->insert($table, $options);
+}
+
+/**
+ * Returns a new MergeQuery object for the active database.
+ *
+ * @param string $table
+ *   Name of the table to associate with this query.
+ * @param array $options
+ *   An array of options to control how the query operates.
+ *
+ * @return \Drupal\Core\Database\Query\Merge
+ *   A new Merge object for this connection.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get
+ *   a database connection injected into your service from the container and
+ *   call merge() on it. For example,
+ *   $injected_database->merge($table, $options);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::merge()
+ * @see \Drupal\Core\Database\Connection::defaultOptions()
+ */
+function db_merge($table, array $options = []) {
+  @trigger_error('db_merge() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call merge() on it. For example, $injected_database->merge($table, $options). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection(_db_get_target($options, FALSE))->merge($table, $options);
+}
+
+/**
+ * Returns a new UpdateQuery object for the active database.
+ *
+ * @param string $table
+ *   The table to update.
+ * @param array $options
+ *   An array of options to control how the query operates.
+ *
+ * @return \Drupal\Core\Database\Query\Update
+ *   A new Update object for this connection.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get
+ *   a database connection injected into your service from the container and
+ *   call update() on it. For example,
+ *   $injected_database->update($table, $options);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::update()
+ * @see \Drupal\Core\Database\Connection::defaultOptions()
+ */
+function db_update($table, array $options = []) {
+  @trigger_error('db_update() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call call update() on it. For example, $injected_database->update($table, $options). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection(_db_get_target($options, FALSE))->update($table, $options);
+}
+
+/**
+ * Returns a new DeleteQuery object for the active database.
+ *
+ * @param string $table
+ *   The table from which to delete.
+ * @param array $options
+ *   An array of options to control how the query operates.
+ *
+ * @return \Drupal\Core\Database\Query\Delete
+ *   A new Delete object for this connection.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get
+ *   a database connection injected into your service from the container and
+ *   call delete() on it. For example,
+ *   $injected_database->delete($table, $options);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::delete()
+ * @see \Drupal\Core\Database\Connection::defaultOptions()
+ */
+function db_delete($table, array $options = []) {
+  @trigger_error('db_delete is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call delete() on it. For example, $injected_database->delete($table, $options). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection(_db_get_target($options, FALSE))->delete($table, $options);
+}
+
+/**
+ * Returns a new TruncateQuery object for the active database.
+ *
+ * @param string $table
+ *   The table from which to truncate.
+ * @param array $options
+ *   An array of options to control how the query operates.
+ *
+ * @return \Drupal\Core\Database\Query\Truncate
+ *   A new Truncate object for this connection.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get
+ *   a database connection injected into your service from the container and
+ *   call truncate() on it. For example,
+ *   $injected_database->truncate($table, $options);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::truncate()
+ * @see \Drupal\Core\Database\Connection::defaultOptions()
+ */
+function db_truncate($table, array $options = []) {
+  @trigger_error('db_truncate() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call truncate() on it. For example, $injected_database->truncate($table, $options). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection(_db_get_target($options, FALSE))->truncate($table, $options);
+}
+
+/**
+ * Returns a new SelectQuery object for the active database.
+ *
+ * @param string|\Drupal\Core\Database\Query\SelectInterface $table
+ *   The base table for this query. May be a string or another SelectInterface
+ *   object. If a SelectInterface object is passed, it will be used as a
+ *   subselect.
+ * @param string $alias
+ *   (optional) The alias for the base table of this query.
+ * @param array $options
+ *   (optional) An array of options to control how the query operates.
+ *
+ * @return \Drupal\Core\Database\Query\Select
+ *   A new Select object for this connection.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get
+ *   a database connection injected into your service from the container and
+ *   call select() on it. For example,
+ *   $injected_database->select($table, $alias, $options);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::select()
+ * @see \Drupal\Core\Database\Connection::defaultOptions()
+ */
+function db_select($table, $alias = NULL, array $options = []) {
+  @trigger_error('db_select() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call select() on it. For example, $injected_database->db_select($table, $alias, $options). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection(_db_get_target($options))->select($table, $alias, $options);
+}
+
+/**
+ * Returns a new transaction object for the active database.
+ *
+ * @param string $name
+ *   Optional name of the transaction.
+ * @param array $options
+ *   An array of options to control how the transaction operates:
+ *   - target: The database target name.
+ *
+ * @return \Drupal\Core\Database\Transaction
+ *   A new Transaction object for this connection.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead, get
+ *   a database connection injected into your service from the container and
+ *   call startTransaction() on it. For example,
+ *   $injected_database->startTransaction($name);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::startTransaction()
+ * @see \Drupal\Core\Database\Connection::defaultOptions()
+ */
+function db_transaction($name = NULL, array $options = []) {
+  @trigger_error('db_transaction is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call startTransaction() on it. For example, $injected_database->startTransaction($name). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection(_db_get_target($options))->startTransaction($name);
+}
+
+/**
+ * Sets a new active database.
+ *
+ * @param $key
+ *   The key in the $databases array to set as the default database.
+ *
+ * @return string|null
+ *   The key of the formerly active database.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use
+ * \Drupal\Core\Database\Database::setActiveConnection().
+ *
+ * @see https://www.drupal.org/node/2993033
+ */
+function db_set_active($key = 'default') {
+  @trigger_error('db_set_active() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Use \Drupal\Core\Database\Database::setActiveConnection() instead. See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::setActiveConnection($key);
+}
+
+/**
+ * Restricts a dynamic table name to safe characters.
+ *
+ * Only keeps alphanumeric and underscores.
+ *
+ * @param $table
+ *   The table name to escape.
+ *
+ * @return string
+ *   The escaped table name as a string.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container
+ *   and call escapeTable() on it. For example,
+ *   $injected_database->escapeTable($table);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::escapeTable()
+ */
+function db_escape_table($table) {
+  @trigger_error('db_escape_table() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call escapeTable() on it. For example, $injected_database->escapeTable($table). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->escapeTable($table);
+}
+
+/**
+ * Restricts a dynamic column or constraint name to safe characters.
+ *
+ * Only keeps alphanumeric and underscores.
+ *
+ * @param string $field
+ *   The field name to escape.
+ *
+ * @return string
+ *   The escaped field name as a string.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container
+ *   and call escapeField() on it. For example,
+ *   $injected_database->escapeField($field);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::escapeField()
+ */
+function db_escape_field($field) {
+  @trigger_error('db_escape_field() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call escapeField() on it. For example, $injected_database->escapeField($field). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->escapeField($field);
+}
+
+/**
+ * Escapes characters that work as wildcard characters in a LIKE pattern.
+ *
+ * The wildcard characters "%" and "_" as well as backslash are prefixed with
+ * a backslash. Use this to do a search for a verbatim string without any
+ * wildcard behavior.
+ *
+ * You must use a query builder like \Drupal::database()->select() in order to
+ * use \Drupal::database()->escapeLike() on all supported database systems. Using
+ * \Drupal::database()->escapeLike() with \Drupal::database()->query() or
+ * \Drupal::database()->queryRange() is not supported.
+ *
+ * For example, the following does a case-insensitive query for all rows whose
+ * name starts with $prefix:
+ * @code
+ * $result = \Drupal::database()->select('person', 'p')
+ *   ->fields('p')
+ *   ->condition('name', db_like($prefix) . '%', 'LIKE')
+ *   ->execute()
+ *   ->fetchAll();
+ * @endcode
+ *
+ * Backslash is defined as escape character for LIKE patterns in
+ * DatabaseCondition::mapConditionOperator().
+ *
+ * @param string $string
+ *   The string to escape.
+ *
+ * @return string
+ *   The escaped string.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container
+ *   and call escapeLike() on it. For example,
+ *   $injected_database->escapeLike($string);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::escapeLike()
+ */
+function db_like($string) {
+  @trigger_error('db_like() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call escapeLike() on it. For example, $injected_database->escapeLike($string). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->escapeLike($string);
+}
+
+/**
+ * Retrieves the name of the currently active database driver.
+ *
+ * @return string
+ *   The name of the currently active database driver.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container
+ *   and call driver() on it. For example, $injected_database->driver($string);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::driver()
+ */
+function db_driver() {
+  @trigger_error('db_driver() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call driver() on it. For example, $injected_database->driver($string). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->driver();
+}
+
+/**
+ * Closes the active database connection.
+ *
+ * @param array $options
+ *   An array of options to control which connection is closed. Only the target
+ *   key has any meaning in this case.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Database\Database::closeConnection($target).
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Database::closeConnection()
+ */
+function db_close(array $options = []) {
+  @trigger_error('db_close() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Use \Drupal\Core\Database\Database::closeConnection() instead. See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  Database::closeConnection(_db_get_target($options));
+}
+
+/**
+ * Get target helper.
+ *
+ * Helps get "target" database from the query options.
+ *
+ * @param array $options
+ *   An array of options to control how the query operates. The array is passed
+ *   by reference, and its 'target' key is removed from it during the process,
+ *   so that it will not leak in calls to methods in the Database class.
+ * @param bool $allow_replica
+ *   (Optional) When false, 'replica' connection will be redirected to the
+ *   'default' one. Defaults to TRUE.
+ *
+ * @return string
+ *   The target database key for the database connection.
+ *
+ * @internal
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. There is
+ *   no replacement, this function should not be used. It was introduced in
+ *   Drupal 8.8.0 only as a byproduct of the deprecation of the db_* procedural
+ *   functions.
+ *
+ * @see https://www.drupal.org/node/2993033
+ */
+function _db_get_target(array &$options, $allow_replica = TRUE) {
+  @trigger_error('_db_get_target() is deprecated in drupal:8.8.0. Will be removed before drupal:9.0.0. See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  if (empty($options['target']) || ($options['target'] === 'replica' && !$allow_replica)) {
+    $options['target'] = 'default';
+  }
+  $target = $options['target'];
+  unset($options['target']);
+  return $target;
+}
+
+/**
+ * Retrieves a unique id.
+ *
+ * Use this function if for some reason you can't use a serial field. Using a
+ * serial field is preferred, and InsertQuery::execute() returns the value of
+ * the last ID inserted.
+ *
+ * @param int $existing_id
+ *   After a database import, it might be that the sequences table is behind, so
+ *   by passing in a minimum ID, it can be assured that we never issue the same
+ *   ID.
+ *
+ * @return int
+ *   An integer number larger than any number returned before for this sequence.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container
+ *   and call nextId() on it.
+ *   For example, $injected_database->nextId($existing_id);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Connection::nextId()
+ */
+function db_next_id($existing_id = 0) {
+  @trigger_error('db_next_id() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container and call nextId() on it. For example, $injected_database->nextId($existing_id). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->nextId($existing_id);
+}
+
+/**
+ * Returns a new DatabaseCondition, set to "OR" all conditions together.
+ *
+ * @return \Drupal\Core\Database\Query\Condition
+ *   A new Condition object, set to "OR" all conditions together.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Create
+ *   a \Drupal\Core\Database\Query\Condition object, specifying an OR
+ *   conjunction: new Condition('OR');
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Query\Condition
+ */
+function db_or() {
+  @trigger_error('db_or() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Create a \Drupal\Core\Database\Query\Condition object, specifying an OR conjunction: new Condition(\'OR\'), instead. See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return new Condition('OR');
+}
+
+/**
+ * Returns a new DatabaseCondition, set to "AND" all conditions together.
+ *
+ * @return \Drupal\Core\Database\Query\Condition
+ *   A new Condition object, set to "AND" all conditions together.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Create
+ *   a \Drupal\Core\Database\Query\Condition object, specifying an AND
+ *   conjunction: new Condition('AND');
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Query\Condition
+ */
+function db_and() {
+  @trigger_error('db_and() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Create a \Drupal\Core\Database\Query\Condition object, specifying an AND conjunction: new Condition(\'AND\'), instead. See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return new Condition('AND');
+}
+
+/**
+ * Returns a new DatabaseCondition, set to "XOR" all conditions together.
+ *
+ * @return \Drupal\Core\Database\Query\Condition
+ *   A new Condition object, set to "XOR" all conditions together.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Create
+ *   a \Drupal\Core\Database\Query\Condition object, specifying a XOR
+ *   conjunction: new Condition('XOR');
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Query\Condition
+ */
+function db_xor() {
+  @trigger_error('db_xor() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Create a \Drupal\Core\Database\Query\Condition object, specifying a XOR conjunction: new Condition(\'XOR\'), instead. See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return new Condition('XOR');
+}
+
+/**
+ * Returns a new DatabaseCondition, set to the specified conjunction.
+ *
+ * Internal API function call.  Creating a
+ * \Drupal\Core\Database\Query\Condition object, specifying the desired
+ * conjunction ('AND', 'OR' or 'XOR'), is preferred.
+ *
+ * @param string $conjunction
+ *   The conjunction to use for query conditions (AND, OR or XOR).
+ *
+ * @return \Drupal\Core\Database\Query\Condition
+ *   A new Condition object, set to the specified conjunction.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Create
+ *   a \Drupal\Core\Database\Query\Condition object, specifying the desired
+ *   conjunction: new Condition($conjunction);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Query\Condition
+ */
+function db_condition($conjunction) {
+  @trigger_error('db_condition() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Create a \Drupal\Core\Database\Query\Condition object, specifying the desired conjunction: new Condition($conjunction), instead. See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return new Condition($conjunction);
+}
+
+/**
+ * @} End of "addtogroup database".
+ */
+
+
+/**
+ * @addtogroup schemaapi
+ * @{
+ */
+
+/**
+ * Creates a new table from a Drupal table definition.
+ *
+ * @param string $name
+ *   The name of the table to create.
+ * @param array $table
+ *   A Schema API table definition array.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call createTable() on it. For example,
+ *   $injected_database->schema()->createTable($name, $table);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::createTable()
+ */
+function db_create_table($name, $table) {
+  @trigger_error('db_create_table() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call createTable() on it. For example, $injected_database->schema()->createTable($name, $table). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->createTable($name, $table);
+}
+
+/**
+ * Returns an array of field names from an array of key/index column specifiers.
+ *
+ * This is usually an identity function but if a key/index uses a column prefix
+ * specification, this function extracts just the name.
+ *
+ * @param array $fields
+ *   An array of key/index column specifiers.
+ *
+ * @return array
+ *   An array of field names.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call fieldNames() on it. For example,
+ *   $injected_database->schema()->fieldNames($fields);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::fieldNames()
+ */
+function db_field_names($fields) {
+  @trigger_error('db_field_names() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call fieldNames() on it. For example, $injected_database->schema()->fieldNames($fields). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->fieldNames($fields);
+}
+
+/**
+ * Checks if an index exists in the given table.
+ *
+ * @param string $table
+ *   The name of the table in drupal (no prefixing).
+ * @param string $name
+ *   The name of the index in drupal (no prefixing).
+ *
+ * @return bool
+ *   TRUE if the given index exists, otherwise FALSE.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call indexExists() on it. For example,
+ *   $injected_database->schema()->indexExists($table, $name);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::indexExists()
+ */
+function db_index_exists($table, $name) {
+  @trigger_error('db_index_exists() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call indexExists() on it. For example, $injected_database->schema()->indexExists($table, $name). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->indexExists($table, $name);
+}
+
+/**
+ * Checks if a table exists.
+ *
+ * @param string $table
+ *   The name of the table in drupal (no prefixing).
+ *
+ * @return bool
+ *   TRUE if the given table exists, otherwise FALSE.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call tableExists() on it. For example,
+ *   $injected_database->schema()->tableExists($table);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::tableExists()
+ */
+function db_table_exists($table) {
+  @trigger_error(
+    'db_table_exists() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Use $injected_database->schema()->tableExists($table) instead. See https://www.drupal.org/node/2993033',
+    E_USER_DEPRECATED
+  );
+
+  return Database::getConnection()->schema()->tableExists($table);
+}
+
+/**
+ * Checks if a column exists in the given table.
+ *
+ * @param $table
+ *   The name of the table in drupal (no prefixing).
+ * @param $field
+ *   The name of the field.
+ *
+ * @return bool
+ *   TRUE if the given column exists, otherwise FALSE.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call fieldExists() on it. For example,
+ *   $injected_database->schema()->fieldExists($table, $field);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::fieldExists()
+ */
+function db_field_exists($table, $field) {
+  @trigger_error('db_field_exists() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call fieldExists() on it. For example, $injected_database->schema()->fieldExists($table, $field). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->fieldExists($table, $field);
+}
+
+/**
+ * Finds all tables that are like the specified base table name.
+ *
+ * @param string $table_expression
+ *   An SQL expression, for example "simpletest%" (without the quotes).
+ *
+ * @return array
+ *   Array, both the keys and the values are the matching tables.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call findTables() on it. For example,
+ *   $injected_database->schema()->findTables($table_expression);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::findTables()
+ */
+function db_find_tables($table_expression) {
+  @trigger_error(
+    'db_find_tables() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Use $injected_database->schema()->findTables($table_expression) instead. See https://www.drupal.org/node/2993033',
+    E_USER_DEPRECATED
+  );
+  return Database::getConnection()->schema()->findTables($table_expression);
+}
+
+/**
+ * Renames a table.
+ *
+ * @param $table
+ *   The current name of the table to be renamed.
+ * @param $new_name
+ *   The new name for the table.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call renameTable() on it. For example,
+ *   $injected_database->schema()->renameTable($table, $new_name);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::renameTable()
+ */
+function db_rename_table($table, $new_name) {
+  @trigger_error('db_rename_table() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call renameTable() on it. For example, $injected_database->schema()->renameTable($table, $new_name). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->renameTable($table, $new_name);
+}
+
+/**
+ * Drops a table.
+ *
+ * @param $table
+ *   The table to be dropped.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call dropTable() on it. For example,
+ *   $injected_database->schema()->dropTable($table);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::dropTable()
+ */
+function db_drop_table($table) {
+  @trigger_error('db_drop_table() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Use \Drupal\Core\Database\Database::getConnection()->schema()->dropTable() instead. See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->dropTable($table);
+}
+
+/**
+ * Adds a new field to a table.
+ *
+ * @param $table
+ *   Name of the table to be altered.
+ * @param $field
+ *   Name of the field to be added.
+ * @param array $spec
+ *   The field specification array, as taken from a schema definition. The
+ *   specification may also contain the key 'initial'; the newly-created field
+ *   will be set to the value of the key in all rows. This is most useful for
+ *   creating NOT NULL columns with no default value in existing tables.
+ * @param array $keys_new
+ *   (optional) Keys and indexes specification to be created on the table along
+ *   with adding the field. The format is the same as a table specification, but
+ *   without the 'fields' element. If you are adding a type 'serial' field, you
+ *   MUST specify at least one key or index including it in this array. See
+ *   \Drupal\Core\Database\Schema::changeField() for more explanation why.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call addField() on it. For example,
+ *   $injected_database->schema()->addField($table, $field, $spec, $keys_new);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::addField()
+ * @see \Drupal\Core\Database\Schema::changeField()
+ */
+function db_add_field($table, $field, $spec, $keys_new = []) {
+  @trigger_error('db_add_field() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call addField() on it. For example, $injected_database->schema()->addField($table, $field, $spec, $keys_new). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->addField($table, $field, $spec, $keys_new);
+}
+
+/**
+ * Drops a field.
+ *
+ * @param $table
+ *   The table to be altered.
+ * @param $field
+ *   The field to be dropped.
+ *
+ * @return bool
+ *   TRUE if the field was successfully dropped, FALSE if there was no field by
+ *   that name to begin with.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call dropField() on it. For example,
+ *   $injected_database->schema()->dropField($table, $field);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::dropField()
+ */
+function db_drop_field($table, $field) {
+  @trigger_error('db_drop_field() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call dropField() on it. For example, $injected_database->schema()->dropField($table, $field). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->dropField($table, $field);
+}
+
+/**
+ * Sets the default value for a field.
+ *
+ * @param $table
+ *   The table to be altered.
+ * @param $field
+ *   The field to be altered.
+ * @param $default
+ *   Default value to be set. NULL for 'default NULL'.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call changeField() on it, passing a full field
+ *   specification. For example,
+ *   $injected_database->schema()
+ *     ->changeField($table, $field, $field_new, $spec, $keys_new);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::changeField()
+ */
+function db_field_set_default($table, $field, $default) {
+  @trigger_error('db_field_set_default() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call changeField() on it, passing a full field specification. For example, $injected_database->schema()->changeField($table, $field, $field_new, $spec, $keys_new). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->fieldSetDefault($table, $field, $default);
+}
+
+/**
+ * Sets a field to have no default value.
+ *
+ * @param $table
+ *   The table to be altered.
+ * @param $field
+ *   The field to be altered.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call changeField() on it, passing a full field
+ *   specification. For example,
+ *   $injected_database->schema()
+ *     ->changeField($table, $field, $field_new, $spec, $keys_new);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::changeField()
+ */
+function db_field_set_no_default($table, $field) {
+  @trigger_error('db_field_set_no_default() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call changeField() on it, passing a full field specification. For example, $injected_database->schema()->changeField($table, $field, $field_new, $spec, $keys_new). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->fieldSetNoDefault($table, $field);
+}
+
+/**
+ * Adds a primary key to a database table.
+ *
+ * @param $table
+ *   Name of the table to be altered.
+ * @param $fields
+ *   Array of fields for the primary key.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call addPrimaryKey() on it. For example,
+ *   $injected_database->schema()->addPrimaryKey($table, $fields);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::addPrimaryKey()
+ */
+function db_add_primary_key($table, $fields) {
+  @trigger_error('db_add_primary_key() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call addPrimaryKey() on it. For example, $injected_database->schema()->addPrimaryKey($table, $fields). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->addPrimaryKey($table, $fields);
+}
+
+/**
+ * Drops the primary key of a database table.
+ *
+ * @param $table
+ *   Name of the table to be altered.
+ *
+ * @return bool
+ *   TRUE if the primary key was successfully dropped, FALSE if there was no
+ *   primary key on this table to begin with.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call dropPrimaryKey() on it. For example,
+ *   $injected_database->schema()->dropPrimaryKey($table);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::dropPrimaryKey()
+ */
+function db_drop_primary_key($table) {
+  @trigger_error('db_drop_primary_key() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call dropPrimaryKey() on it. For example, $injected_database->schema()->dropPrimaryKey($table). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->dropPrimaryKey($table);
+}
+
+/**
+ * Adds a unique key.
+ *
+ * @param $table
+ *   The table to be altered.
+ * @param $name
+ *   The name of the key.
+ * @param array $fields
+ *   An array of field names.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call addUniqueKey() on it. For example,
+ *   $injected_database->schema()->addUniqueKey($table, $name, $fields);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::addUniqueKey()
+ */
+function db_add_unique_key($table, $name, $fields) {
+  @trigger_error('db_add_unique_key() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call addUniqueKey() on it. For example, $injected_database->schema()->addUniqueKey($table, $name, $fields). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->addUniqueKey($table, $name, $fields);
+}
+
+/**
+ * Drops a unique key.
+ *
+ * @param $table
+ *   The table to be altered.
+ * @param $name
+ *   The name of the key.
+ *
+ * @return bool
+ *   TRUE if the key was successfully dropped, FALSE if there was no key by
+ *   that name to begin with.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call dropUniqueKey() on it. For example,
+ *   $injected_database->schema()->dropUniqueKey($table, $name);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::dropUniqueKey()
+ */
+function db_drop_unique_key($table, $name) {
+  @trigger_error('db_drop_unique_key() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call dropUniqueKey() on it. For example, $injected_database->schema()->dropUniqueKey($table, $name). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->dropUniqueKey($table, $name);
+}
+
+/**
+ * Adds an index.
+ *
+ * @param $table
+ *   The table to be altered.
+ * @param $name
+ *   The name of the index.
+ * @param array $fields
+ *   An array of field names.
+ * @param array $spec
+ *   The table specification of the table to be altered, as taken from a schema
+ *   definition. See \Drupal\Core\Database\Schema::addIndex() for how to obtain
+ *   this specification.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call addIndex() on it. For example,
+ *   $injected_database->schema()->addIndex($table, $name, $fields, $spec);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see hook_schema()
+ * @see schemaapi
+ * @see \Drupal\Core\Database\Schema::addIndex()
+ */
+function db_add_index($table, $name, $fields, array $spec) {
+  @trigger_error('db_add_index() is deprecated in drupal:8.0.x and will be removed in drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call addIndex() on it. For example, $injected_database->schema()->addIndex($table, $name, $fields, $spec). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  Database::getConnection()->schema()->addIndex($table, $name, $fields, $spec);
+}
+
+/**
+ * Drops an index.
+ *
+ * @param $table
+ *   The table to be altered.
+ * @param $name
+ *   The name of the index.
+ *
+ * @return bool
+ *   TRUE if the index was successfully dropped, FALSE if there was no index
+ *   by that name to begin with.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call dropIndex() on it. For example,
+ *   $injected_database->schema()->dropIndex($table, $name);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::dropIndex()
+ */
+function db_drop_index($table, $name) {
+  @trigger_error('db_drop_index() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call dropIndex() on it. For example, $injected_database->schema()->dropIndex($table, $name). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->dropIndex($table, $name);
+}
+
+/**
+ * Changes a field definition.
+ *
+ * IMPORTANT NOTE: To maintain database portability, you have to explicitly
+ * recreate all indices and primary keys that are using the changed field.
+ *
+ * That means that you have to drop all affected keys and indexes with
+ * \Drupal::database()->schema()->drop{PrimaryKey,UniqueKey,Index}() before
+ * calling \Drupal\Core\Database\Schema::changeField().
+ * To recreate the keys and indices, pass the key definitions as the optional
+ * $keys_new argument directly to \Drupal\Core\Database\Schema::changeField().
+ *
+ * For example, suppose you have:
+ * @code
+ * $schema['foo'] = array(
+ *   'fields' => array(
+ *     'bar' => array('type' => 'int', 'not null' => TRUE)
+ *   ),
+ *   'primary key' => array('bar')
+ * );
+ * @endcode
+ * and you want to change foo.bar to be type serial, leaving it as the primary
+ * key. The correct sequence is:
+ * @code
+ * $schema = \Drupal::database()->schema();
+ * $schema->dropPrimaryKey('foo');
+ * $schema->changeField('foo', 'bar', 'bar',
+ *   array('type' => 'serial', 'not null' => TRUE),
+ *   array('primary key' => array('bar')));
+ * @endcode
+ *
+ * The reasons for this are due to the different database engines:
+ *
+ * On PostgreSQL, changing a field definition involves adding a new field and
+ * dropping an old one which causes any indices, primary keys and sequences
+ * (from serial-type fields) that use the changed field to be dropped.
+ *
+ * On MySQL, all type 'serial' fields must be part of at least one key or index
+ * as soon as they are created. You cannot use
+ * \Drupal::database()->schema()->add{PrimaryKey,UniqueKey,Index}() for this
+ * purpose because the ALTER TABLE command will fail to add the column without
+ * a key or index specification. The solution is to use the optional $keys_new
+ * argument to create the key or index at the same time as field.
+ *
+ * You could use
+ * \Drupal::database()->schema()->add{PrimaryKey,UniqueKey,Index}() in all
+ * cases unless you are converting a field to be type serial. You can use the
+ * $keys_new argument in all cases.
+ *
+ * @param $table
+ *   Name of the table.
+ * @param $field
+ *   Name of the field to change.
+ * @param $field_new
+ *   New name for the field (set to the same as $field if you don't want to
+ *   change the name).
+ * @param $spec
+ *   The field specification for the new field.
+ * @param array $keys_new
+ *   (optional) Keys and indexes specification to be created on the table along
+ *   with changing the field. The format is the same as a table specification
+ *   but without the 'fields' element.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Instead,
+ *   get a database connection injected into your service from the container,
+ *   get its schema driver, and call changeField() on it. For example,
+ *   $injected_database->schema()
+ *     ->changeField($table, $field, $field_new, $spec, $keys_new);
+ *
+ * @see https://www.drupal.org/node/2993033
+ * @see \Drupal\Core\Database\Schema::changeField()
+ */
+function db_change_field($table, $field, $field_new, $spec, $keys_new = []) {
+  @trigger_error('db_change_field() is deprecated in drupal:8.0.0. It will be removed from drupal:9.0.0. Instead, get a database connection injected into your service from the container, get its schema driver, and call changeField() on it. For example, $injected_database->schema()->changeField($table, $field, $field_new, $spec, $keys_new). See https://www.drupal.org/node/2993033', E_USER_DEPRECATED);
+  return Database::getConnection()->schema()->changeField($table, $field, $field_new, $spec, $keys_new);
+}
+
+/**
+ * @} End of "addtogroup schemaapi".
+ */
+
+/**
+ * Sets a session variable specifying the lag time for ignoring a replica
+ * server (A replica server is traditionally referred to as
+ * a "slave" in database server documentation).
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal::service('database.replica_kill_switch')->trigger() instead.
+ *
+ * @see https://www.drupal.org/node/2997500
+ * @see https://www.drupal.org/node/2275877
+ */
+function db_ignore_replica() {
+  @trigger_error('db_ignore_replica() is deprecated in drupal:8.7.0. It will be removed from drupal:9.0.0. Use \Drupal\Core\Database\ReplicaKillSwitch::trigger() instead. See https://www.drupal.org/node/2997500', E_USER_DEPRECATED);
+  \Drupal::service('database.replica_kill_switch')->trigger();
+}

+ 485 - 0
web/core/includes/entity.inc

@@ -0,0 +1,485 @@
+<?php
+
+/**
+ * @file
+ * Entity API for handling entities like nodes or users.
+ */
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Clears the entity render cache for all entity types.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Instead,
+ *   use \Drupal\Core\Entity\EntityViewBuilderInterface::resetCache() on the
+ *   required entity types or invalidate specific cache tags.
+ *
+ * @see https://www.drupal.org/node/3000037
+ * @see \Drupal\Core\Entity\EntityViewBuilderInterface::resetCache()
+ * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getDefinitions()
+ */
+function entity_render_cache_clear() {
+  @trigger_error(__FUNCTION__ . '() is deprecated. Use \Drupal\Core\Entity\EntityViewBuilderInterface::resetCache() on the required entity types or invalidate specific cache tags instead. See https://www.drupal.org/node/3000037', E_USER_DEPRECATED);
+  $entity_manager = Drupal::entityManager();
+  foreach ($entity_manager->getDefinitions() as $entity_type => $info) {
+    if ($entity_manager->hasHandler($entity_type, 'view_builder')) {
+      $entity_manager->getViewBuilder($entity_type)->resetCache();
+    }
+  }
+}
+
+/**
+ * Returns the entity bundle info.
+ *
+ * @param string|null $entity_type
+ *   The entity type whose bundle info should be returned, or NULL for all
+ *   bundles info. Defaults to NULL.
+ *
+ * @return array
+ *   The bundle info for a specific entity type, or all entity types.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getBundleInfo() for a
+ *   single bundle, or
+ *   \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getAllBundleInfo() for
+ *   all bundles.
+ *
+ * @see https://www.drupal.org/node/3051077
+ * @see \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getBundleInfo()
+ * @see \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getAllBundleInfo()
+ */
+function entity_get_bundles($entity_type = NULL) {
+  @trigger_error('entity_get_bundles() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getBundleInfo() for a single bundle, or \Drupal\Core\Entity\EntityTypeBundleInfoInterface::getAllBundleInfo() for all bundles. See https://www.drupal.org/node/3051077', E_USER_DEPRECATED);
+  if (isset($entity_type)) {
+    return \Drupal::entityManager()->getBundleInfo($entity_type);
+  }
+  else {
+    return \Drupal::entityManager()->getAllBundleInfo();
+  }
+}
+
+/**
+ * Loads an entity from the database.
+ *
+ * @param string $entity_type
+ *   The entity type to load, e.g. node or user.
+ * @param mixed $id
+ *   The id of the entity to load.
+ * @param bool $reset
+ *   Whether to reset the internal cache for the requested entity type.
+ *
+ * @return \Drupal\Core\Entity\EntityInterface|null
+ *   The entity object, or NULL if there is no entity with the given ID.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
+ *   entity type storage's load() method.
+ *
+ * @see https://www.drupal.org/node/2266845
+ */
+function entity_load($entity_type, $id, $reset = FALSE) {
+  @trigger_error('entity_load() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity type storage\'s load() method. See https://www.drupal.org/node/2266845', E_USER_DEPRECATED);
+  $controller = \Drupal::entityManager()->getStorage($entity_type);
+  if ($reset) {
+    $controller->resetCache([$id]);
+  }
+  return $controller->load($id);
+}
+
+/**
+ * Loads an entity from the database.
+ *
+ * @param string $entity_type
+ *   The entity type to load, e.g. node or user.
+ * @param int $revision_id
+ *   The id of the entity to load.
+ *
+ * @return \Drupal\Core\Entity\EntityInterface|null
+ *   The entity object, or NULL if there is no entity with the given revision
+ *   id.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
+ *   entity type storage's loadRevision() method.
+ *
+ * @see https://www.drupal.org/node/1818376
+ */
+function entity_revision_load($entity_type, $revision_id) {
+  @trigger_error('entity_revision_load() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity type storage\'s loadRevision() method. See https://www.drupal.org/node/1818376', E_USER_DEPRECATED);
+  return \Drupal::entityManager()
+    ->getStorage($entity_type)
+    ->loadRevision($revision_id);
+}
+
+/**
+ * Deletes an entity revision.
+ *
+ * @param string $entity_type
+ *   The entity type to load, e.g. node or user.
+ * @param $revision_id
+ *   The revision ID to delete.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
+ *   entity type storage's deleteRevision() method.
+ *
+ * @see https://www.drupal.org/node/1818376
+ */
+function entity_revision_delete($entity_type, $revision_id) {
+  @trigger_error('entity_revision_delete() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity type storage\'s deleteRevision() method. See https://www.drupal.org/node/1818376', E_USER_DEPRECATED);
+  \Drupal::entityManager()
+    ->getStorage($entity_type)
+    ->deleteRevision($revision_id);
+}
+
+/**
+ * Loads multiple entities from the database.
+ *
+ * This function should be used whenever you need to load more than one entity
+ * from the database. The entities are loaded into memory and will not require
+ * database access if loaded again during the same page request.
+ *
+ * The actual loading is done through a class that has to implement the
+ * \Drupal\Core\Entity\EntityStorageInterface interface. By default,
+ * \Drupal\Core\Entity\Sql\SqlContentEntityStorage is used for content entities
+ * and Drupal\Core\Config\Entity\ConfigEntityStorage for config entities. Entity
+ * types can specify that a different class should be used by setting the
+ * "handlers['storage']" key in the entity plugin annotation. These classes
+ * can either implement the \Drupal\Core\Entity\EntityStorageInterface
+ * interface, or, most commonly, extend the
+ * \Drupal\Core\Entity\Sql\SqlContentEntityStorage class. See
+ * \Drupal\node\Entity\Node and \Drupal\node\NodeStorage for an example.
+ *
+ * @param string $entity_type
+ *   The entity type to load, e.g. node or user.
+ * @param array $ids
+ *   (optional) An array of entity IDs. If omitted, all entities are loaded.
+ * @param bool $reset
+ *   Whether to reset the internal cache for the requested entity type.
+ *
+ * @return array
+ *   An array of entity objects indexed by their IDs.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
+ *   entity type storage's loadMultiple() method.
+ *
+ * @see https://www.drupal.org/node/2266845
+ */
+function entity_load_multiple($entity_type, array $ids = NULL, $reset = FALSE) {
+  @trigger_error('entity_load_multiple() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity type storage\'s loadMultiple() method. See https://www.drupal.org/node/2266845', E_USER_DEPRECATED);
+  $controller = \Drupal::entityManager()->getStorage($entity_type);
+  if ($reset) {
+    $controller->resetCache($ids);
+  }
+  return $controller->loadMultiple($ids);
+}
+
+/**
+ * Load entities by their property values.
+ *
+ * @param string $entity_type
+ *   The entity type to load, e.g. node or user.
+ * @param array $values
+ *   An associative array where the keys are the property names and the
+ *   values are the values those properties must have.
+ *
+ * @return array
+ *   An array of entity objects indexed by their IDs. Returns an empty array if
+ *   no matching entities are found.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
+ *   entity type storage's loadByProperties() method.
+ *
+ * @see https://www.drupal.org/node/3050910
+ */
+function entity_load_multiple_by_properties($entity_type, array $values) {
+  @trigger_error('entity_load_multiple_by_properties() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity type storage\'s loadByProperties() method. See https://www.drupal.org/node/3050910', E_USER_DEPRECATED);
+  return \Drupal::entityManager()
+    ->getStorage($entity_type)
+    ->loadByProperties($values);
+}
+
+/**
+ * Loads the unchanged, i.e. not modified, entity from the database.
+ *
+ * Unlike entity_load() this function ensures the entity is directly loaded from
+ * the database, thus bypassing any static cache. In particular, this function
+ * is useful to determine changes by comparing the entity being saved to the
+ * stored entity.
+ *
+ * @param $entity_type
+ *   The entity type to load, e.g. node or user.
+ * @param $id
+ *   The ID of the entity to load.
+ *
+ * @return \Drupal\Core\Entity\EntityInterface|null
+ *   The unchanged entity, or FALSE if the entity cannot be loaded.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
+ *   entity type storage's loadUnchanged() method.
+ *
+ * @see https://www.drupal.org/node/1935744
+ */
+function entity_load_unchanged($entity_type, $id) {
+  @trigger_error('entity_load_unchanged() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity type storage\'s loadUnchanged() method. See https://www.drupal.org/node/1935744', E_USER_DEPRECATED);
+  return \Drupal::entityManager()
+    ->getStorage($entity_type)
+    ->loadUnchanged($id);
+}
+
+/**
+ * Deletes multiple entities permanently.
+ *
+ * @param string $entity_type
+ *   The type of the entity.
+ * @param array $ids
+ *   An array of entity IDs of the entities to delete.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use
+ *   the entity storage's delete() method to delete multiple entities:
+ *   @code
+ *     $storage_handler = \Drupal::entityTypeManager()->getStorage($entity_type);
+ *     $entities = $storage_handler->loadMultiple($ids);
+ *     $storage_handler->delete($entities);
+ *   @endcode
+ *
+ * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
+ * @see \Drupal\Core\Entity\EntityStorageInterface::loadMultiple()
+ * @see \Drupal\Core\Entity\EntityStorageInterface::delete()
+ * @see https://www.drupal.org/node/3051072
+ */
+function entity_delete_multiple($entity_type, array $ids) {
+  @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.0.0 and will be removed in drupal:9.0.0. Use the entity storage\'s delete() method to delete multiple entities. @see https://www.drupal.org/node/3051072', E_USER_DEPRECATED);
+  $controller = \Drupal::entityManager()->getStorage($entity_type);
+  $entities = $controller->loadMultiple($ids);
+  $controller->delete($entities);
+}
+
+/**
+ * Constructs a new entity object, without permanently saving it.
+ *
+ * @param string $entity_type
+ *   The type of the entity.
+ * @param array $values
+ *   (optional) An array of values to set, keyed by property name. If the
+ *   entity type has bundles, the bundle key has to be specified.
+ *
+ * @return \Drupal\Core\Entity\EntityInterface
+ *   A new entity object.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use
+ *   The method overriding Entity::create() for the entity type, e.g.
+ *   \Drupal\node\Entity\Node::create() if the entity type is known. If the
+ *   entity type is variable, use the entity storage's create() method to
+ *   construct a new entity:
+ *   @code
+ *     \Drupal::entityTypeManager()->getStorage($entity_type)->create($values);
+ *   @endcode
+ *
+ * @see https://www.drupal.org/node/2266845
+ * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getStorage()
+ * @see \Drupal\Core\Entity\EntityStorageInterface::create()
+ */
+function entity_create($entity_type, array $values = []) {
+  @trigger_error('entity_create() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the create() method of the entity type class directly or \Drupal::entityTypeManager()->getStorage($entity_type)->create($values) instead. See https://www.drupal.org/node/2266845', E_USER_DEPRECATED);
+  return \Drupal::entityManager()
+    ->getStorage($entity_type)
+    ->create($values);
+}
+
+/**
+ * Returns the label of an entity.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity for which to generate the label.
+ * @param $langcode
+ *   (optional) The language code of the language that should be used for
+ *   getting the label. If set to NULL, the entity's default language is
+ *   used.
+ *
+ * @return string|null
+ *   The label of the entity, or NULL if there is no label defined.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use the
+ *   entity's label() method.
+ *
+ * @see https://www.drupal.org/node/2549923
+ * @see \Drupal\Core\Entity\EntityInterface::label()
+ */
+function entity_page_label(EntityInterface $entity, $langcode = NULL) {
+  @trigger_error('entity_page_label() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use the entity\'s label() method. See https://www.drupal.org/node/2549923', E_USER_DEPRECATED);
+  return $entity->label($langcode);
+}
+
+/**
+ * Returns the render array for an entity.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity to be rendered.
+ * @param string $view_mode
+ *   The view mode that should be used to display the entity.
+ * @param string $langcode
+ *   (optional) For which language the entity should be rendered, defaults to
+ *   the current content language.
+ * @param bool $reset
+ *   (optional) Whether to reset the render cache for the requested entity.
+ *   Defaults to FALSE.
+ *
+ * @return array
+ *   A render array for the entity.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use the entity view builder's view() method for creating a render array:
+ *   @code
+ *     $view_builder = \Drupal::entityTypeManager()
+ *       ->getViewBuilder($entity->getEntityTypeId());
+ *     return $view_builder->view($entity, $view_mode, $langcode);
+ *   @endcode
+ *
+ * @see https://www.drupal.org/node/3033656
+ * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getViewBuilder()
+ * @see \Drupal\Core\Entity\EntityViewBuilderInterface::view()
+ */
+function entity_view(EntityInterface $entity, $view_mode, $langcode = NULL, $reset = FALSE) {
+  @trigger_error('entity_view() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal::entityTypeManager()->getViewBuilder($entity->getEntityTypeId())->view($entity, $view_mode, $langcode) instead. See https://www.drupal.org/node/3033656', E_USER_DEPRECATED);
+  $render_controller = \Drupal::entityManager()->getViewBuilder($entity->getEntityTypeId());
+  if ($reset) {
+    $render_controller->resetCache([$entity]);
+  }
+  return $render_controller->view($entity, $view_mode, $langcode);
+}
+
+/**
+ * Returns the render array for the provided entities.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface[] $entities
+ *   The entities to be rendered, must be of the same type.
+ * @param string $view_mode
+ *   The view mode that should be used to display the entity.
+ * @param string $langcode
+ *   (optional) For which language the entity should be rendered, defaults to
+ *   the current content language.
+ * @param bool $reset
+ *   (optional) Whether to reset the render cache for the requested entities.
+ *   Defaults to FALSE.
+ *
+ * @return array
+ *   A render array for the entities, indexed by the same keys as the
+ *   entities array passed in $entities.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use the entity view builder's viewMultiple() method for creating a render
+ *   array for the provided entities:
+ *   @code
+ *     $view_builder = \Drupal::entityTypeManager()
+ *       ->getViewBuilder($entity->getEntityTypeId());
+ *     return $view_builder->viewMultiple($entities, $view_mode, $langcode);
+ *   @endcode
+ *
+ * @see https://www.drupal.org/node/3033656
+ * @see \Drupal\Core\Entity\EntityTypeManagerInterface::getViewBuilder()
+ * @see \Drupal\Core\Entity\EntityViewBuilderInterface::viewMultiple()
+ */
+function entity_view_multiple(array $entities, $view_mode, $langcode = NULL, $reset = FALSE) {
+  @trigger_error('entity_view_multiple() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal::entityTypeManager()->getViewBuilder($entity->getEntityTypeId())->viewMultiple($entities, $view_mode, $langcode) instead. See https://www.drupal.org/node/3033656', E_USER_DEPRECATED);
+  $render_controller = \Drupal::entityManager()->getViewBuilder(reset($entities)->getEntityTypeId());
+  if ($reset) {
+    $render_controller->resetCache($entities);
+  }
+  return $render_controller->viewMultiple($entities, $view_mode, $langcode);
+}
+
+/**
+ * Returns the entity view display associated with a bundle and view mode.
+ *
+ * Use this function when assigning suggested display options for a component
+ * in a given view mode. Note that they will only be actually used at render
+ * time if the view mode itself is configured to use dedicated display settings
+ * for the bundle; if not, the 'default' display is used instead.
+ *
+ * The function reads the entity view display from the current configuration, or
+ * returns a ready-to-use empty one if configuration entry exists yet for this
+ * bundle and view mode. This streamlines manipulation of display objects by
+ * always returning a consistent object that reflects the current state of the
+ * configuration.
+ *
+ * Example usage:
+ * - Set the 'body' field to be displayed and the 'field_image' field to be
+ *   hidden on article nodes in the 'default' display.
+ * @code
+ * entity_get_display('node', 'article', 'default')
+ *   ->setComponent('body', array(
+ *     'type' => 'text_summary_or_trimmed',
+ *     'settings' => array('trim_length' => '200')
+ *     'weight' => 1,
+ *   ))
+ *   ->removeComponent('field_image')
+ *   ->save();
+ * @endcode
+ *
+ * @param string $entity_type
+ *   The entity type.
+ * @param string $bundle
+ *   The bundle.
+ * @param string $view_mode
+ *   The view mode, or 'default' to retrieve the 'default' display object for
+ *   this bundle.
+ *
+ * @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface
+ *   The entity view display associated with the view mode.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   EntityDisplayRepositoryInterface::getViewDisplay() instead.
+ *
+ * @see https://www.drupal.org/node/2835616
+ */
+function entity_get_display($entity_type, $bundle, $view_mode) {
+  @trigger_error('entity_get_display() is deprecated in drupal:8.8.0. It will be removed before drupal:9.0.0. Use \Drupal::service(\'entity_display.repository\')->getViewDisplay() instead. See https://www.drupal.org/node/2835616', E_USER_DEPRECATED);
+  return \Drupal::service('entity_display.repository')
+    ->getViewDisplay($entity_type, $bundle, $view_mode);
+}
+
+/**
+ * Returns the entity form display associated with a bundle and form mode.
+ *
+ * The function reads the entity form display object from the current
+ * configuration, or returns a ready-to-use empty one if no configuration entry
+ * exists yet for this bundle and form mode. This streamlines manipulation of
+ * entity form displays by always returning a consistent object that reflects
+ * the current state of the configuration.
+ *
+ * Example usage:
+ * - Set the 'body' field to be displayed with the 'text_textarea_with_summary'
+ *   widget and the 'field_image' field to be hidden on article nodes in the
+ *  'default' form mode.
+ * @code
+ * entity_get_form_display('node', 'article', 'default')
+ *   ->setComponent('body', array(
+ *     'type' => 'text_textarea_with_summary',
+ *     'weight' => 1,
+ *   ))
+ *   ->setComponent('field_image', array(
+ *     'region' => 'hidden',
+ *   ))
+ *   ->save();
+ * @endcode
+ *
+ * @param string $entity_type
+ *   The entity type.
+ * @param string $bundle
+ *   The bundle.
+ * @param string $form_mode
+ *   The form mode.
+ *
+ * @return \Drupal\Core\Entity\Display\EntityFormDisplayInterface
+ *   The entity form display associated with the given form mode.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   EntityDisplayRepositoryInterface::getFormDisplay() instead.
+ *
+ * @see https://www.drupal.org/node/2835616
+ * @see \Drupal\Core\Entity\EntityStorageInterface::create()
+ * @see \Drupal\Core\Entity\EntityStorageInterface::load()
+ */
+function entity_get_form_display($entity_type, $bundle, $form_mode) {
+  @trigger_error('entity_get_form_display() is deprecated in drupal:8.8.0. It will be removed before drupal:9.0.0. Use \Drupal::service(\'entity_display.repository\')->getFormDisplay() instead. See https://www.drupal.org/node/2835616', E_USER_DEPRECATED);
+  return \Drupal::service('entity_display.repository')
+    ->getFormDisplay($entity_type, $bundle, $form_mode);
+}

+ 365 - 0
web/core/includes/errors.inc

@@ -0,0 +1,365 @@
+<?php
+
+/**
+ * @file
+ * Functions for error handling.
+ */
+
+use Drupal\Component\Render\FormattableMarkup;
+use Drupal\Component\Utility\Xss;
+use Drupal\Core\Installer\InstallerKernel;
+use Drupal\Core\Logger\RfcLogLevel;
+use Drupal\Core\Render\Markup;
+use Drupal\Core\Utility\Error;
+use Symfony\Component\HttpFoundation\Response;
+
+/**
+ * Maps PHP error constants to watchdog severity levels.
+ *
+ * The error constants are documented at
+ * http://php.net/manual/errorfunc.constants.php
+ *
+ * @ingroup logging_severity_levels
+ */
+function drupal_error_levels() {
+  $types = [
+    E_ERROR => ['Error', RfcLogLevel::ERROR],
+    E_WARNING => ['Warning', RfcLogLevel::WARNING],
+    E_PARSE => ['Parse error', RfcLogLevel::ERROR],
+    E_NOTICE => ['Notice', RfcLogLevel::NOTICE],
+    E_CORE_ERROR => ['Core error', RfcLogLevel::ERROR],
+    E_CORE_WARNING => ['Core warning', RfcLogLevel::WARNING],
+    E_COMPILE_ERROR => ['Compile error', RfcLogLevel::ERROR],
+    E_COMPILE_WARNING => ['Compile warning', RfcLogLevel::WARNING],
+    E_USER_ERROR => ['User error', RfcLogLevel::ERROR],
+    E_USER_WARNING => ['User warning', RfcLogLevel::WARNING],
+    E_USER_NOTICE => ['User notice', RfcLogLevel::NOTICE],
+    E_STRICT => ['Strict warning', RfcLogLevel::DEBUG],
+    E_RECOVERABLE_ERROR => ['Recoverable fatal error', RfcLogLevel::ERROR],
+    E_DEPRECATED => ['Deprecated function', RfcLogLevel::DEBUG],
+    E_USER_DEPRECATED => ['User deprecated function', RfcLogLevel::DEBUG],
+  ];
+
+  return $types;
+}
+
+/**
+ * Provides custom PHP error handling.
+ *
+ * @param $error_level
+ *   The level of the error raised.
+ * @param $message
+ *   The error message.
+ * @param $filename
+ *   The filename that the error was raised in.
+ * @param $line
+ *   The line number the error was raised at.
+ * @param $context
+ *   An array that points to the active symbol table at the point the error
+ *   occurred.
+ */
+function _drupal_error_handler_real($error_level, $message, $filename, $line, $context) {
+  if ($error_level & error_reporting()) {
+    $types = drupal_error_levels();
+    list($severity_msg, $severity_level) = $types[$error_level];
+    $backtrace = debug_backtrace();
+    $caller = Error::getLastCaller($backtrace);
+
+    // We treat recoverable errors as fatal.
+    $recoverable = $error_level == E_RECOVERABLE_ERROR;
+    // As __toString() methods must not throw exceptions (recoverable errors)
+    // in PHP, we allow them to trigger a fatal error by emitting a user error
+    // using trigger_error().
+    $to_string = $error_level == E_USER_ERROR && substr($caller['function'], -strlen('__toString()')) == '__toString()';
+    _drupal_log_error([
+      '%type' => isset($types[$error_level]) ? $severity_msg : 'Unknown error',
+      // The standard PHP error handler considers that the error messages
+      // are HTML. We mimick this behavior here.
+      '@message' => Markup::create(Xss::filterAdmin($message)),
+      '%function' => $caller['function'],
+      '%file' => $caller['file'],
+      '%line' => $caller['line'],
+      'severity_level' => $severity_level,
+      'backtrace' => $backtrace,
+      '@backtrace_string' => (new \Exception())->getTraceAsString(),
+    ], $recoverable || $to_string);
+  }
+  // If the site is a test site then fail for user deprecations so they can be
+  // caught by the deprecation error handler.
+  elseif (DRUPAL_TEST_IN_CHILD_SITE && $error_level === E_USER_DEPRECATED) {
+    static $seen = [];
+    if (array_search($message, $seen, TRUE) === FALSE) {
+      // Only report each deprecation once. Too many headers can break some
+      // Chrome and web driver testing.
+      $seen[] = $message;
+      $backtrace = debug_backtrace();
+      $caller = Error::getLastCaller($backtrace);
+      _drupal_error_header(
+        Markup::create(Xss::filterAdmin($message)),
+        'User deprecated function',
+        $caller['function'],
+        $caller['file'],
+        $caller['line']
+      );
+    }
+  }
+}
+
+/**
+ * Determines whether an error should be displayed.
+ *
+ * When in maintenance mode or when error_level is ERROR_REPORTING_DISPLAY_ALL,
+ * all errors should be displayed. For ERROR_REPORTING_DISPLAY_SOME, $error
+ * will be examined to determine if it should be displayed.
+ *
+ * @param $error
+ *   Optional error to examine for ERROR_REPORTING_DISPLAY_SOME.
+ *
+ * @return
+ *   TRUE if an error should be displayed.
+ */
+function error_displayable($error = NULL) {
+  if (defined('MAINTENANCE_MODE')) {
+    return TRUE;
+  }
+  $error_level = _drupal_get_error_level();
+  if ($error_level == ERROR_REPORTING_DISPLAY_ALL || $error_level == ERROR_REPORTING_DISPLAY_VERBOSE) {
+    return TRUE;
+  }
+  if ($error_level == ERROR_REPORTING_DISPLAY_SOME && isset($error)) {
+    return $error['%type'] != 'Notice' && $error['%type'] != 'Strict warning';
+  }
+  return FALSE;
+}
+
+/**
+ * Logs a PHP error or exception and displays an error page in fatal cases.
+ *
+ * @param $error
+ *   An array with the following keys: %type, @message, %function, %file,
+ *   %line, @backtrace_string, severity_level, and backtrace. All the parameters
+ *   are plain-text, with the exception of @message, which needs to be an HTML
+ *   string, and backtrace, which is a standard PHP backtrace.
+ * @param bool $fatal
+ *   TRUE for:
+ *   - An exception is thrown and not caught by something else.
+ *   - A recoverable fatal error, which is a fatal error.
+ *   Non-recoverable fatal errors cannot be logged by Drupal.
+ */
+function _drupal_log_error($error, $fatal = FALSE) {
+  $is_installer = InstallerKernel::installationAttempted();
+
+  // Backtrace array is not a valid replacement value for t().
+  $backtrace = $error['backtrace'];
+  unset($error['backtrace']);
+
+  // When running inside the testing framework, we relay the errors
+  // to the tested site by the way of HTTP headers.
+  if (DRUPAL_TEST_IN_CHILD_SITE && !headers_sent() && (!defined('SIMPLETEST_COLLECT_ERRORS') || SIMPLETEST_COLLECT_ERRORS)) {
+    _drupal_error_header($error['@message'], $error['%type'], $error['%function'], $error['%file'], $error['%line']);
+  }
+
+  $response = new Response();
+
+  // Only call the logger if there is a logger factory available. This can occur
+  // if there is an error while rebuilding the container or during the
+  // installer.
+  if (\Drupal::hasService('logger.factory')) {
+    try {
+      // Provide the PHP backtrace to logger implementations.
+      \Drupal::logger('php')->log($error['severity_level'], '%type: @message in %function (line %line of %file) @backtrace_string.', $error + ['backtrace' => $backtrace]);
+    }
+    catch (\Exception $e) {
+      // We can't log, for example because the database connection is not
+      // available. At least try to log to PHP error log.
+      error_log(strtr('Failed to log error: %type: @message in %function (line %line of %file). @backtrace_string', $error));
+    }
+  }
+
+  // Log fatal errors, so developers can find and debug them.
+  if ($fatal) {
+    error_log(sprintf('%s: %s in %s on line %d %s', $error['%type'], $error['@message'], $error['%file'], $error['%line'], $error['@backtrace_string']));
+  }
+
+  if (PHP_SAPI === 'cli') {
+    if ($fatal) {
+      // When called from CLI, simply output a plain text message.
+      // Should not translate the string to avoid errors producing more errors.
+      $response->setContent(html_entity_decode(strip_tags(new FormattableMarkup('%type: @message in %function (line %line of %file).', $error))) . "\n");
+      $response->send();
+      exit(1);
+    }
+  }
+
+  if (\Drupal::hasRequest() && \Drupal::request()->isXmlHttpRequest()) {
+    if ($fatal) {
+      if (error_displayable($error)) {
+        // When called from JavaScript, simply output the error message.
+        // Should not translate the string to avoid errors producing more errors.
+        $response->setContent(new FormattableMarkup('%type: @message in %function (line %line of %file).', $error));
+        $response->send();
+      }
+      exit;
+    }
+  }
+  else {
+    // Display the message if the current error reporting level allows this type
+    // of message to be displayed, and unconditionally in update.php.
+    $message = '';
+    $class = NULL;
+    if (error_displayable($error)) {
+      $class = 'error';
+
+      // If error type is 'User notice' then treat it as debug information
+      // instead of an error message.
+      // @see debug()
+      if ($error['%type'] == 'User notice') {
+        $error['%type'] = 'Debug';
+        $class = 'status';
+      }
+
+      // Attempt to reduce verbosity by removing DRUPAL_ROOT from the file path
+      // in the message. This does not happen for (false) security.
+      if (\Drupal::hasService('app.root')) {
+        $root_length = strlen(\Drupal::root());
+        if (substr($error['%file'], 0, $root_length) == \Drupal::root()) {
+          $error['%file'] = substr($error['%file'], $root_length + 1);
+        }
+      }
+
+      // Check if verbose error reporting is on.
+      $error_level = _drupal_get_error_level();
+
+      if ($error_level != ERROR_REPORTING_DISPLAY_VERBOSE) {
+        // Without verbose logging, use a simple message.
+
+        // We use \Drupal\Component\Render\FormattableMarkup directly here,
+        // rather than use t() since we are in the middle of error handling, and
+        // we don't want t() to cause further errors.
+        $message = new FormattableMarkup('%type: @message in %function (line %line of %file).', $error);
+      }
+      else {
+        // With verbose logging, we will also include a backtrace.
+
+        // First trace is the error itself, already contained in the message.
+        // While the second trace is the error source and also contained in the
+        // message, the message doesn't contain argument values, so we output it
+        // once more in the backtrace.
+        array_shift($backtrace);
+        // Generate a backtrace containing only scalar argument values.
+        $error['@backtrace'] = Error::formatBacktrace($backtrace);
+        $message = new FormattableMarkup('%type: @message in %function (line %line of %file). <pre class="backtrace">@backtrace</pre>', $error);
+      }
+    }
+
+    if ($fatal) {
+      // We fallback to a maintenance page at this point, because the page generation
+      // itself can generate errors.
+      // Should not translate the string to avoid errors producing more errors.
+      $message = 'The website encountered an unexpected error. Please try again later.' . '<br />' . $message;
+
+      if ($is_installer) {
+        // install_display_output() prints the output and ends script execution.
+        $output = [
+          '#title' => 'Error',
+          '#markup' => $message,
+        ];
+        install_display_output($output, $GLOBALS['install_state'], $response->headers->all());
+        exit;
+      }
+
+      $response->setContent($message);
+      $response->setStatusCode(500, '500 Service unavailable (with message)');
+
+      $response->send();
+      // An exception must halt script execution.
+      exit;
+    }
+
+    if ($message) {
+      if (\Drupal::hasService('session')) {
+        // Message display is dependent on sessions being available.
+        \Drupal::messenger()->addMessage($message, $class, TRUE);
+      }
+      else {
+        print $message;
+      }
+    }
+  }
+}
+
+/**
+ * Returns the current error level.
+ *
+ * This function should only be used to get the current error level prior to the
+ * kernel being booted or before Drupal is installed. In all other situations
+ * the following code is preferred:
+ * @code
+ * \Drupal::config('system.logging')->get('error_level');
+ * @endcode
+ *
+ * @return string
+ *   The current error level.
+ */
+function _drupal_get_error_level() {
+  // Raise the error level to maximum for the installer, so users are able to
+  // file proper bug reports for installer errors. The returned value is
+  // different to the one below, because the installer actually has a
+  // 'config.factory' service, which reads the default 'error_level' value from
+  // System module's default configuration and the default value is not verbose.
+  // @see error_displayable()
+  if (InstallerKernel::installationAttempted()) {
+    return ERROR_REPORTING_DISPLAY_VERBOSE;
+  }
+  $error_level = NULL;
+  // Try to get the error level configuration from database. If this fails,
+  // for example if the database connection is not there, try to read it from
+  // settings.php.
+  try {
+    $error_level = \Drupal::config('system.logging')->get('error_level');
+  }
+  catch (\Exception $e) {
+    $error_level = isset($GLOBALS['config']['system.logging']['error_level']) ? $GLOBALS['config']['system.logging']['error_level'] : ERROR_REPORTING_HIDE;
+  }
+
+  // If there is no container or if it has no config.factory service, we are
+  // possibly in an edge-case error situation while trying to serve a regular
+  // request on a public site, so use the non-verbose default value.
+  return $error_level ?: ERROR_REPORTING_DISPLAY_ALL;
+}
+
+/**
+ * Adds error information to headers so that tests can access it.
+ *
+ * @param $message
+ *   The error message.
+ * @param $type
+ *   The type of error.
+ * @param $function
+ *   The function that emitted the error.
+ * @param $file
+ *   The file that emitted the error.
+ * @param $line
+ *   The line number in file that emitted the error.
+ */
+function _drupal_error_header($message, $type, $function, $file, $line) {
+  // $number does not use drupal_static as it should not be reset
+  // as it uniquely identifies each PHP error.
+  static $number = 0;
+  $assertion = [
+    $message,
+    $type,
+    [
+      'function' => $function,
+      'file' => $file,
+      'line' => $line,
+    ],
+  ];
+  // For non-fatal errors (e.g. PHP notices) _drupal_log_error can be called
+  // multiple times per request. In that case the response is typically
+  // generated outside of the error handler, e.g., in a controller. As a
+  // result it is not possible to use a Response object here but instead the
+  // headers need to be emitted directly.
+  header('X-Drupal-Assertion-' . $number . ': ' . rawurlencode(serialize($assertion)));
+  $number++;
+}

+ 1144 - 0
web/core/includes/file.inc

@@ -0,0 +1,1144 @@
+<?php
+
+/**
+ * @file
+ * API for handling file uploads and server file management.
+ */
+
+use Drupal\Component\FileSecurity\FileSecurity;
+use Drupal\Component\FileSystem\FileSystem as ComponentFileSystem;
+use Drupal\Component\Utility\Environment;
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\File\Exception\FileException;
+use Drupal\Core\File\Exception\FileWriteException;
+use Drupal\Core\File\FileSystem;
+use Drupal\Core\File\FileSystemInterface;
+use Drupal\Core\StreamWrapper\StreamWrapperManager;
+
+/**
+ * Default mode for new directories.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystem::CHMOD_DIRECTORY.
+ *
+ * @see \Drupal\Core\File\FileSystemInterface::chmod()
+ * @see https://www.drupal.org/node/2418133
+ */
+const FILE_CHMOD_DIRECTORY = FileSystem::CHMOD_DIRECTORY;
+
+/**
+ * Default mode for new files.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystem::CHMOD_FILE.
+ *
+ * @see \Drupal\Core\File\FileSystemInterface::chmod()
+ * @see https://www.drupal.org/node/2418133
+ */
+const FILE_CHMOD_FILE = FileSystem::CHMOD_FILE;
+
+/**
+ * @defgroup file File interface
+ * @{
+ * Common file handling functions.
+ */
+
+/**
+ * Flag used to create a directory if not present.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::CREATE_DIRECTORY.
+ */
+const FILE_CREATE_DIRECTORY = FileSystemInterface::CREATE_DIRECTORY;
+
+/**
+ * Flag used to indicate file permissions may be changed.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::MODIFY_PERMISSIONS.
+ */
+const FILE_MODIFY_PERMISSIONS = FileSystemInterface::MODIFY_PERMISSIONS;
+
+/**
+ * Flag for dealing with existing files: Appends number until name is unique.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::EXISTS_RENAME.
+ */
+const FILE_EXISTS_RENAME = FileSystemInterface::EXISTS_RENAME;
+
+/**
+ * Flag for dealing with existing files: Replace the existing file.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::EXISTS_REPLACE.
+ */
+const FILE_EXISTS_REPLACE = FileSystemInterface::EXISTS_REPLACE;
+
+/**
+ * Flag for dealing with existing files: Do nothing and return FALSE.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::EXISTS_ERROR.
+ */
+const FILE_EXISTS_ERROR = FileSystemInterface::EXISTS_ERROR;
+
+/**
+ * Indicates that the file is permanent and should not be deleted.
+ *
+ * Temporary files older than the system.file.temporary_maximum_age
+ * configuration value will be, if clean-up not disabled, removed during cron
+ * runs, but permanent files will not be removed during the file garbage
+ * collection process.
+ */
+const FILE_STATUS_PERMANENT = 1;
+
+/**
+ * Returns the scheme of a URI (e.g. a stream).
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   Drupal\Core\StreamWrapper\StreamWrapperManagerInterface::getScheme()
+ *   instead.
+ *
+ * @see https://www.drupal.org/node/3035273
+ */
+function file_uri_scheme($uri) {
+  @trigger_error('file_uri_scheme() is deprecated in drupal:8.0.0 and will be removed before drupal:9.0.0. Use \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface::getScheme() instead. See https://www.drupal.org/node/3035273', E_USER_DEPRECATED);
+  return StreamWrapperManager::getScheme($uri);
+}
+
+/**
+ * Checks that the scheme of a stream URI is valid.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   Drupal\Core\StreamWrapper\StreamWrapperManagerInterface::isValidScheme()
+ *   instead.
+ *
+ * @see https://www.drupal.org/node/3035273
+ */
+function file_stream_wrapper_valid_scheme($scheme) {
+  @trigger_error('file_stream_wrapper_valid_scheme() is deprecated in drupal:8.0.0 and will be removed before drupal:9.0.0. Use \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface::isValidScheme() instead. See https://www.drupal.org/node/3035273', E_USER_DEPRECATED);
+  return \Drupal::service('stream_wrapper_manager')->isValidScheme($scheme);
+}
+
+/**
+ * Returns the part of a URI after the schema.
+ *
+ * @param string $uri
+ *   A stream, referenced as "scheme://target" or "data:target".
+ *
+ * @return string|bool
+ *   A string containing the target (path), or FALSE if none.
+ *   For example, the URI "public://sample/test.txt" would return
+ *   "sample/test.txt".
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface::getTarget()
+ *   instead.
+ *
+ * @see https://www.drupal.org/node/3035273
+ */
+function file_uri_target($uri) {
+  @trigger_error('file_uri_target() is deprecated in drupal:8.8.0 and will be removed before drupal:9.0.0. Use \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface::getTarget() instead. See https://www.drupal.org/node/3035273', E_USER_DEPRECATED);
+  return StreamWrapperManager::getTarget($uri);
+}
+
+/**
+ * Gets the default file stream implementation.
+ *
+ * @return string
+ *   'public', 'private' or any other file scheme defined as the default.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal::config('system.file')->get('default_scheme') instead.
+ *
+ * @see https://www.drupal.org/node/3049030
+ */
+function file_default_scheme() {
+  @trigger_error('file_default_scheme() is deprecated in drupal:8.8.0. It will be removed from drupal:9.0.0. Use \Drupal::config(\'system.file\')->get(\'default_scheme\') instead. See https://www.drupal.org/node/3049030', E_USER_DEPRECATED);
+  return \Drupal::config('system.file')->get('default_scheme');
+}
+
+/**
+ * Normalizes a URI by making it syntactically correct.
+ *
+ * A stream is referenced as "scheme://target".
+ *
+ * The following actions are taken:
+ * - Remove trailing slashes from target
+ * - Trim erroneous leading slashes from target. e.g. ":///" becomes "://".
+ *
+ * @param string $uri
+ *   String reference containing the URI to normalize.
+ *
+ * @return string
+ *   The normalized URI.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface::normalizeUri()
+ *   instead.
+ *
+ * @see https://www.drupal.org/node/3035273
+ */
+function file_stream_wrapper_uri_normalize($uri) {
+  @trigger_error('file_stream_wrapper_uri_normalize() is deprecated in drupal:8.8.0 and will be removed before drupal:9.0.0. Use \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface::normalizeUri() instead. See https://www.drupal.org/node/3035273', E_USER_DEPRECATED);
+  return \Drupal::service('stream_wrapper_manager')->normalizeUri($uri);
+}
+
+/**
+ * Creates a web-accessible URL for a stream to an external or local file.
+ *
+ * Compatibility: normal paths and stream wrappers.
+ *
+ * There are two kinds of local files:
+ * - "managed files", i.e. those stored by a Drupal-compatible stream wrapper.
+ *   These are files that have either been uploaded by users or were generated
+ *   automatically (for example through CSS aggregation).
+ * - "shipped files", i.e. those outside of the files directory, which ship as
+ *   part of Drupal core or contributed modules or themes.
+ *
+ * @param string $uri
+ *   The URI to a file for which we need an external URL, or the path to a
+ *   shipped file.
+ *
+ * @return string
+ *   A string containing a URL that may be used to access the file.
+ *   If the provided string already contains a preceding 'http', 'https', or
+ *   '/', nothing is done and the same string is returned. If a stream wrapper
+ *   could not be found to generate an external URL, then FALSE is returned.
+ *
+ * @see https://www.drupal.org/node/515192
+ * @see file_url_transform_relative()
+ */
+function file_create_url($uri) {
+  // Allow the URI to be altered, e.g. to serve a file from a CDN or static
+  // file server.
+  \Drupal::moduleHandler()->alter('file_url', $uri);
+
+  $scheme = StreamWrapperManager::getScheme($uri);
+
+  if (!$scheme) {
+    // Allow for:
+    // - root-relative URIs (e.g. /foo.jpg in http://example.com/foo.jpg)
+    // - protocol-relative URIs (e.g. //bar.jpg, which is expanded to
+    //   http://example.com/bar.jpg by the browser when viewing a page over
+    //   HTTP and to https://example.com/bar.jpg when viewing a HTTPS page)
+    // Both types of relative URIs are characterized by a leading slash, hence
+    // we can use a single check.
+    if (mb_substr($uri, 0, 1) == '/') {
+      return $uri;
+    }
+    else {
+      // If this is not a properly formatted stream, then it is a shipped file.
+      // Therefore, return the urlencoded URI with the base URL prepended.
+      $options = UrlHelper::parse($uri);
+      $path = $GLOBALS['base_url'] . '/' . UrlHelper::encodePath($options['path']);
+      // Append the query.
+      if ($options['query']) {
+        $path .= '?' . UrlHelper::buildQuery($options['query']);
+      }
+
+      // Append fragment.
+      if ($options['fragment']) {
+        $path .= '#' . $options['fragment'];
+      }
+
+      return $path;
+    }
+  }
+  elseif ($scheme == 'http' || $scheme == 'https' || $scheme == 'data') {
+    // Check for HTTP and data URI-encoded URLs so that we don't have to
+    // implement getExternalUrl() for the HTTP and data schemes.
+    return $uri;
+  }
+  else {
+    // Attempt to return an external URL using the appropriate wrapper.
+    if ($wrapper = \Drupal::service('stream_wrapper_manager')->getViaUri($uri)) {
+      return $wrapper->getExternalUrl();
+    }
+    else {
+      return FALSE;
+    }
+  }
+}
+
+/**
+ * Transforms an absolute URL of a local file to a relative URL.
+ *
+ * May be useful to prevent problems on multisite set-ups and prevent mixed
+ * content errors when using HTTPS + HTTP.
+ *
+ * @param string $file_url
+ *   A file URL of a local file as generated by file_create_url().
+ *
+ * @return string
+ *   If the file URL indeed pointed to a local file and was indeed absolute,
+ *   then the transformed, relative URL to the local file. Otherwise: the
+ *   original value of $file_url.
+ *
+ * @see file_create_url()
+ */
+function file_url_transform_relative($file_url) {
+  // Unfortunately, we pretty much have to duplicate Symfony's
+  // Request::getHttpHost() method because Request::getPort() may return NULL
+  // instead of a port number.
+  $request = \Drupal::request();
+  $host = $request->getHost();
+  $scheme = $request->getScheme();
+  $port = $request->getPort() ?: 80;
+  if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) {
+    $http_host = $host;
+  }
+  else {
+    $http_host = $host . ':' . $port;
+  }
+
+  return preg_replace('|^https?://' . preg_quote($http_host, '|') . '|', '', $file_url);
+}
+
+/**
+ * Checks that the directory exists and is writable.
+ *
+ * Directories need to have execute permissions to be considered a directory by
+ * FTP servers, etc.
+ *
+ * @param $directory
+ *   A string reference containing the name of a directory path or URI. A
+ *   trailing slash will be trimmed from a path.
+ * @param $options
+ *   A bitmask to indicate if the directory should be created if it does
+ *   not exist (FILE_CREATE_DIRECTORY) or made writable if it is read-only
+ *   (FILE_MODIFY_PERMISSIONS).
+ *
+ * @return
+ *   TRUE if the directory exists (or was created) and is writable. FALSE
+ *   otherwise.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::prepareDirectory().
+ */
+function file_prepare_directory(&$directory, $options = FileSystemInterface::MODIFY_PERMISSIONS) {
+  @trigger_error('file_prepare_directory() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::prepareDirectory(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->prepareDirectory($directory, $options);
+}
+
+/**
+ * Creates a .htaccess file in each Drupal files directory if it is missing.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ * \Drupal\Core\File\HtaccessWriterInterface::ensure() instead.
+ *
+ * @see https://www.drupal.org/node/2940126
+ */
+function file_ensure_htaccess() {
+  @trigger_error("file_ensure_htaccess() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\File\HtaccessWriter::ensure() instead. See https://www.drupal.org/node/2940126", E_USER_DEPRECATED);
+  \Drupal::service('file.htaccess_writer')->ensure();
+}
+
+/**
+ * Creates a .htaccess file in the given directory.
+ *
+ * @param string $directory
+ *   The directory.
+ * @param bool $private
+ *   (Optional) FALSE indicates that $directory should be a web-accessible
+ *   directory. Defaults to TRUE which indicates a private directory.
+ * @param bool $force_overwrite
+ *   (Optional) Set to TRUE to attempt to overwrite the existing .htaccess file
+ *   if one is already present. Defaults to FALSE.
+ *
+ * @return bool
+ *   TRUE when file exists or created successfully, FALSE otherwise.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Component\FileSecurity\FileSecurity::writeHtaccess() instead.
+ *
+ * @see https://www.drupal.org/node/2940126
+ */
+function file_save_htaccess($directory, $private = TRUE, $force_overwrite = FALSE) {
+  @trigger_error('file_save_htaccess() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Component\FileSecurity\FileSecurity::writeHtaccess() instead. See https://www.drupal.org/node/2940126', E_USER_DEPRECATED);
+  return \Drupal::service('file.htaccess_writer')->write($directory, $private, $force_overwrite);
+}
+
+/**
+ * Returns the standard .htaccess lines that Drupal writes to file directories.
+ *
+ * @param bool $private
+ *   (Optional) Set to FALSE to return the .htaccess lines for a web-accessible
+ *   public directory. The default is TRUE, which returns the .htaccess lines
+ *   for a private directory that should not be web-accessible.
+ *
+ * @return string
+ *   The desired contents of the .htaccess file.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Component\FileSecurity\FileSecurity::htaccessLines().
+ *
+ * @see https://www.drupal.org/node/2418133
+ */
+function file_htaccess_lines($private = TRUE) {
+  return FileSecurity::htaccessLines($private);
+}
+
+/**
+ * Determines whether the URI has a valid scheme for file API operations.
+ *
+ * There must be a scheme and it must be a Drupal-provided scheme like
+ * 'public', 'private', 'temporary', or an extension provided with
+ * hook_stream_wrappers().
+ *
+ * @param $uri
+ *   The URI to be tested.
+ *
+ * @return
+ *   TRUE if the URI is allowed.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface::isValidUri()
+ *   instead.
+ *
+ * @see https://www.drupal.org/node/3035273
+ */
+function file_valid_uri($uri) {
+  @trigger_error('file_valid_uri() is deprecated in drupal:8.8.0 and will be removed before drupal:9.0.0. Use \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface::isValidUri() instead. See https://www.drupal.org/node/3035273', E_USER_DEPRECATED);
+  return \Drupal::service('stream_wrapper_manager')->isValidUri($uri);
+}
+
+/**
+ * Copies a file to a new location without database changes or hook invocation.
+ *
+ * This is a powerful function that in many ways performs like an advanced
+ * version of copy().
+ * - Checks if $source and $destination are valid and readable/writable.
+ * - If file already exists in $destination either the call will error out,
+ *   replace the file or rename the file based on the $replace parameter.
+ * - If the $source and $destination are equal, the behavior depends on the
+ *   $replace parameter. FILE_EXISTS_REPLACE will error out. FILE_EXISTS_RENAME
+ *   will rename the file until the $destination is unique.
+ * - Works around a PHP bug where copy() does not properly support streams if
+ *   safe_mode or open_basedir are enabled.
+ *   @see https://bugs.php.net/bug.php?id=60456
+ *
+ * @param $source
+ *   A string specifying the filepath or URI of the source file.
+ * @param $destination
+ *   A URI containing the destination that $source should be copied to. The
+ *   URI may be a bare filepath (without a scheme). If this value is omitted,
+ *   Drupal's default files scheme will be used, usually "public://".
+ * @param $replace
+ *   Replace behavior when the destination file already exists:
+ *   - FILE_EXISTS_REPLACE - Replace the existing file.
+ *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
+ *       unique.
+ *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
+ *
+ * @return
+ *   The path to the new file, or FALSE in the event of an error.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::copy().
+ *
+ * @see file_copy()
+ * @see https://www.drupal.org/node/3006851
+ */
+function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+  @trigger_error('file_unmanaged_copy() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::copy(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED);
+  try {
+    $file_system = \Drupal::service('file_system');
+
+    // Build a destination URI if necessary.
+    if (!isset($destination)) {
+      $destination = file_build_uri($file_system->basename($source));
+    }
+    return $file_system->copy($source, $destination, $replace);
+  }
+  catch (FileException $e) {
+    return FALSE;
+  }
+}
+
+/**
+ * Internal function that prepares the destination for a file_unmanaged_copy or
+ * file_unmanaged_move operation.
+ *
+ * - Checks if $source and $destination are valid and readable/writable.
+ * - Checks that $source is not equal to $destination; if they are an error
+ *   is reported.
+ * - If file already exists in $destination either the call will error out,
+ *   replace the file or rename the file based on the $replace parameter.
+ *
+ * @param $source
+ *   A string specifying the filepath or URI of the source file.
+ * @param $destination
+ *   A URI containing the destination that $source should be moved/copied to.
+ *   The URI may be a bare filepath (without a scheme) and in that case the
+ *   default scheme (file://) will be used. If this value is omitted, Drupal's
+ *   default files scheme will be used, usually "public://".
+ * @param $replace
+ *   Replace behavior when the destination file already exists:
+ *   - FILE_EXISTS_REPLACE - Replace the existing file.
+ *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
+ *       unique.
+ *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
+ *
+ * @return
+ *   TRUE, or FALSE in the event of an error.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::getDestinationFilename() instead.
+ *
+ * @see file_unmanaged_copy()
+ * @see file_unmanaged_move()
+ * @see https://www.drupal.org/node/3006851
+ */
+function file_unmanaged_prepare($source, &$destination = NULL, $replace = FILE_EXISTS_RENAME) {
+  @trigger_error('file_unmanaged_prepare() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::getDestinationFilename() instead. See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED);
+  $original_source = $source;
+  $logger = \Drupal::logger('file');
+  /** @var \Drupal\Core\File\FileSystemInterface $file_system */
+  $file_system = \Drupal::service('file_system');
+
+  // Assert that the source file actually exists.
+  if (!file_exists($source)) {
+    // @todo Replace \Drupal::messenger()->addError() calls with exceptions
+    // instead.
+    \Drupal::messenger()->addError(t('The specified file %file could not be moved/copied because no file by that name exists. Please check that you supplied the correct filename.', ['%file' => $original_source]));
+    if (($realpath = $file_system->realpath($original_source)) !== FALSE) {
+      $logger->notice('File %file (%realpath) could not be moved/copied because it does not exist.', ['%file' => $original_source, '%realpath' => $realpath]);
+    }
+    else {
+      $logger->notice('File %file could not be moved/copied because it does not exist.', ['%file' => $original_source]);
+    }
+    return FALSE;
+  }
+
+  // Build a destination URI if necessary.
+  if (!isset($destination)) {
+    $destination = file_build_uri($file_system->basename($source));
+  }
+
+  // Prepare the destination directory.
+  if (file_prepare_directory($destination)) {
+    // The destination is already a directory, so append the source basename.
+    $destination = file_stream_wrapper_uri_normalize($destination . '/' . $file_system->basename($source));
+  }
+  else {
+    // Perhaps $destination is a dir/file?
+    $dirname = $file_system->dirname($destination);
+    if (!file_prepare_directory($dirname)) {
+      // The destination is not valid.
+      $logger->notice('File %file could not be moved/copied because the destination directory %destination is not configured correctly.', ['%file' => $original_source, '%destination' => $dirname]);
+      \Drupal::messenger()->addError(t('The specified file %file could not be moved/copied because the destination directory is not properly configured. This may be caused by a problem with file or directory permissions. More information is available in the system log.', ['%file' => $original_source]));
+      return FALSE;
+    }
+  }
+
+  // Determine whether we can perform this operation based on overwrite rules.
+  $destination = file_destination($destination, $replace);
+  if ($destination === FALSE) {
+    \Drupal::messenger()->addError(t('The file %file could not be moved/copied because a file by that name already exists in the destination directory.', ['%file' => $original_source]));
+    $logger->notice('File %file could not be moved/copied because a file by that name already exists in the destination directory (%destination)', ['%file' => $original_source, '%destination' => $destination]);
+    return FALSE;
+  }
+
+  // Assert that the source and destination filenames are not the same.
+  $real_source = $file_system->realpath($source);
+  $real_destination = $file_system->realpath($destination);
+  if ($source == $destination || ($real_source !== FALSE) && ($real_source == $real_destination)) {
+    \Drupal::messenger()->addError(t('The specified file %file was not moved/copied because it would overwrite itself.', ['%file' => $source]));
+    $logger->notice('File %file could not be moved/copied because it would overwrite itself.', ['%file' => $source]);
+    return FALSE;
+  }
+  // Make sure the .htaccess files are present.
+  file_ensure_htaccess();
+  return TRUE;
+}
+
+/**
+ * Constructs a URI to Drupal's default files location given a relative path.
+ */
+function file_build_uri($path) {
+  $uri = \Drupal::config('system.file')->get('default_scheme') . '://' . $path;
+  /** @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager */
+  $stream_wrapper_manager = \Drupal::service('stream_wrapper_manager');
+  return $stream_wrapper_manager->normalizeUri($uri);
+}
+
+/**
+ * Determines the destination path for a file.
+ *
+ * @param $destination
+ *   A string specifying the desired final URI or filepath.
+ * @param $replace
+ *   Replace behavior when the destination file already exists.
+ *   - FILE_EXISTS_REPLACE - Replace the existing file.
+ *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
+ *       unique.
+ *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
+ *
+ * @return
+ *   The destination filepath, or FALSE if the file already exists
+ *   and FILE_EXISTS_ERROR is specified.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::getDestinationFilename().
+ *
+ * @see https://www.drupal.org/node/3006851
+ */
+function file_destination($destination, $replace) {
+  @trigger_error('file_destination() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::getDestinationFilename(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->getDestinationFilename($destination, $replace);
+}
+
+/**
+ * Moves a file to a new location without database changes or hook invocation.
+ *
+ * This is a powerful function that in many ways performs like an advanced
+ * version of rename().
+ * - Checks if $source and $destination are valid and readable/writable.
+ * - Checks that $source is not equal to $destination; if they are an error
+ *   is reported.
+ * - If file already exists in $destination either the call will error out,
+ *   replace the file or rename the file based on the $replace parameter.
+ * - Works around a PHP bug where rename() does not properly support streams if
+ *   safe_mode or open_basedir are enabled.
+ *   @see https://bugs.php.net/bug.php?id=60456
+ *
+ * @param $source
+ *   A string specifying the filepath or URI of the source file.
+ * @param $destination
+ *   A URI containing the destination that $source should be moved to. The
+ *   URI may be a bare filepath (without a scheme) and in that case the default
+ *   scheme (file://) will be used. If this value is omitted, Drupal's default
+ *   files scheme will be used, usually "public://".
+ * @param $replace
+ *   Replace behavior when the destination file already exists:
+ *   - FILE_EXISTS_REPLACE - Replace the existing file.
+ *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
+ *       unique.
+ *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
+ *
+ * @return
+ *   The path to the new file, or FALSE in the event of an error.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::move().
+ *
+ * @see file_move()
+ * @see https://www.drupal.org/node/3006851
+ */
+function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+  @trigger_error('file_unmanaged_move() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::move(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED);
+  try {
+    $file_system = \Drupal::service('file_system');
+
+    // Build a destination URI if necessary.
+    if (!isset($destination)) {
+      $destination = file_build_uri($file_system->basename($source));
+    }
+    return $file_system->move($source, $destination, $replace);
+  }
+  catch (FileException $e) {
+    return FALSE;
+  }
+}
+
+/**
+ * Modifies a filename as needed for security purposes.
+ *
+ * Munging a file name prevents unknown file extensions from masking exploit
+ * files. When web servers such as Apache decide how to process a URL request,
+ * they use the file extension. If the extension is not recognized, Apache
+ * skips that extension and uses the previous file extension. For example, if
+ * the file being requested is exploit.php.pps, and Apache does not recognize
+ * the '.pps' extension, it treats the file as PHP and executes it. To make
+ * this file name safe for Apache and prevent it from executing as PHP, the
+ * .php extension is "munged" into .php_, making the safe file name
+ * exploit.php_.pps.
+ *
+ * Specifically, this function adds an underscore to all extensions that are
+ * between 2 and 5 characters in length, internal to the file name, and not
+ * included in $extensions.
+ *
+ * Function behavior is also controlled by the configuration
+ * 'system.file:allow_insecure_uploads'. If it evaluates to TRUE, no alterations
+ * will be made, if it evaluates to FALSE, the filename is 'munged'. *
+ * @param $filename
+ *   File name to modify.
+ * @param $extensions
+ *   A space-separated list of extensions that should not be altered.
+ * @param $alerts
+ *   If TRUE, \Drupal::messenger()->addStatus() will be called to display
+ *   a message if the file name was changed.
+ *
+ * @return string
+ *   The potentially modified $filename.
+ */
+function file_munge_filename($filename, $extensions, $alerts = TRUE) {
+  $original = $filename;
+
+  // Allow potentially insecure uploads for very savvy users and admin
+  if (!\Drupal::config('system.file')->get('allow_insecure_uploads')) {
+    // Remove any null bytes. See
+    // http://php.net/manual/security.filesystem.nullbytes.php
+    $filename = str_replace(chr(0), '', $filename);
+
+    $whitelist = array_unique(explode(' ', strtolower(trim($extensions))));
+
+    // Split the filename up by periods. The first part becomes the basename
+    // the last part the final extension.
+    $filename_parts = explode('.', $filename);
+    // Remove file basename.
+    $new_filename = array_shift($filename_parts);
+    // Remove final extension.
+    $final_extension = array_pop($filename_parts);
+
+    // Loop through the middle parts of the name and add an underscore to the
+    // end of each section that could be a file extension but isn't in the list
+    // of allowed extensions.
+    foreach ($filename_parts as $filename_part) {
+      $new_filename .= '.' . $filename_part;
+      if (!in_array(strtolower($filename_part), $whitelist) && preg_match("/^[a-zA-Z]{2,5}\d?$/", $filename_part)) {
+        $new_filename .= '_';
+      }
+    }
+    $filename = $new_filename . '.' . $final_extension;
+
+    if ($alerts && $original != $filename) {
+      \Drupal::messenger()->addStatus(t('For security reasons, your upload has been renamed to %filename.', ['%filename' => $filename]));
+    }
+  }
+
+  return $filename;
+}
+
+/**
+ * Undoes the effect of file_munge_filename().
+ *
+ * @param $filename
+ *   String with the filename to be unmunged.
+ *
+ * @return
+ *   An unmunged filename string.
+ */
+function file_unmunge_filename($filename) {
+  return str_replace('_.', '.', $filename);
+}
+
+/**
+ * Creates a full file path from a directory and filename.
+ *
+ * If a file with the specified name already exists, an alternative will be
+ * used.
+ *
+ * @param $basename
+ *   String filename
+ * @param $directory
+ *   String containing the directory or parent URI.
+ *
+ * @return
+ *   File path consisting of $directory and a unique filename based off
+ *   of $basename.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::createFilename().
+ *
+ * @see https://www.drupal.org/node/3006851
+ */
+function file_create_filename($basename, $directory) {
+  @trigger_error('file_create_filename() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::createFilename(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->createFilename($basename, $directory);
+}
+
+/**
+ * Deletes a file and its database record.
+ *
+ * Instead of directly deleting a file, it is strongly recommended to delete
+ * file usages instead. That will automatically mark the file as temporary and
+ * remove it during cleanup.
+ *
+ * @param $fid
+ *   The file id.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\Entity\EntityStorageInterface::delete() instead.
+ *
+ * @see file_unmanaged_delete()
+ * @see \Drupal\file\FileUsage\FileUsageBase::delete()
+ * @see \Drupal\Core\Entity\EntityStorageInterface::delete()
+ * @see https://www.drupal.org/node/3021663
+ */
+function file_delete($fid) {
+  @trigger_error('file_delete() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityStorageInterface::delete() instead. See https://www.drupal.org/node/3021663.', E_USER_DEPRECATED);
+  return file_delete_multiple([$fid]);
+}
+
+/**
+ * Deletes files.
+ *
+ * Instead of directly deleting a file, it is strongly recommended to delete
+ * file usages instead. That will automatically mark the file as temporary and
+ * remove it during cleanup.
+ *
+ * @param $fids
+ *   An array of file ids.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\Entity\EntityStorageInterface::delete() instead.
+ *
+ * @see file_unmanaged_delete()
+ * @see \Drupal\file\FileUsage\FileUsageBase::delete()
+ * @see \Drupal\Core\Entity\EntityStorageInterface::delete()
+ * @see https://www.drupal.org/node/3021663
+ */
+function file_delete_multiple(array $fids) {
+  @trigger_error('file_delete_multiple() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityStorageInterface::delete() instead. See https://www.drupal.org/node/3021663.', E_USER_DEPRECATED);
+  $storage = \Drupal::entityTypeManager()->getStorage('file');
+  $entities = $storage->loadMultiple($fids);
+  $storage->delete($entities);
+}
+
+/**
+ * Deletes a file without database changes or hook invocations.
+ *
+ * This function should be used when the file to be deleted does not have an
+ * entry recorded in the files table.
+ *
+ * @param $path
+ *   A string containing a file path or (streamwrapper) URI.
+ *
+ * @return
+ *   TRUE for success or path does not exist, or FALSE in the event of an
+ *   error.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::delete().
+ *
+ * @see file_delete()
+ * @see file_unmanaged_delete_recursive()
+ * @see https://www.drupal.org/node/3006851
+ */
+function file_unmanaged_delete($path) {
+  @trigger_error('file_unmanaged_delete() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::delete(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED);
+  try {
+    return \Drupal::service('file_system')->delete($path);
+  }
+  catch (FileException $e) {
+    return FALSE;
+  }
+}
+
+/**
+ * Deletes all files and directories in the specified filepath recursively.
+ *
+ * If the specified path is a directory then the function will call itself
+ * recursively to process the contents. Once the contents have been removed the
+ * directory will also be removed.
+ *
+ * If the specified path is a file then it will be passed to
+ * file_unmanaged_delete().
+ *
+ * Note that this only deletes visible files with write permission.
+ *
+ * @param $path
+ *   A string containing either an URI or a file or directory path.
+ * @param callable $callback
+ *   (optional) Callback function to run on each file prior to deleting it and
+ *   on each directory prior to traversing it. For example, can be used to
+ *   modify permissions.
+ *
+ * @return
+ *   TRUE for success or if path does not exist, FALSE in the event of an
+ *   error.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::deleteRecursive().
+ *
+ * @see file_unmanaged_delete()
+ * @see https://www.drupal.org/node/3006851
+ */
+function file_unmanaged_delete_recursive($path, $callback = NULL) {
+  @trigger_error('file_unmanaged_delete_recursive() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::deleteRecursive(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED);
+  $callback = is_callable($callback) ? $callback : NULL;
+  try {
+    return \Drupal::service('file_system')->deleteRecursive($path, $callback);
+  }
+  catch (FileException $e) {
+    return FALSE;
+  }
+}
+
+/**
+ * Moves an uploaded file to a new location.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystem::moveUploadedFile().
+ *
+ * @see https://www.drupal.org/node/2418133
+ */
+function drupal_move_uploaded_file($filename, $uri) {
+  @trigger_error('drupal_move_uploaded_file() is deprecated in Drupal 8.0.x-dev and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::moveUploadedFile(). See https://www.drupal.org/node/2418133.', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->moveUploadedFile($filename, $uri);
+}
+
+/**
+ * Saves a file to the specified destination without invoking file API.
+ *
+ * This function is identical to file_save_data() except the file will not be
+ * saved to the {file_managed} table and none of the file_* hooks will be
+ * called.
+ *
+ * @param $data
+ *   A string containing the contents of the file.
+ * @param $destination
+ *   A string containing the destination location. This must be a stream wrapper
+ *   URI. If no value is provided, a randomized name will be generated and the
+ *   file will be saved using Drupal's default files scheme, usually
+ *   "public://".
+ * @param $replace
+ *   Replace behavior when the destination file already exists:
+ *   - FILE_EXISTS_REPLACE - Replace the existing file.
+ *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
+ *                          unique.
+ *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
+ *
+ * @return
+ *   A string with the path of the resulting file, or FALSE on error.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::saveData().
+ *
+ * @see file_save_data()
+ * @see https://www.drupal.org/node/3006851
+ */
+function file_unmanaged_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+  @trigger_error('file_unmanaged_save_data() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::saveData(). See https://www.drupal.org/node/3006851.', E_USER_DEPRECATED);
+  try {
+    // Build a destination URI if necessary.
+    if (!isset($destination)) {
+      $destination = file_default_scheme() . '://';
+    }
+    return \Drupal::service('file_system')->saveData($data, $destination, $replace);
+  }
+  catch (FileWriteException $e) {
+    \Drupal::messenger()->addError(t('The file could not be created.'));
+    return FALSE;
+  }
+  catch (FileException $e) {
+    return FALSE;
+  }
+}
+
+/**
+ * Finds all files that match a given mask in a given directory.
+ *
+ * Directories and files beginning with a dot are excluded; this prevents
+ * hidden files and directories (such as SVN working directories) from being
+ * scanned. Use the umask option to skip configuration directories to
+ * eliminate the possibility of accidentally exposing configuration
+ * information. Also, you can use the base directory, recurse, and min_depth
+ * options to improve performance by limiting how much of the filesystem has
+ * to be traversed.
+ *
+ * @param $dir
+ *   The base directory or URI to scan, without trailing slash.
+ * @param $mask
+ *   The preg_match() regular expression for files to be included.
+ * @param $options
+ *   An associative array of additional options, with the following elements:
+ *   - 'nomask': The preg_match() regular expression for files to be excluded.
+ *     Defaults to the 'file_scan_ignore_directories' setting.
+ *   - 'callback': The callback function to call for each match. There is no
+ *     default callback.
+ *   - 'recurse': When TRUE, the directory scan will recurse the entire tree
+ *     starting at the provided directory. Defaults to TRUE.
+ *   - 'key': The key to be used for the returned associative array of files.
+ *     Possible values are 'uri', for the file's URI; 'filename', for the
+ *     basename of the file; and 'name' for the name of the file without the
+ *     extension. Defaults to 'uri'.
+ *   - 'min_depth': Minimum depth of directories to return files from. Defaults
+ *     to 0.
+ * @param $depth
+ *   The current depth of recursion. This parameter is only used internally and
+ *   should not be passed in.
+ *
+ * @return
+ *   An associative array (keyed on the chosen key) of objects with 'uri',
+ *   'filename', and 'name' properties corresponding to the matched files.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystemInterface::scanDirectory() instead.
+ *
+ * @see https://www.drupal.org/node/3038437
+ */
+function file_scan_directory($dir, $mask, $options = [], $depth = 0) {
+  @trigger_error('file_scan_directory() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\File\FileSystemInterface::scanDirectory() instead. See https://www.drupal.org/node/3038437', E_USER_DEPRECATED);
+  $files = [];
+  try {
+    if (is_dir($dir)) {
+      $files = \Drupal::service('file_system')->scanDirectory($dir, $mask, $options);
+    }
+  }
+  catch (FileException $e) {
+    // Ignore and return empty array for BC.
+  }
+  return $files;
+}
+
+/**
+ * Determines the maximum file upload size by querying the PHP settings.
+ *
+ * @return
+ *   A file size limit in bytes based on the PHP upload_max_filesize and
+ *   post_max_size
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Component\Utility\Environment::getUploadMaxSize() instead.
+ */
+function file_upload_max_size() {
+  @trigger_error('file_upload_max_size() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Component\Utility\Environment::getUploadMaxSize() instead. See https://www.drupal.org/node/3000058.', E_USER_DEPRECATED);
+  return Environment::getUploadMaxSize();
+}
+
+/**
+ * Sets the permissions on a file or directory.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystem::chmod().
+ *
+ * @see https://www.drupal.org/node/2418133
+ */
+function drupal_chmod($uri, $mode = NULL) {
+  @trigger_error('drupal_chmod() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::chmod(). See https://www.drupal.org/node/2418133.', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->chmod($uri, $mode);
+}
+
+/**
+ * Deletes a file.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystem::unlink().
+ *
+ * @see \Drupal\Core\File\FileSystem::unlink()
+ * @see https://www.drupal.org/node/2418133
+ */
+function drupal_unlink($uri, $context = NULL) {
+  @trigger_error('drupal_unlink() is deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::unlink(). See https://www.drupal.org/node/2418133.', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->unlink($uri, $context);
+}
+
+/**
+ * Resolves the absolute filepath of a local URI or filepath.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystem::realpath().
+ *
+ * @see https://www.drupal.org/node/2418133
+ */
+function drupal_realpath($uri) {
+  @trigger_error('drupal_realpath() is deprecated in drupal:8.0.0 and will be removed in drupal:9.0.0. Use \Drupal\Core\File\FileSystemInterface::realpath(). See https://www.drupal.org/node/2418133.', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->realpath($uri);
+}
+
+/**
+ * Gets the name of the directory from a given path.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystem::dirname().
+ *
+ * @see https://www.drupal.org/node/2418133
+ */
+function drupal_dirname($uri) {
+  @trigger_error('drupal_dirname() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::dirname(). See https://www.drupal.org/node/2418133.', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->dirname($uri);
+}
+
+/**
+ * Gets the filename from a given path.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystem::basename().
+ *
+ * @see https://www.drupal.org/node/2418133
+ */
+function drupal_basename($uri, $suffix = NULL) {
+  @trigger_error('drupal_basename() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::basename(). See https://www.drupal.org/node/2418133.', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->basename($uri, $suffix);
+}
+
+/**
+ * Creates a directory, optionally creating missing components in the path to
+ * the directory.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystem::mkdir().
+ *
+ * @see https://www.drupal.org/node/2418133
+ */
+function drupal_mkdir($uri, $mode = NULL, $recursive = FALSE, $context = NULL) {
+  @trigger_error('drupal_mkdir() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::mkdir(). See https://www.drupal.org/node/2418133.', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->mkdir($uri, $mode, $recursive, $context);
+}
+
+/**
+ * Removes a directory.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystem::rmdir().
+ *
+ * @see https://www.drupal.org/node/2418133
+ */
+function drupal_rmdir($uri, $context = NULL) {
+  @trigger_error('drupal_rmdir() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::rmdir(). See https://www.drupal.org/node/2418133.', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->rmdir($uri, $context);
+}
+
+/**
+ * Creates a file with a unique filename in the specified directory.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Core\File\FileSystem::tempnam().
+ *
+ * @see https://www.drupal.org/node/2418133
+ */
+function drupal_tempnam($directory, $prefix) {
+  @trigger_error('tempnam() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\File\FileSystemInterface::tempnam(). See https://www.drupal.org/node/2418133.', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->tempnam($directory, $prefix);
+}
+
+/**
+ * Gets and sets the path of the configured temporary directory.
+ *
+ * @return mixed|null
+ *   A string containing the path to the temporary directory.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\File\FileSystemInterface::getTempDirectory() instead.
+ *
+ * @see \Drupal\Core\File\FileSystemInterface::getTempDirectory()
+ * @see https://www.drupal.org/node/3039255
+ */
+function file_directory_temp() {
+  @trigger_error('file_directory_temp() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\File\FileSystemInterface::getTempDirectory() instead. See https://www.drupal.org/node/3039255', E_USER_DEPRECATED);
+  return \Drupal::service('file_system')->getTempDirectory();
+}
+
+/**
+ * Discovers a writable system-appropriate temporary directory.
+ *
+ * @return mixed
+ *   A string containing the path to the temporary directory.
+ *
+ * @deprecated in drupal:8.3.0 and is removed from drupal:9.0.0.
+ *   Use \Drupal\Component\FileSystem\FileSystem::getOsTemporaryDirectory().
+ *
+ * @see https://www.drupal.org/node/2418133
+ */
+function file_directory_os_temp() {
+  @trigger_error('file_directory_os_temp() is deprecated in drupal:8.3.0 and is removed from drupal:9.0.0. Use \Drupal\Component\FileSystem\FileSystem::getOsTemporaryDirectory() instead. See https://www.drupal.org/node/2418133', E_USER_DEPRECATED);
+  return ComponentFileSystem::getOsTemporaryDirectory();
+}
+
+/**
+ * @} End of "defgroup file".
+ */

+ 1029 - 0
web/core/includes/form.inc

@@ -0,0 +1,1029 @@
+<?php
+
+/**
+ * @file
+ * Functions for form and batch generation and processing.
+ */
+
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Render\Element;
+use Drupal\Core\Render\Element\RenderElement;
+use Drupal\Core\Template\Attribute;
+use Drupal\Core\Url;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+/**
+ * Prepares variables for select element templates.
+ *
+ * Default template: select.html.twig.
+ *
+ * It is possible to group options together; to do this, change the format of
+ * the #options property to an associative array in which the keys are group
+ * labels, and the values are associative arrays in the normal #options format.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #title, #value, #options, #description, #extra,
+ *     #multiple, #required, #name, #attributes, #size, #sort_options,
+ *     #sort_start.
+ */
+function template_preprocess_select(&$variables) {
+  $element = $variables['element'];
+  Element::setAttributes($element, ['id', 'name', 'size']);
+  RenderElement::setAttributes($element, ['form-select']);
+
+  $variables['attributes'] = $element['#attributes'];
+  $variables['options'] = form_select_options($element);
+}
+
+/**
+ * Converts the options in a select element into a structured array for output.
+ *
+ * This function calls itself recursively to obtain the values for each optgroup
+ * within the list of options and when the function encounters an object with
+ * an 'options' property inside $element['#options'].
+ *
+ * @param array $element
+ *   An associative array containing properties of the select element. See
+ *   \Drupal\Core\Render\Element\Select for details, but note that the
+ *   #empty_option and #empty_value properties are processed, and the
+ *   #value property is set, before reaching this function.
+ * @param array|null $choices
+ *   (optional) Either an associative array of options in the same format as
+ *   $element['#options'] above, or NULL. This parameter is only used internally
+ *   and is not intended to be passed in to the initial function call.
+ *
+ * @return mixed[]
+ *   A structured, possibly nested, array of options and optgroups for use in a
+ *   select form element.
+ *   - label: A translated string whose value is the text of a single HTML
+ *     option element, or the label attribute for an optgroup.
+ *   - options: Optional, array of options for an optgroup.
+ *   - selected: A boolean that indicates whether the option is selected when
+ *     rendered.
+ *   - type: A string that defines the element type. The value can be 'option'
+ *     or 'optgroup'.
+ *   - value: A string that contains the value attribute for the option.
+ */
+function form_select_options($element, $choices = NULL) {
+  if (!isset($choices)) {
+    if (empty($element['#options'])) {
+      return [];
+    }
+    $choices = $element['#options'];
+    $sort_options = isset($element['#sort_options']) && $element['#sort_options'];
+    $sort_start = $element['#sort_start'] ?? 0;
+  }
+  else {
+    // We are within an option group.
+    $sort_options = isset($choices['#sort_options']) && $choices['#sort_options'];
+    $sort_start = $choices['#sort_start'] ?? 0;
+    unset($choices['#sort_options']);
+    unset($choices['#sort_start']);
+  }
+
+  // array_key_exists() accommodates the rare event where $element['#value'] is NULL.
+  // isset() fails in this situation.
+  $value_valid = isset($element['#value']) || array_key_exists('#value', $element);
+  $value_is_array = $value_valid && is_array($element['#value']);
+  // Check if the element is multiple select and no value has been selected.
+  $empty_value = (empty($element['#value']) && !empty($element['#multiple']));
+  $options = [];
+  foreach ($choices as $key => $choice) {
+    if (is_array($choice)) {
+      $options[] = [
+        'type' => 'optgroup',
+        'label' => $key,
+        'options' => form_select_options($element, $choice),
+      ];
+    }
+    elseif (is_object($choice) && isset($choice->option)) {
+      $options = array_merge($options, form_select_options($element, $choice->option));
+    }
+    else {
+      $option = [];
+      $key = (string) $key;
+      $empty_choice = $empty_value && $key == '_none';
+      if ($value_valid && ((!$value_is_array && (string) $element['#value'] === $key || ($value_is_array && in_array($key, $element['#value']))) || $empty_choice)) {
+        $option['selected'] = TRUE;
+      }
+      else {
+        $option['selected'] = FALSE;
+      }
+      $option['type'] = 'option';
+      $option['value'] = $key;
+      $option['label'] = $choice;
+      $options[] = $option;
+    }
+  }
+  if ($sort_options) {
+    $unsorted = array_slice($options, 0, $sort_start);
+    $sorted = array_slice($options, $sort_start);
+    uasort($sorted, function ($a, $b) {
+      return strcmp((string) $a['label'], (string) $b['label']);
+    });
+    $options = array_merge($unsorted, $sorted);
+  }
+  return $options;
+}
+
+/**
+ * Returns the indexes of a select element's options matching a given key.
+ *
+ * This function is useful if you need to modify the options that are
+ * already in a form element; for example, to remove choices which are
+ * not valid because of additional filters imposed by another module.
+ * One example might be altering the choices in a taxonomy selector.
+ * To correctly handle the case of a multiple hierarchy taxonomy,
+ * #options arrays can now hold an array of objects, instead of a
+ * direct mapping of keys to labels, so that multiple choices in the
+ * selector can have the same key (and label). This makes it difficult
+ * to manipulate directly, which is why this helper function exists.
+ *
+ * This function does not support optgroups (when the elements of the
+ * #options array are themselves arrays), and will return FALSE if
+ * arrays are found. The caller must either flatten/restore or
+ * manually do their manipulations in this case, since returning the
+ * index is not sufficient, and supporting this would make the
+ * "helper" too complicated and cumbersome to be of any help.
+ *
+ * As usual with functions that can return array() or FALSE, do not
+ * forget to use === and !== if needed.
+ *
+ * @param $element
+ *   The select element to search.
+ * @param $key
+ *   The key to look for.
+ *
+ * @return
+ *   An array of indexes that match the given $key. Array will be
+ *   empty if no elements were found. FALSE if optgroups were found.
+ */
+function form_get_options($element, $key) {
+  $keys = [];
+  foreach ($element['#options'] as $index => $choice) {
+    if (is_array($choice)) {
+      return FALSE;
+    }
+    elseif (is_object($choice)) {
+      if (isset($choice->option[$key])) {
+        $keys[] = $index;
+      }
+    }
+    elseif ($index == $key) {
+      $keys[] = $index;
+    }
+  }
+  return $keys;
+}
+
+/**
+ * Prepares variables for fieldset element templates.
+ *
+ * Default template: fieldset.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #attributes, #children, #description, #id, #title,
+ *     #value.
+ */
+function template_preprocess_fieldset(&$variables) {
+  $element = $variables['element'];
+  Element::setAttributes($element, ['id']);
+  RenderElement::setAttributes($element);
+  $variables['attributes'] = isset($element['#attributes']) ? $element['#attributes'] : [];
+  $variables['prefix'] = isset($element['#field_prefix']) ? $element['#field_prefix'] : NULL;
+  $variables['suffix'] = isset($element['#field_suffix']) ? $element['#field_suffix'] : NULL;
+  $variables['title_display'] = isset($element['#title_display']) ? $element['#title_display'] : NULL;
+  $variables['children'] = $element['#children'];
+  $variables['required'] = !empty($element['#required']) ? $element['#required'] : NULL;
+
+  if (isset($element['#title']) && $element['#title'] !== '') {
+    $variables['legend']['title'] = ['#markup' => $element['#title']];
+  }
+
+  $variables['legend']['attributes'] = new Attribute();
+  // Add 'visually-hidden' class to legend span.
+  if ($variables['title_display'] == 'invisible') {
+    $variables['legend_span']['attributes'] = new Attribute(['class' => ['visually-hidden']]);
+  }
+  else {
+    $variables['legend_span']['attributes'] = new Attribute();
+  }
+
+  if (!empty($element['#description'])) {
+    $description_id = $element['#attributes']['id'] . '--description';
+    $description_attributes['id'] = $description_id;
+    $variables['description']['attributes'] = new Attribute($description_attributes);
+    $variables['description']['content'] = $element['#description'];
+
+    // Add the description's id to the fieldset aria attributes.
+    $variables['attributes']['aria-describedby'] = $description_id;
+  }
+
+  // Suppress error messages.
+  $variables['errors'] = NULL;
+}
+
+/**
+ * Prepares variables for details element templates.
+ *
+ * Default template: details.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #attributes, #children, #description, #required,
+ *     #summary_attributes, #title, #value.
+ */
+function template_preprocess_details(&$variables) {
+  $element = $variables['element'];
+  $variables['attributes'] = $element['#attributes'];
+  $variables['summary_attributes'] = new Attribute($element['#summary_attributes']);
+  if (!empty($element['#title'])) {
+    $variables['summary_attributes']['role'] = 'button';
+    if (!empty($element['#attributes']['id'])) {
+      $variables['summary_attributes']['aria-controls'] = $element['#attributes']['id'];
+    }
+    $variables['summary_attributes']['aria-expanded'] = !empty($element['#attributes']['open']) ? 'true' : 'false';
+    $variables['summary_attributes']['aria-pressed'] = $variables['summary_attributes']['aria-expanded'];
+  }
+  $variables['title'] = (!empty($element['#title'])) ? $element['#title'] : '';
+  // If the element title is a string, wrap it a render array so that markup
+  // will not be escaped (but XSS-filtered).
+  if (is_string($variables['title']) && $variables['title'] !== '') {
+    $variables['title'] = ['#markup' => $variables['title']];
+  }
+  $variables['description'] = (!empty($element['#description'])) ? $element['#description'] : '';
+  $variables['children'] = (isset($element['#children'])) ? $element['#children'] : '';
+  $variables['value'] = (isset($element['#value'])) ? $element['#value'] : '';
+  $variables['required'] = !empty($element['#required']) ? $element['#required'] : NULL;
+
+  // Suppress error messages.
+  $variables['errors'] = NULL;
+}
+
+/**
+ * Prepares variables for radios templates.
+ *
+ * Default template: radios.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #title, #value, #options, #description, #required,
+ *     #attributes, #children.
+ */
+function template_preprocess_radios(&$variables) {
+  $element = $variables['element'];
+  $variables['attributes'] = [];
+  if (isset($element['#id'])) {
+    $variables['attributes']['id'] = $element['#id'];
+  }
+  if (isset($element['#attributes']['title'])) {
+    $variables['attributes']['title'] = $element['#attributes']['title'];
+  }
+  $variables['children'] = $element['#children'];
+}
+
+/**
+ * Prepares variables for checkboxes templates.
+ *
+ * Default template: checkboxes.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #children, #attributes.
+ */
+function template_preprocess_checkboxes(&$variables) {
+  $element = $variables['element'];
+  $variables['attributes'] = [];
+  if (isset($element['#id'])) {
+    $variables['attributes']['id'] = $element['#id'];
+  }
+  if (isset($element['#attributes']['title'])) {
+    $variables['attributes']['title'] = $element['#attributes']['title'];
+  }
+  $variables['children'] = $element['#children'];
+}
+
+/**
+ * Prepares variables for vertical tabs templates.
+ *
+ * Default template: vertical-tabs.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties and children of
+ *     the details element. Properties used: #children.
+ */
+function template_preprocess_vertical_tabs(&$variables) {
+  $element = $variables['element'];
+  $variables['children'] = (!empty($element['#children'])) ? $element['#children'] : '';
+}
+
+/**
+ * Prepares variables for input templates.
+ *
+ * Default template: input.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #attributes.
+ */
+function template_preprocess_input(&$variables) {
+  $element = $variables['element'];
+  // Remove name attribute if empty, for W3C compliance.
+  if (isset($variables['attributes']['name']) && empty((string) $variables['attributes']['name'])) {
+    unset($variables['attributes']['name']);
+  }
+  $variables['children'] = $element['#children'];
+}
+
+/**
+ * Prepares variables for form templates.
+ *
+ * Default template: form.html.twig.
+ *
+ * @param $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #action, #method, #attributes, #children
+ */
+function template_preprocess_form(&$variables) {
+  $element = $variables['element'];
+  if (isset($element['#action'])) {
+    $element['#attributes']['action'] = UrlHelper::stripDangerousProtocols($element['#action']);
+  }
+  Element::setAttributes($element, ['method', 'id']);
+  if (empty($element['#attributes']['accept-charset'])) {
+    $element['#attributes']['accept-charset'] = "UTF-8";
+  }
+  $variables['attributes'] = $element['#attributes'];
+  $variables['children'] = $element['#children'];
+}
+
+/**
+ * Prepares variables for textarea templates.
+ *
+ * Default template: textarea.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #title, #value, #description, #rows, #cols, #maxlength,
+ *     #placeholder, #required, #attributes, #resizable.
+ */
+function template_preprocess_textarea(&$variables) {
+  $element = $variables['element'];
+  $attributes = ['id', 'name', 'rows', 'cols', 'maxlength', 'placeholder'];
+  Element::setAttributes($element, $attributes);
+  RenderElement::setAttributes($element, ['form-textarea']);
+  $variables['wrapper_attributes'] = new Attribute();
+  $variables['attributes'] = new Attribute($element['#attributes']);
+  $variables['value'] = $element['#value'];
+  $variables['resizable'] = !empty($element['#resizable']) ? $element['#resizable'] : NULL;
+  $variables['required'] = !empty($element['#required']) ? $element['#required'] : NULL;
+}
+
+/**
+ * Returns HTML for a form element.
+ * Prepares variables for form element templates.
+ *
+ * Default template: form-element.html.twig.
+ *
+ * In addition to the element itself, the DIV contains a label for the element
+ * based on the optional #title_display property, and an optional #description.
+ *
+ * The optional #title_display property can have these values:
+ * - before: The label is output before the element. This is the default.
+ *   The label includes the #title and the required marker, if #required.
+ * - after: The label is output after the element. For example, this is used
+ *   for radio and checkbox #type elements. If the #title is empty but the field
+ *   is #required, the label will contain only the required marker.
+ * - invisible: Labels are critical for screen readers to enable them to
+ *   properly navigate through forms but can be visually distracting. This
+ *   property hides the label for everyone except screen readers.
+ * - attribute: Set the title attribute on the element to create a tooltip
+ *   but output no label element. This is supported only for checkboxes
+ *   and radios in
+ *   \Drupal\Core\Render\Element\CompositeFormElementTrait::preRenderCompositeFormElement().
+ *   It is used where a visual label is not needed, such as a table of
+ *   checkboxes where the row and column provide the context. The tooltip will
+ *   include the title and required marker.
+ *
+ * If the #title property is not set, then the label and any required marker
+ * will not be output, regardless of the #title_display or #required values.
+ * This can be useful in cases such as the password_confirm element, which
+ * creates children elements that have their own labels and required markers,
+ * but the parent element should have neither. Use this carefully because a
+ * field without an associated label can cause accessibility challenges.
+ *
+ * To associate the label with a different field, set the #label_for property
+ * to the ID of the desired field.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #title, #title_display, #description, #id, #required,
+ *     #children, #type, #name, #label_for.
+ */
+function template_preprocess_form_element(&$variables) {
+  $element = &$variables['element'];
+
+  // This function is invoked as theme wrapper, but the rendered form element
+  // may not necessarily have been processed by
+  // \Drupal::formBuilder()->doBuildForm().
+  $element += [
+    '#title_display' => 'before',
+    '#wrapper_attributes' => [],
+    '#label_attributes' => [],
+    '#label_for' => NULL,
+  ];
+  $variables['attributes'] = $element['#wrapper_attributes'];
+
+  // Add element #id for #type 'item'.
+  if (isset($element['#markup']) && !empty($element['#id'])) {
+    $variables['attributes']['id'] = $element['#id'];
+  }
+
+  // Pass elements #type and #name to template.
+  if (!empty($element['#type'])) {
+    $variables['type'] = $element['#type'];
+  }
+  if (!empty($element['#name'])) {
+    $variables['name'] = $element['#name'];
+  }
+
+  // Pass elements disabled status to template.
+  $variables['disabled'] = !empty($element['#attributes']['disabled']) ? $element['#attributes']['disabled'] : NULL;
+
+  // Suppress error messages.
+  $variables['errors'] = NULL;
+
+  // If #title is not set, we don't display any label.
+  if (!isset($element['#title'])) {
+    $element['#title_display'] = 'none';
+  }
+
+  $variables['title_display'] = $element['#title_display'];
+
+  $variables['prefix'] = isset($element['#field_prefix']) ? $element['#field_prefix'] : NULL;
+  $variables['suffix'] = isset($element['#field_suffix']) ? $element['#field_suffix'] : NULL;
+
+  $variables['description'] = NULL;
+  if (!empty($element['#description'])) {
+    $variables['description_display'] = $element['#description_display'];
+    $description_attributes = [];
+    if (!empty($element['#id'])) {
+      $description_attributes['id'] = $element['#id'] . '--description';
+    }
+    $variables['description']['attributes'] = new Attribute($description_attributes);
+    $variables['description']['content'] = $element['#description'];
+  }
+
+  // Add label_display and label variables to template.
+  $variables['label_display'] = $element['#title_display'];
+  $variables['label'] = ['#theme' => 'form_element_label'];
+  $variables['label'] += array_intersect_key($element, array_flip(['#id', '#required', '#title', '#title_display']));
+  $variables['label']['#attributes'] = $element['#label_attributes'];
+  if (!empty($element['#label_for'])) {
+    $variables['label']['#for'] = $element['#label_for'];
+    if (!empty($element['#id'])) {
+      $variables['label']['#id'] = $element['#id'] . '--label';
+    }
+  }
+
+  $variables['children'] = $element['#children'];
+}
+
+/**
+ * Prepares variables for form label templates.
+ *
+ * Form element labels include the #title and a #required marker. The label is
+ * associated with the element itself by the element #id. Labels may appear
+ * before or after elements, depending on form-element.html.twig and
+ * #title_display.
+ *
+ * This function will not be called for elements with no labels, depending on
+ * #title_display. For elements that have an empty #title and are not required,
+ * this function will output no label (''). For required elements that have an
+ * empty #title, this will output the required marker alone within the label.
+ * The label will use the #id to associate the marker with the field that is
+ * required. That is especially important for screenreader users to know
+ * which field is required.
+ *
+ * To associate the label with a different field, set the #for property to the
+ * ID of the desired field.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #required, #title, #id, #value, #description, #for.
+ */
+function template_preprocess_form_element_label(&$variables) {
+  $element = $variables['element'];
+  // If title and required marker are both empty, output no label.
+  if (isset($element['#title']) && $element['#title'] !== '') {
+    $variables['title'] = ['#markup' => $element['#title']];
+  }
+
+  // Pass elements title_display to template.
+  $variables['title_display'] = $element['#title_display'];
+
+  // A #for property of a dedicated #type 'label' element as precedence.
+  if (!empty($element['#for'])) {
+    $variables['attributes']['for'] = $element['#for'];
+    // A custom #id allows the referenced form input element to refer back to
+    // the label element; e.g., in the 'aria-labelledby' attribute.
+    if (!empty($element['#id'])) {
+      $variables['attributes']['id'] = $element['#id'];
+    }
+  }
+  // Otherwise, point to the #id of the form input element.
+  elseif (!empty($element['#id'])) {
+    $variables['attributes']['for'] = $element['#id'];
+  }
+
+  // Pass elements required to template.
+  $variables['required'] = !empty($element['#required']) ? $element['#required'] : NULL;
+}
+
+/**
+ * @defgroup batch Batch operations
+ * @{
+ * Creates and processes batch operations.
+ *
+ * Functions allowing forms processing to be spread out over several page
+ * requests, thus ensuring that the processing does not get interrupted
+ * because of a PHP timeout, while allowing the user to receive feedback
+ * on the progress of the ongoing operations.
+ *
+ * The API is primarily designed to integrate nicely with the Form API
+ * workflow, but can also be used by non-Form API scripts (like update.php)
+ * or even simple page callbacks (which should probably be used sparingly).
+ *
+ * Example:
+ * @code
+ * $batch = array(
+ *   'title' => t('Exporting'),
+ *   'operations' => array(
+ *     array('my_function_1', array($account->id(), 'story')),
+ *     array('my_function_2', array()),
+ *   ),
+ *   'finished' => 'my_finished_callback',
+ *   'file' => 'path_to_file_containing_myfunctions',
+ * );
+ * batch_set($batch);
+ * // Only needed if not inside a form _submit handler.
+ * // Setting redirect in batch_process.
+ * batch_process('node/1');
+ * @endcode
+ *
+ * Note: if the batch 'title', 'init_message', 'progress_message', or
+ * 'error_message' could contain any user input, it is the responsibility of
+ * the code calling batch_set() to sanitize them first with a function like
+ * \Drupal\Component\Utility\Html::escape() or
+ * \Drupal\Component\Utility\Xss::filter(). Furthermore, if the batch operation
+ * returns any user input in the 'results' or 'message' keys of $context, it
+ * must also sanitize them first.
+ *
+ * Sample callback_batch_operation():
+ * @code
+ * // Simple and artificial: load a node of a given type for a given user
+ * function my_function_1($uid, $type, &$context) {
+ *   // The $context array gathers batch context information about the execution (read),
+ *   // as well as 'return values' for the current operation (write)
+ *   // The following keys are provided :
+ *   // 'results' (read / write): The array of results gathered so far by
+ *   //   the batch processing, for the current operation to append its own.
+ *   // 'message' (write): A text message displayed in the progress page.
+ *   // The following keys allow for multi-step operations :
+ *   // 'sandbox' (read / write): An array that can be freely used to
+ *   //   store persistent data between iterations. It is recommended to
+ *   //   use this instead of $_SESSION, which is unsafe if the user
+ *   //   continues browsing in a separate window while the batch is processing.
+ *   // 'finished' (write): A float number between 0 and 1 informing
+ *   //   the processing engine of the completion level for the operation.
+ *   //   1 (or no value explicitly set) means the operation is finished
+ *   //   and the batch processing can continue to the next operation.
+ *
+ *   $nodes = \Drupal::entityTypeManager()->getStorage('node')
+ *     ->loadByProperties(['uid' => $uid, 'type' => $type]);
+ *   $node = reset($nodes);
+ *   $context['results'][] = $node->id() . ' : ' . Html::escape($node->label());
+ *   $context['message'] = Html::escape($node->label());
+ * }
+ *
+ * // A more advanced example is a multi-step operation that loads all rows,
+ * // five by five.
+ * function my_function_2(&$context) {
+ *   if (empty($context['sandbox'])) {
+ *     $context['sandbox']['progress'] = 0;
+ *     $context['sandbox']['current_id'] = 0;
+ *     $context['sandbox']['max'] = \Drupal::database()
+ *       ->query('SELECT COUNT(DISTINCT id) FROM {example}')
+ *       ->fetchField();
+ *   }
+ *   $limit = 5;
+ *   $result = \Drupal::database()->select('example')
+ *     ->fields('example', array('id'))
+ *     ->condition('id', $context['sandbox']['current_id'], '>')
+ *     ->orderBy('id')
+ *     ->range(0, $limit)
+ *     ->execute();
+ *   foreach ($result as $row) {
+ *     $context['results'][] = $row->id . ' : ' . Html::escape($row->title);
+ *     $context['sandbox']['progress']++;
+ *     $context['sandbox']['current_id'] = $row->id;
+ *     $context['message'] = Html::escape($row->title);
+ *   }
+ *   if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+ *     $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+ *   }
+ * }
+ * @endcode
+ *
+ * Sample callback_batch_finished():
+ * @code
+ * function my_finished_callback($success, $results, $operations) {
+ *   // The 'success' parameter means no fatal PHP errors were detected. All
+ *   // other error management should be handled using 'results'.
+ *   if ($success) {
+ *     $message = \Drupal::translation()->formatPlural(count($results), 'One post processed.', '@count posts processed.');
+ *   }
+ *   else {
+ *     $message = t('Finished with an error.');
+ *   }
+ *   \Drupal::messenger()->addMessage($message);
+ *   // Providing data for the redirected page is done through $_SESSION.
+ *   foreach ($results as $result) {
+ *     $items[] = t('Loaded node %title.', array('%title' => $result));
+ *   }
+ *   $_SESSION['my_batch_results'] = $items;
+ * }
+ * @endcode
+ */
+
+/**
+ * Adds a new batch.
+ *
+ * Batch operations are added as new batch sets. Batch sets are used to spread
+ * processing (primarily, but not exclusively, forms processing) over several
+ * page requests. This helps to ensure that the processing is not interrupted
+ * due to PHP timeouts, while users are still able to receive feedback on the
+ * progress of the ongoing operations. Combining related operations into
+ * distinct batch sets provides clean code independence for each batch set,
+ * ensuring that two or more batches, submitted independently, can be processed
+ * without mutual interference. Each batch set may specify its own set of
+ * operations and results, produce its own UI messages, and trigger its own
+ * 'finished' callback. Batch sets are processed sequentially, with the progress
+ * bar starting afresh for each new set.
+ *
+ * @param $batch_definition
+ *   An associative array defining the batch, with the following elements (all
+ *   are optional except as noted):
+ *   - operations: (required) Array of operations to be performed, where each
+ *     item is an array consisting of the name of an implementation of
+ *     callback_batch_operation() and an array of parameter.
+ *     Example:
+ *     @code
+ *     array(
+ *       array('callback_batch_operation_1', array($arg1)),
+ *       array('callback_batch_operation_2', array($arg2_1, $arg2_2)),
+ *     )
+ *     @endcode
+ *   - title: A safe, translated string to use as the title for the progress
+ *     page. Defaults to t('Processing').
+ *   - init_message: Message displayed while the processing is initialized.
+ *     Defaults to t('Initializing.').
+ *   - progress_message: Message displayed while processing the batch. Available
+ *     placeholders are @current, @remaining, @total, @percentage, @estimate and
+ *     @elapsed. Defaults to t('Completed @current of @total.').
+ *   - error_message: Message displayed if an error occurred while processing
+ *     the batch. Defaults to t('An error has occurred.').
+ *   - finished: Name of an implementation of callback_batch_finished(). This is
+ *     executed after the batch has completed. This should be used to perform
+ *     any result massaging that may be needed, and possibly save data in
+ *     $_SESSION for display after final page redirection.
+ *   - file: Path to the file containing the definitions of the 'operations' and
+ *     'finished' functions, for instance if they don't reside in the main
+ *     .module file. The path should be relative to base_path(), and thus should
+ *     be built using drupal_get_path().
+ *   - library: An array of batch-specific CSS and JS libraries.
+ *   - url_options: options passed to the \Drupal\Core\Url object when
+ *     constructing redirect URLs for the batch.
+ *   - progressive: A Boolean that indicates whether or not the batch needs to
+ *     run progressively. TRUE indicates that the batch will run in more than
+ *     one run. FALSE (default) indicates that the batch will finish in a single
+ *     run.
+ *   - queue: An override of the default queue (with name and class fields
+ *     optional). An array containing two elements:
+ *     - name: Unique identifier for the queue.
+ *     - class: The name of a class that implements
+ *       \Drupal\Core\Queue\QueueInterface, including the full namespace but not
+ *       starting with a backslash. It must have a constructor with two
+ *       arguments: $name and a \Drupal\Core\Database\Connection object.
+ *       Typically, the class will either be \Drupal\Core\Queue\Batch or
+ *       \Drupal\Core\Queue\BatchMemory. Defaults to Batch if progressive is
+ *       TRUE, or to BatchMemory if progressive is FALSE.
+ */
+function batch_set($batch_definition) {
+  if ($batch_definition) {
+    $batch =& batch_get();
+
+    // Initialize the batch if needed.
+    if (empty($batch)) {
+      $batch = [
+        'sets' => [],
+        'has_form_submits' => FALSE,
+      ];
+    }
+
+    // Base and default properties for the batch set.
+    $init = [
+      'sandbox' => [],
+      'results' => [],
+      'success' => FALSE,
+      'start' => 0,
+      'elapsed' => 0,
+    ];
+    $defaults = [
+      'title' => t('Processing'),
+      'init_message' => t('Initializing.'),
+      'progress_message' => t('Completed @current of @total.'),
+      'error_message' => t('An error has occurred.'),
+    ];
+    $batch_set = $init + $batch_definition + $defaults;
+
+    // Tweak init_message to avoid the bottom of the page flickering down after
+    // init phase.
+    $batch_set['init_message'] .= '<br/>&nbsp;';
+
+    // The non-concurrent workflow of batch execution allows us to save
+    // numberOfItems() queries by handling our own counter.
+    $batch_set['total'] = count($batch_set['operations']);
+    $batch_set['count'] = $batch_set['total'];
+
+    // Add the set to the batch.
+    if (empty($batch['id'])) {
+      // The batch is not running yet. Simply add the new set.
+      $batch['sets'][] = $batch_set;
+    }
+    else {
+      // The set is being added while the batch is running.
+      _batch_append_set($batch, $batch_set);
+    }
+  }
+}
+
+/**
+ * Appends a batch set to a running batch.
+ *
+ * Inserts the new set right after the current one to ensure execution order,
+ * and stores its operations in a queue. If the current batch has already
+ * inserted a new set, additional sets will be inserted after the last inserted
+ * set.
+ *
+ * @param &$batch
+ *   The batch array.
+ * @param $batch_set
+ *   The batch set.
+ */
+function _batch_append_set(&$batch, $batch_set) {
+  $append_after_index = $batch['current_set'];
+  $reached_current_set = FALSE;
+  foreach ($batch['sets'] as $index => $set) {
+    // As the indexes are not ordered numerically we need to first reach the
+    // index of the current set and then search for the proper place to append
+    // the new batch set.
+    if (!$reached_current_set) {
+      if ($index == $batch['current_set']) {
+        $reached_current_set = TRUE;
+      }
+      continue;
+    }
+    if ($index > $append_after_index) {
+      if (isset($set['appended_after_index'])) {
+        $append_after_index = $index;
+      }
+      else {
+        break;
+      }
+    }
+  }
+  $batch_set['appended_after_index'] = $append_after_index;
+
+  // Iterate by reference over the existing batch sets and assign them by
+  // reference in the new batch sets array in order not to break a retrieved
+  // reference to the current set. Among other places a reference to the current
+  // set is being retrieved in _batch_process(). Additionally, we have to
+  // preserve the original indexes, as they are used to generate the queue name
+  // of each batch set, otherwise the operations of the new batch set will be
+  // queued in the queue of a previous batch set.
+  // @see _batch_populate_queue().
+  $new_sets = [];
+  foreach ($batch['sets'] as $index => &$set) {
+    $new_sets[$index] = &$set;
+    if ($index == $append_after_index) {
+      $new_set_index = count($batch['sets']);
+      $new_sets[$new_set_index] = $batch_set;
+    }
+  }
+
+  $batch['sets'] = $new_sets;
+  _batch_populate_queue($batch, $new_set_index);
+}
+
+/**
+ * Processes the batch.
+ *
+ * This function is generally not needed in form submit handlers;
+ * Form API takes care of batches that were set during form submission.
+ *
+ * @param \Drupal\Core\Url|string $redirect
+ *   (optional) Either a path or Url object to redirect to when the batch has
+ *   finished processing. For example, to redirect users to the home page, use
+ *   '<front>'. If you wish to allow standard form API batch handling to occur
+ *   and force the user to be redirected to a custom location after the batch
+ *   has finished processing, you do not need to use batch_process() and this
+ *   parameter. Instead, make the batch 'finished' callback return an instance
+ *   of \Symfony\Component\HttpFoundation\RedirectResponse, which will be used
+ *   automatically by the standard batch processing pipeline (and which takes
+ *   precedence over this parameter). If this parameter is omitted and no
+ *   redirect response was returned by the 'finished' callback, the user will
+ *   be redirected to the page that started the batch. Any query arguments will
+ *   be automatically persisted.
+ * @param \Drupal\Core\Url $url
+ *   (optional) URL of the batch processing page. Should only be used for
+ *   separate scripts like update.php.
+ * @param $redirect_callback
+ *   (optional) Specify a function to be called to redirect to the progressive
+ *   processing page.
+ *
+ * @return \Symfony\Component\HttpFoundation\RedirectResponse|null
+ *   A redirect response if the batch is progressive. No return value otherwise.
+ */
+function batch_process($redirect = NULL, Url $url = NULL, $redirect_callback = NULL) {
+  $batch =& batch_get();
+
+  if (isset($batch)) {
+    // Add process information
+    $process_info = [
+      'current_set' => 0,
+      'progressive' => TRUE,
+      'url' => isset($url) ? $url : Url::fromRoute('system.batch_page.html'),
+      'source_url' => Url::fromRouteMatch(\Drupal::routeMatch())->mergeOptions(['query' => \Drupal::request()->query->all()]),
+      'batch_redirect' => $redirect,
+      'theme' => \Drupal::theme()->getActiveTheme()->getName(),
+      'redirect_callback' => $redirect_callback,
+    ];
+    $batch += $process_info;
+
+    // The batch is now completely built. Allow other modules to make changes
+    // to the batch so that it is easier to reuse batch processes in other
+    // environments.
+    \Drupal::moduleHandler()->alter('batch', $batch);
+
+    // Assign an arbitrary id: don't rely on a serial column in the 'batch'
+    // table, since non-progressive batches skip database storage completely.
+    $batch['id'] = \Drupal::database()->nextId();
+
+    // Move operations to a job queue. Non-progressive batches will use a
+    // memory-based queue.
+    foreach ($batch['sets'] as $key => $batch_set) {
+      _batch_populate_queue($batch, $key);
+    }
+
+    // Initiate processing.
+    if ($batch['progressive']) {
+      // Now that we have a batch id, we can generate the redirection link in
+      // the generic error message.
+      /** @var \Drupal\Core\Url $batch_url */
+      $batch_url = $batch['url'];
+      /** @var \Drupal\Core\Url $error_url */
+      $error_url = clone $batch_url;
+      $query_options = $error_url->getOption('query');
+      $query_options['id'] = $batch['id'];
+      $query_options['op'] = 'finished';
+      $error_url->setOption('query', $query_options);
+
+      $batch['error_message'] = t('Please continue to <a href=":error_url">the error page</a>', [':error_url' => $error_url->toString(TRUE)->getGeneratedUrl()]);
+
+      // Clear the way for the redirection to the batch processing page, by
+      // saving and unsetting the 'destination', if there is any.
+      $request = \Drupal::request();
+      if ($request->query->has('destination')) {
+        $batch['destination'] = $request->query->get('destination');
+        $request->query->remove('destination');
+      }
+
+      // Store the batch.
+      \Drupal::service('batch.storage')->create($batch);
+
+      // Set the batch number in the session to guarantee that it will stay alive.
+      $_SESSION['batches'][$batch['id']] = TRUE;
+
+      // Redirect for processing.
+      $query_options = $error_url->getOption('query');
+      $query_options['op'] = 'start';
+      $query_options['id'] = $batch['id'];
+      $batch_url->setOption('query', $query_options);
+      if (($function = $batch['redirect_callback']) && function_exists($function)) {
+        $function($batch_url->toString(), ['query' => $query_options]);
+      }
+      else {
+        return new RedirectResponse($batch_url->setAbsolute()->toString(TRUE)->getGeneratedUrl());
+      }
+    }
+    else {
+      // Non-progressive execution: bypass the whole progressbar workflow
+      // and execute the batch in one pass.
+      require_once __DIR__ . '/batch.inc';
+      _batch_process();
+    }
+  }
+}
+
+/**
+ * Retrieves the current batch.
+ */
+function &batch_get() {
+  // Not drupal_static(), because Batch API operates at a lower level than most
+  // use-cases for resetting static variables, and we specifically do not want a
+  // global drupal_static_reset() resetting the batch information. Functions
+  // that are part of the Batch API and need to reset the batch information may
+  // call batch_get() and manipulate the result by reference. Functions that are
+  // not part of the Batch API can also do this, but shouldn't.
+  static $batch = [];
+  return $batch;
+}
+
+/**
+ * Populates a job queue with the operations of a batch set.
+ *
+ * Depending on whether the batch is progressive or not, the
+ * Drupal\Core\Queue\Batch or Drupal\Core\Queue\BatchMemory handler classes will
+ * be used. The name and class of the queue are added by reference to the
+ * batch set.
+ *
+ * @param $batch
+ *   The batch array.
+ * @param $set_id
+ *   The id of the set to process.
+ */
+function _batch_populate_queue(&$batch, $set_id) {
+  $batch_set = &$batch['sets'][$set_id];
+
+  if (isset($batch_set['operations'])) {
+    $batch_set += [
+      'queue' => [
+        'name' => 'drupal_batch:' . $batch['id'] . ':' . $set_id,
+        'class' => $batch['progressive'] ? 'Drupal\Core\Queue\Batch' : 'Drupal\Core\Queue\BatchMemory',
+      ],
+    ];
+
+    $queue = _batch_queue($batch_set);
+    $queue->createQueue();
+    foreach ($batch_set['operations'] as $operation) {
+      $queue->createItem($operation);
+    }
+
+    unset($batch_set['operations']);
+  }
+}
+
+/**
+ * Returns a queue object for a batch set.
+ *
+ * @param $batch_set
+ *   The batch set.
+ *
+ * @return
+ *   The queue object.
+ */
+function _batch_queue($batch_set) {
+  static $queues;
+
+  if (!isset($queues)) {
+    $queues = [];
+  }
+
+  if (isset($batch_set['queue'])) {
+    $name = $batch_set['queue']['name'];
+    $class = $batch_set['queue']['class'];
+
+    if (!isset($queues[$class][$name])) {
+      $queues[$class][$name] = new $class($name, \Drupal::database());
+    }
+    return $queues[$class][$name];
+  }
+}
+
+/**
+ * @} End of "defgroup batch".
+ */

+ 2468 - 0
web/core/includes/install.core.inc

@@ -0,0 +1,2468 @@
+<?php
+
+/**
+ * @file
+ * API functions for installing Drupal.
+ */
+
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Batch\BatchBuilder;
+use Drupal\Core\Config\ConfigImporter;
+use Drupal\Core\Config\ConfigImporterException;
+use Drupal\Core\Config\Importer\ConfigImporterBatch;
+use Drupal\Core\Config\FileStorage;
+use Drupal\Core\Config\StorageComparer;
+use Drupal\Core\DrupalKernel;
+use Drupal\Core\Database\Database;
+use Drupal\Core\Database\DatabaseExceptionWrapper;
+use Drupal\Core\File\FileSystemInterface;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Installer\Exception\AlreadyInstalledException;
+use Drupal\Core\Installer\Exception\InstallerException;
+use Drupal\Core\Installer\Exception\NoProfilesException;
+use Drupal\Core\Installer\Form\SiteSettingsForm;
+use Drupal\Core\Installer\InstallerKernel;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Logger\LoggerChannelFactory;
+use Drupal\Core\Site\Settings;
+use Drupal\Core\StringTranslation\Translator\FileTranslation;
+use Drupal\Core\StackMiddleware\ReverseProxyMiddleware;
+use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Url;
+use Drupal\language\Entity\ConfigurableLanguage;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Route;
+use Drupal\user\Entity\User;
+use GuzzleHttp\Exception\RequestException;
+
+/**
+ * Do not run the task during the current installation request.
+ *
+ * This can be used to skip running an installation task when certain
+ * conditions are met, even though the task may still show on the list of
+ * installation tasks presented to the user. For example, the Drupal installer
+ * uses this flag to skip over the database configuration form when valid
+ * database connection information is already available from settings.php. It
+ * also uses this flag to skip language import tasks when the installation is
+ * being performed in English.
+ */
+const INSTALL_TASK_SKIP = 1;
+
+/**
+ * Run the task on each installation request that reaches it.
+ *
+ * This is primarily used by the Drupal installer for bootstrap-related tasks.
+ */
+const INSTALL_TASK_RUN_IF_REACHED = 2;
+
+/**
+ * Run the task on each installation request until the database is set up.
+ *
+ * This is the default method for running tasks and should be used for most
+ * tasks that occur after the database is set up; these tasks will then run
+ * once and be marked complete once they are successfully finished. For
+ * example, the Drupal installer uses this flag for the batch installation of
+ * modules on the new site, and also for the configuration form that collects
+ * basic site information and sets up the site maintenance account.
+ */
+const INSTALL_TASK_RUN_IF_NOT_COMPLETED = 3;
+
+/**
+ * Installs Drupal either interactively or via an array of passed-in settings.
+ *
+ * The Drupal installation happens in a series of steps, which may be spread
+ * out over multiple page requests. Each request begins by trying to determine
+ * the last completed installation step (also known as a "task"), if one is
+ * available from a previous request. Control is then passed to the task
+ * handler, which processes the remaining tasks that need to be run until (a)
+ * an error is thrown, (b) a new page needs to be displayed, or (c) the
+ * installation finishes (whichever happens first).
+ *
+ * @param $class_loader
+ *   The class loader. Normally Composer's ClassLoader, as included by the
+ *   front controller, but may also be decorated; e.g.,
+ *   \Symfony\Component\ClassLoader\ApcClassLoader.
+ * @param $settings
+ *   An optional array of installation settings. Leave this empty for a normal,
+ *   interactive, browser-based installation intended to occur over multiple
+ *   page requests. Alternatively, if an array of settings is passed in, the
+ *   installer will attempt to use it to perform the installation in a single
+ *   page request (optimized for the command line) and not send any output
+ *   intended for the web browser. See install_state_defaults() for a list of
+ *   elements that are allowed to appear in this array.
+ * @param callable $callback
+ *   (optional) A callback to allow command line processes to update a progress
+ *   bar. The callback is passed the $install_state variable.
+ *
+ * @see install_state_defaults()
+ */
+function install_drupal($class_loader, $settings = [], callable $callback = NULL) {
+  global $install_state;
+  // Initialize the installation state with the settings that were passed in,
+  // as well as a boolean indicating whether or not this is an interactive
+  // installation.
+  $interactive = empty($settings);
+  $install_state = $settings + ['interactive' => $interactive] + install_state_defaults();
+
+  try {
+    // Begin the page request. This adds information about the current state of
+    // the Drupal installation to the passed-in array.
+    install_begin_request($class_loader, $install_state);
+    // Based on the installation state, run the remaining tasks for this page
+    // request, and collect any output.
+    $output = install_run_tasks($install_state, $callback);
+  }
+  catch (InstallerException $e) {
+    // In the non-interactive installer, exceptions are always thrown directly.
+    if (!$install_state['interactive']) {
+      throw $e;
+    }
+    $output = [
+      '#title' => $e->getTitle(),
+      '#markup' => $e->getMessage(),
+    ];
+  }
+
+  // After execution, all tasks might be complete, in which case
+  // $install_state['installation_finished'] is TRUE. In case the last task
+  // has been processed, remove the global $install_state, so other code can
+  // reliably check whether it is running during the installer.
+  // @see drupal_installation_attempted()
+  $state = $install_state;
+  if (!empty($install_state['installation_finished'])) {
+    unset($GLOBALS['install_state']);
+    // If installation is finished ensure any further container rebuilds do not
+    // use the installer's service provider.
+    unset($GLOBALS['conf']['container_service_providers']['InstallerServiceProvider']);
+  }
+
+  // All available tasks for this page request are now complete. Interactive
+  // installations can send output to the browser or redirect the user to the
+  // next page.
+  if ($state['interactive']) {
+    // If a session has been initiated in this request, make sure to save it.
+    if (\Drupal::request()->hasSession()) {
+      \Drupal::request()->getSession()->save();
+    }
+    if ($state['parameters_changed']) {
+      // Redirect to the correct page if the URL parameters have changed.
+      install_goto(install_redirect_url($state));
+    }
+    elseif (isset($output)) {
+      // Display a page only if some output is available. Otherwise it is
+      // possible that we are printing a JSON page and theme output should
+      // not be shown.
+      install_display_output($output, $state);
+    }
+    elseif ($state['installation_finished']) {
+      // Redirect to the newly installed site.
+      $finish_url = '';
+      if (isset($install_state['profile_info']['distribution']['install']['finish_url'])) {
+        $finish_url = $install_state['profile_info']['distribution']['install']['finish_url'];
+      }
+      install_goto($finish_url);
+    }
+  }
+}
+
+/**
+ * Returns an array of default settings for the global installation state.
+ *
+ * The installation state is initialized with these settings at the beginning
+ * of each page request. They may evolve during the page request, but they are
+ * initialized again once the next request begins.
+ *
+ * Non-interactive Drupal installations can override some of these default
+ * settings by passing in an array to the installation script, most notably
+ * 'parameters' (which contains one-time parameters such as 'profile' and
+ * 'langcode' that are normally passed in via the URL) and 'forms' (which can
+ * be used to programmatically submit forms during the installation; the keys
+ * of each element indicate the name of the installation task that the form
+ * submission is for, and the values are used as the $form_state->getValues()
+ * array that is passed on to the form submission via
+ * \Drupal::formBuilder()->submitForm()).
+ *
+ * @see \Drupal\Core\Form\FormBuilderInterface::submitForm()
+ */
+function install_state_defaults() {
+  $defaults = [
+    // The current task being processed.
+    'active_task' => NULL,
+    // The last task that was completed during the previous installation
+    // request.
+    'completed_task' => NULL,
+    // Partial configuration cached during an installation from existing config.
+    'config' => NULL,
+    // The path to the configuration to install when installing from config.
+    'config_install_path' => NULL,
+    // TRUE when there are valid config directories.
+    'config_verified' => FALSE,
+    // TRUE when there is a valid database connection.
+    'database_verified' => FALSE,
+    // TRUE when a valid settings.php exists (containing both database
+    // connection information and config directory names).
+    'settings_verified' => FALSE,
+    // TRUE when the base system has been installed and is ready to operate.
+    'base_system_verified' => FALSE,
+    // Whether a translation file for the selected language will be downloaded
+    // from the translation server.
+    'download_translation' => FALSE,
+    // An array of forms to be programmatically submitted during the
+    // installation. The keys of each element indicate the name of the
+    // installation task that the form submission is for, and the values are
+    // used as the $form_state->getValues() array that is passed on to the form
+    // submission via \Drupal::formBuilder()->submitForm().
+    'forms' => [],
+    // This becomes TRUE only at the end of the installation process, after
+    // all available tasks have been completed and Drupal is fully installed.
+    // It is used by the installer to store correct information in the database
+    // about the completed installation, as well as to inform theme functions
+    // that all tasks are finished (so that the task list can be displayed
+    // correctly).
+    'installation_finished' => FALSE,
+    // Whether or not this installation is interactive. By default this will
+    // be set to FALSE if settings are passed in to install_drupal().
+    'interactive' => TRUE,
+    // An array of parameters for the installation, pre-populated by the URL
+    // or by the settings passed in to install_drupal(). This is primarily
+    // used to store 'profile' (the name of the chosen installation profile)
+    // and 'langcode' (the code of the chosen installation language), since
+    // these settings need to persist from page request to page request before
+    // the database is available for storage.
+    'parameters' => [],
+    // Whether or not the parameters have changed during the current page
+    // request. For interactive installations, this will trigger a page
+    // redirect.
+    'parameters_changed' => FALSE,
+    // An array of information about the chosen installation profile. This will
+    // be filled in based on the profile's .info.yml file.
+    'profile_info' => [],
+    // An array of available installation profiles.
+    'profiles' => [],
+    // The name of the theme to use during installation.
+    'theme' => 'seven',
+    // The server URL where the interface translation files can be downloaded.
+    // Tokens in the pattern will be replaced by appropriate values for the
+    // required translation file.
+    'server_pattern' => 'http://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po',
+    // Installation tasks can set this to TRUE to force the page request to
+    // end (even if there is no themable output), in the case of an interactive
+    // installation. This is needed only rarely; for example, it would be used
+    // by an installation task that prints JSON output rather than returning a
+    // themed page. The most common example of this is during batch processing,
+    // but the Drupal installer automatically takes care of setting this
+    // parameter properly in that case, so that individual installation tasks
+    // which implement the batch API do not need to set it themselves.
+    'stop_page_request' => FALSE,
+    // Installation tasks can set this to TRUE to indicate that the task should
+    // be run again, even if it normally wouldn't be. This can be used, for
+    // example, if a single task needs to be spread out over multiple page
+    // requests, or if it needs to perform some validation before allowing
+    // itself to be marked complete. The most common examples of this are batch
+    // processing and form submissions, but the Drupal installer automatically
+    // takes care of setting this parameter properly in those cases, so that
+    // individual installation tasks which implement the batch API or form API
+    // do not need to set it themselves.
+    'task_not_complete' => FALSE,
+    // A list of installation tasks which have already been performed during
+    // the current page request.
+    'tasks_performed' => [],
+    // An array of translation files URIs available for the installation. Keyed
+    // by the translation language code.
+    'translations' => [],
+  ];
+  return $defaults;
+}
+
+/**
+ * Begins an installation request, modifying the installation state as needed.
+ *
+ * This function performs commands that must run at the beginning of every page
+ * request. It throws an exception if the installation should not proceed.
+ *
+ * @param $class_loader
+ *   The class loader. Normally Composer's ClassLoader, as included by the
+ *   front controller, but may also be decorated; e.g.,
+ *   \Symfony\Component\ClassLoader\ApcClassLoader.
+ * @param $install_state
+ *   An array of information about the current installation state. This is
+ *   modified with information gleaned from the beginning of the page request.
+ *
+ * @see install_drupal()
+ */
+function install_begin_request($class_loader, &$install_state) {
+  $request = Request::createFromGlobals();
+
+  // Add any installation parameters passed in via the URL.
+  if ($install_state['interactive']) {
+    $install_state['parameters'] += $request->query->all();
+  }
+
+  // Validate certain core settings that are used throughout the installation.
+  if (!empty($install_state['parameters']['profile'])) {
+    $install_state['parameters']['profile'] = preg_replace('/[^a-zA-Z_0-9]/', '', $install_state['parameters']['profile']);
+  }
+  if (!empty($install_state['parameters']['langcode'])) {
+    $install_state['parameters']['langcode'] = preg_replace('/[^a-zA-Z_0-9\-]/', '', $install_state['parameters']['langcode']);
+  }
+
+  // Allow command line scripts to override server variables used by Drupal.
+  require_once __DIR__ . '/bootstrap.inc';
+
+  // If the hash salt leaks, it becomes possible to forge a valid testing user
+  // agent, install a new copy of Drupal, and take over the original site.
+  // The user agent header is used to pass a database prefix in the request when
+  // running tests. However, for security reasons, it is imperative that no
+  // installation be permitted using such a prefix.
+  $user_agent = $request->cookies->get('SIMPLETEST_USER_AGENT') ?: $request->server->get('HTTP_USER_AGENT');
+  if ($install_state['interactive'] && strpos($user_agent, 'simpletest') !== FALSE && !drupal_valid_test_ua()) {
+    header($request->server->get('SERVER_PROTOCOL') . ' 403 Forbidden');
+    exit;
+  }
+  if ($install_state['interactive'] && drupal_valid_test_ua()) {
+    // Set the default timezone. While this doesn't cause any tests to fail, PHP
+    // complains if 'date.timezone' is not set in php.ini. The Australia/Sydney
+    // timezone is chosen so all tests are run using an edge case scenario
+    // (UTC+10  and DST). This choice is made to prevent timezone related
+    // regressions and reduce the fragility of the testing system in general.
+    date_default_timezone_set('Australia/Sydney');
+  }
+
+  $site_path = empty($install_state['site_path']) ? DrupalKernel::findSitePath($request, FALSE) : $install_state['site_path'];
+  Settings::initialize(dirname(dirname(__DIR__)), $site_path, $class_loader);
+
+  // Ensure that procedural dependencies are loaded as early as possible,
+  // since the error/exception handlers depend on them.
+  require_once __DIR__ . '/../modules/system/system.install';
+  require_once __DIR__ . '/common.inc';
+  require_once __DIR__ . '/file.inc';
+  require_once __DIR__ . '/install.inc';
+  require_once __DIR__ . '/schema.inc';
+  require_once __DIR__ . '/database.inc';
+  require_once __DIR__ . '/form.inc';
+  require_once __DIR__ . '/batch.inc';
+
+  // Load module basics (needed for hook invokes).
+  include_once __DIR__ . '/module.inc';
+  require_once __DIR__ . '/entity.inc';
+
+  // Create a minimal mocked container to support calls to t() in the pre-kernel
+  // base system verification code paths below. The strings are not actually
+  // used or output for these calls.
+  // @todo Separate API level checks from UI-facing error messages.
+  $container = new ContainerBuilder();
+  $container->setParameter('language.default_values', Language::$defaultValues);
+  $container
+    ->register('language.default', 'Drupal\Core\Language\LanguageDefault')
+    ->addArgument('%language.default_values%');
+  $container
+    ->register('string_translation', 'Drupal\Core\StringTranslation\TranslationManager')
+    ->addArgument(new Reference('language.default'));
+
+  // Register the stream wrapper manager.
+  $container
+    ->register('stream_wrapper_manager', 'Drupal\Core\StreamWrapper\StreamWrapperManager')
+    ->addMethodCall('setContainer', [new Reference('service_container')]);
+  $container
+    ->register('file_system', 'Drupal\Core\File\FileSystem')
+    ->addArgument(new Reference('stream_wrapper_manager'))
+    ->addArgument(Settings::getInstance())
+    ->addArgument((new LoggerChannelFactory())->get('file'));
+
+  // Register the class loader so contrib and custom database drivers can be
+  // autoloaded.
+  // @see drupal_get_database_types()
+  $container->set('class_loader', $class_loader);
+
+  \Drupal::setContainer($container);
+
+  // Determine whether base system services are ready to operate.
+  try {
+    $sync_directory = Settings::get('config_sync_directory', FALSE);
+    $install_state['config_verified'] = file_exists($sync_directory);
+  }
+  catch (Exception $e) {
+    $install_state['config_verified'] = FALSE;
+  }
+  $install_state['database_verified'] = install_verify_database_settings($site_path);
+  // A valid settings.php has database settings and a hash_salt value. Other
+  // settings like config_directories will be checked by system_requirements().
+  $install_state['settings_verified'] = $install_state['database_verified'] && (bool) Settings::get('hash_salt', FALSE);
+
+  if ($install_state['settings_verified']) {
+    try {
+      $system_schema = system_schema();
+      end($system_schema);
+      $table = key($system_schema);
+      $install_state['base_system_verified'] = Database::getConnection()->schema()->tableExists($table);
+    }
+    catch (DatabaseExceptionWrapper $e) {
+      // The last defined table of the base system_schema() does not exist yet.
+      // $install_state['base_system_verified'] defaults to FALSE, so the code
+      // following below will use the minimal installer service container.
+      // As soon as the base system is verified here, the installer operates in
+      // a full and regular Drupal environment, without any kind of exceptions.
+    }
+  }
+
+  // Replace services with in-memory and null implementations. This kernel is
+  // replaced with a regular one in drupal_install_system().
+  if (!$install_state['base_system_verified']) {
+    $environment = 'install';
+    $GLOBALS['conf']['container_service_providers']['InstallerServiceProvider'] = 'Drupal\Core\Installer\InstallerServiceProvider';
+  }
+  else {
+    $environment = 'prod';
+    $GLOBALS['conf']['container_service_providers']['InstallerServiceProvider'] = 'Drupal\Core\Installer\NormalInstallerServiceProvider';
+  }
+  $GLOBALS['conf']['container_service_providers']['InstallerConfigOverride'] = 'Drupal\Core\Installer\ConfigOverride';
+
+  // Note, InstallerKernel::createFromRequest() is not used because Settings is
+  // already initialized.
+  $kernel = new InstallerKernel($environment, $class_loader, FALSE);
+  $kernel::bootEnvironment();
+  $kernel->setSitePath($site_path);
+  $kernel->boot();
+  $container = $kernel->getContainer();
+  // If Drupal is being installed behind a proxy, configure the request.
+  ReverseProxyMiddleware::setSettingsOnRequest($request, Settings::getInstance());
+
+  // Register the file translation service.
+  if (isset($GLOBALS['config']['locale.settings']['translation']['path'])) {
+    $directory = $GLOBALS['config']['locale.settings']['translation']['path'];
+  }
+  else {
+    $directory = $site_path . '/files/translations';
+  }
+  /** @var \Drupal\Core\File\FileSystemInterface $file_system */
+  $file_system = $container->get('file_system');
+  $container->set('string_translator.file_translation', new FileTranslation($directory, $file_system));
+  $container->get('string_translation')
+    ->addTranslator($container->get('string_translator.file_translation'));
+
+  // Add list of all available profiles to the installation state.
+  $listing = new ExtensionDiscovery($container->get('app.root'));
+  $listing->setProfileDirectories([]);
+  $install_state['profiles'] += $listing->scan('profile');
+
+  // Prime drupal_get_filename()'s static cache.
+  foreach ($install_state['profiles'] as $name => $profile) {
+    drupal_get_filename('profile', $name, $profile->getPathname());
+    // drupal_get_filename() is called both with 'module' and 'profile', see
+    // \Drupal\Core\Config\ConfigInstaller::getProfileStorages for example.
+    drupal_get_filename('module', $name, $profile->getPathname());
+  }
+
+  if ($profile = _install_select_profile($install_state)) {
+    $install_state['parameters']['profile'] = $profile;
+    install_load_profile($install_state);
+    if (isset($install_state['profile_info']['distribution']['install']['theme'])) {
+      $install_state['theme'] = $install_state['profile_info']['distribution']['install']['theme'];
+    }
+  }
+
+  // Before having installed the system module and being able to do a module
+  // rebuild, prime the drupal_get_filename() static cache with the system
+  // module's location.
+  // @todo Remove as part of https://www.drupal.org/node/2186491
+  drupal_get_filename('module', 'system', 'core/modules/system/system.info.yml');
+
+  // Use the language from profile configuration if available.
+  if (!empty($install_state['config_install_path']) && $install_state['config']['system.site']) {
+    $install_state['parameters']['langcode'] = $install_state['config']['system.site']['default_langcode'];
+  }
+  elseif (isset($install_state['profile_info']['distribution']['langcode'])) {
+    // Otherwise, Use the language from the profile configuration, if available,
+    // to override the language previously set in the parameters.
+    $install_state['parameters']['langcode'] = $install_state['profile_info']['distribution']['langcode'];
+  }
+
+  // Set the default language to the selected language, if any.
+  if (isset($install_state['parameters']['langcode'])) {
+    $default_language = new Language(['id' => $install_state['parameters']['langcode']]);
+    $container->get('language.default')->set($default_language);
+    \Drupal::translation()->setDefaultLangcode($install_state['parameters']['langcode']);
+  }
+
+  // Override the module list with a minimal set of modules.
+  $module_handler = \Drupal::moduleHandler();
+  if (!$module_handler->moduleExists('system')) {
+    $module_handler->addModule('system', 'core/modules/system');
+  }
+  if ($profile && !$module_handler->moduleExists($profile)) {
+    $module_handler->addProfile($profile, $install_state['profiles'][$profile]->getPath());
+  }
+
+  // Load all modules and perform request related initialization.
+  $kernel->preHandle($request);
+
+  // Initialize a route on this legacy request similar to
+  // \Drupal\Core\DrupalKernel::prepareLegacyRequest() since normal routing
+  // will not happen.
+  $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('<none>'));
+  $request->attributes->set(RouteObjectInterface::ROUTE_NAME, '<none>');
+
+  // Prepare for themed output. We need to run this at the beginning of the
+  // page request to avoid a different theme accidentally getting set. (We also
+  // need to run it even in the case of command-line installations, to prevent
+  // any code in the installer that happens to initialize the theme system from
+  // accessing the database before it is set up yet.)
+  drupal_maintenance_theme();
+
+  if (!$install_state['database_verified']) {
+    // Do not install over an existing installation. The call to
+    // install_verify_database_ready() will throw an AlreadyInstalledException
+    // if this is the case.
+    install_verify_database_ready();
+  }
+  // Verify the last completed task in the database, if there is one.
+  $task = install_verify_completed_task();
+
+  // Ensure that the active configuration is empty before installation starts.
+  if ($install_state['config_verified'] && empty($task)) {
+    if (count($kernel->getConfigStorage()->listAll())) {
+      $task = NULL;
+      throw new AlreadyInstalledException($container->get('string_translation'));
+    }
+  }
+
+  // Modify the installation state as appropriate.
+  $install_state['completed_task'] = $task;
+}
+
+/**
+ * Runs all tasks for the current installation request.
+ *
+ * In the case of an interactive installation, all tasks will be attempted
+ * until one is reached that has output which needs to be displayed to the
+ * user, or until a page redirect is required. Otherwise, tasks will be
+ * attempted until the installation is finished.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state. This is
+ *   passed along to each task, so it can be modified if necessary.
+ * @param callable $callback
+ *   (optional) A callback to allow command line processes to update a progress
+ *   bar. The callback is passed the $install_state variable.
+ *
+ * @return
+ *   HTML output from the last completed task.
+ */
+function install_run_tasks(&$install_state, callable $callback = NULL) {
+  do {
+    // Obtain a list of tasks to perform. The list of tasks itself can be
+    // dynamic (e.g., some might be defined by the installation profile,
+    // which is not necessarily known until the earlier tasks have run),
+    // so we regenerate the remaining tasks based on the installation state,
+    // each time through the loop.
+    $tasks_to_perform = install_tasks_to_perform($install_state);
+    // Run the first task on the list.
+    reset($tasks_to_perform);
+    $task_name = key($tasks_to_perform);
+    $task = array_shift($tasks_to_perform);
+    $install_state['active_task'] = $task_name;
+    $original_parameters = $install_state['parameters'];
+    $output = install_run_task($task, $install_state);
+    // Ensure the maintenance theme is initialized. If the install task has
+    // rebuilt the container the active theme will not be set. This can occur if
+    // the task has installed a module.
+    drupal_maintenance_theme();
+
+    $install_state['parameters_changed'] = ($install_state['parameters'] != $original_parameters);
+    // Store this task as having been performed during the current request,
+    // and save it to the database as completed, if we need to and if the
+    // database is in a state that allows us to do so. Also mark the
+    // installation as 'done' when we have run out of tasks.
+    if (!$install_state['task_not_complete']) {
+      $install_state['tasks_performed'][] = $task_name;
+      $install_state['installation_finished'] = empty($tasks_to_perform);
+      if ($task['run'] == INSTALL_TASK_RUN_IF_NOT_COMPLETED || $install_state['installation_finished']) {
+        \Drupal::state()->set('install_task', $install_state['installation_finished'] ? 'done' : $task_name);
+      }
+    }
+    if ($callback) {
+      $callback($install_state);
+    }
+    // Stop when there are no tasks left. In the case of an interactive
+    // installation, also stop if we have some output to send to the browser,
+    // the URL parameters have changed, or an end to the page request was
+    // specifically called for.
+    $finished = empty($tasks_to_perform) || ($install_state['interactive'] && (isset($output) || $install_state['parameters_changed'] || $install_state['stop_page_request']));
+  } while (!$finished);
+  return $output;
+}
+
+/**
+ * Runs an individual installation task.
+ *
+ * @param $task
+ *   An array of information about the task to be run as returned by
+ *   hook_install_tasks().
+ * @param $install_state
+ *   An array of information about the current installation state. This is
+ *   passed in by reference so that it can be modified by the task.
+ *
+ * @return
+ *   The output of the task function, if there is any.
+ */
+function install_run_task($task, &$install_state) {
+  $function = $task['function'];
+
+  if ($task['type'] == 'form') {
+    return install_get_form($function, $install_state);
+  }
+  elseif ($task['type'] == 'batch') {
+    // Start a new batch based on the task function, if one is not running
+    // already.
+    $current_batch = \Drupal::state()->get('install_current_batch');
+    if (!$install_state['interactive'] || !$current_batch) {
+      $batches = $function($install_state);
+      if (empty($batches)) {
+        // If the task did some processing and decided no batch was necessary,
+        // there is nothing more to do here.
+        return;
+      }
+      // Create a one item list of batches if only one batch was provided.
+      if (isset($batches['operations'])) {
+        $batches = [$batches];
+      }
+      foreach ($batches as $batch) {
+        batch_set($batch);
+        // For interactive batches, we need to store the fact that this batch
+        // task is currently running. Otherwise, we need to make sure the batch
+        // will complete in one page request.
+        if ($install_state['interactive']) {
+          \Drupal::state()->set('install_current_batch', $function);
+        }
+        else {
+          $batch =& batch_get();
+          $batch['progressive'] = FALSE;
+        }
+      }
+      // Process the batch. For progressive batches, this will redirect.
+      // Otherwise, the batch will complete.
+      // Disable the default script for the URL and clone the object, as
+      // batch_process() will add additional options to the batch URL.
+      $url = Url::fromUri('base:install.php', ['query' => $install_state['parameters'], 'script' => '']);
+      $response = batch_process($url, clone $url);
+      if ($response instanceof Response) {
+        if (\Drupal::request()->hasSession()) {
+          \Drupal::request()->getSession()->save();
+        }
+        // Send the response.
+        $response->send();
+        exit;
+      }
+    }
+    // If we are in the middle of processing this batch, keep sending back
+    // any output from the batch process, until the task is complete.
+    elseif ($current_batch == $function) {
+      $output = _batch_page(\Drupal::request());
+      // Because Batch API now returns a JSON response for intermediary steps,
+      // but the installer doesn't handle Response objects yet, just send the
+      // output here and emulate the old model.
+      // @todo Replace this when we refactor the installer to use a request-
+      //   response workflow.
+      if ($output instanceof Response) {
+        $output->send();
+        $output = NULL;
+      }
+      // The task is complete when we try to access the batch page and receive
+      // FALSE in return, since this means we are at a URL where we are no
+      // longer requesting a batch ID.
+      if ($output === FALSE) {
+        // Return nothing so the next task will run in the same request.
+        \Drupal::state()->delete('install_current_batch');
+        return;
+      }
+      else {
+        // We need to force the page request to end if the task is not
+        // complete, since the batch API sometimes prints JSON output
+        // rather than returning a themed page.
+        $install_state['task_not_complete'] = $install_state['stop_page_request'] = TRUE;
+        return $output;
+      }
+    }
+  }
+
+  else {
+    // For normal tasks, just return the function result, whatever it is.
+    return $function($install_state);
+  }
+}
+
+/**
+ * Returns a list of tasks to perform during the current installation request.
+ *
+ * Note that the list of tasks can change based on the installation state as
+ * the page request evolves (for example, if an installation profile hasn't
+ * been selected yet, we don't yet know which profile tasks need to be run).
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return
+ *   A list of tasks to be performed, with associated metadata.
+ */
+function install_tasks_to_perform($install_state) {
+  // Start with a list of all currently available tasks.
+  $tasks = install_tasks($install_state);
+  foreach ($tasks as $name => $task) {
+    // Remove any tasks that were already performed or that never should run.
+    // Also, if we started this page request with an indication of the last
+    // task that was completed, skip that task and all those that come before
+    // it, unless they are marked as always needing to run.
+    if ($task['run'] == INSTALL_TASK_SKIP || in_array($name, $install_state['tasks_performed']) || (!empty($install_state['completed_task']) && empty($completed_task_found) && $task['run'] != INSTALL_TASK_RUN_IF_REACHED)) {
+      unset($tasks[$name]);
+    }
+    if (!empty($install_state['completed_task']) && $name == $install_state['completed_task']) {
+      $completed_task_found = TRUE;
+    }
+  }
+  return $tasks;
+}
+
+/**
+ * Returns a list of all tasks the installer currently knows about.
+ *
+ * This function will return tasks regardless of whether or not they are
+ * intended to run on the current page request. However, the list can change
+ * based on the installation state (for example, if an installation profile
+ * hasn't been selected yet, we don't yet know which profile tasks will be
+ * available).
+ *
+ * You can override this using hook_install_tasks() or
+ * hook_install_tasks_alter().
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return
+ *   A list of tasks, with associated metadata as returned by
+ *   hook_install_tasks().
+ */
+function install_tasks($install_state) {
+  // Determine whether a translation file must be imported during the
+  // 'install_import_translations' task. Import when a non-English language is
+  // available and selected. Also we will need translations even if the
+  // installer language is English but there are other languages on the system.
+  $locale_module_installed = \Drupal::moduleHandler()->moduleExists('locale');
+  $needs_translations = $locale_module_installed && ((count($install_state['translations']) > 1 && !empty($install_state['parameters']['langcode']) && $install_state['parameters']['langcode'] != 'en') || \Drupal::languageManager()->isMultilingual());
+  // Determine whether a translation file must be downloaded during the
+  // 'install_download_translation' task. Download when a non-English language
+  // is selected, but no translation is yet in the translations directory.
+  $needs_download = isset($install_state['parameters']['langcode']) && !isset($install_state['translations'][$install_state['parameters']['langcode']]) && $install_state['parameters']['langcode'] != 'en';
+
+  // Start with the core installation tasks that run before handing control
+  // to the installation profile.
+  $tasks = [
+    'install_select_language' => [
+      'display_name' => t('Choose language'),
+      'run' => INSTALL_TASK_RUN_IF_REACHED,
+    ],
+    'install_download_translation' => [
+      'run' => $needs_download ? INSTALL_TASK_RUN_IF_REACHED : INSTALL_TASK_SKIP,
+    ],
+    'install_select_profile' => [
+      'display_name' => t('Choose profile'),
+      'display' => empty($install_state['profile_info']['distribution']['name']) && count($install_state['profiles']) != 1,
+      'run' => INSTALL_TASK_RUN_IF_REACHED,
+    ],
+    'install_load_profile' => [
+      'run' => INSTALL_TASK_RUN_IF_REACHED,
+    ],
+    'install_verify_requirements' => [
+      'display_name' => t('Verify requirements'),
+    ],
+    'install_settings_form' => [
+      'display_name' => t('Set up database'),
+      'type' => 'form',
+      // Even though the form only allows the user to enter database settings,
+      // we still need to display it if settings.php is invalid in any way,
+      // since the form submit handler is where settings.php is rewritten.
+      'run' => $install_state['settings_verified'] ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_NOT_COMPLETED,
+      'function' => 'Drupal\Core\Installer\Form\SiteSettingsForm',
+    ],
+    'install_write_profile' => [],
+    'install_verify_database_ready' => [
+      'run' => INSTALL_TASK_RUN_IF_NOT_COMPLETED,
+    ],
+    'install_base_system' => [
+      'run' => $install_state['base_system_verified'] ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_NOT_COMPLETED,
+    ],
+    // All tasks below are executed in a regular, full Drupal environment.
+    'install_bootstrap_full' => [
+      'run' => INSTALL_TASK_RUN_IF_REACHED,
+    ],
+    'install_profile_modules' => [
+      'display_name' => t('Install site'),
+      'type' => 'batch',
+    ],
+    'install_profile_themes' => [],
+    'install_install_profile' => [],
+    'install_import_translations' => [
+      'display_name' => t('Set up translations'),
+      'display' => $needs_translations,
+      'type' => 'batch',
+      'run' => $needs_translations ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
+    ],
+    'install_configure_form' => [
+      'display_name' => t('Configure site'),
+      'type' => 'form',
+      'function' => 'Drupal\Core\Installer\Form\SiteConfigureForm',
+    ],
+  ];
+
+  if (!empty($install_state['config_install_path'])) {
+    // The chosen profile indicates that rather than installing a new site, an
+    // instance of the same site should be installed from the given
+    // configuration.
+    // That means we need to remove the steps installing the extensions and
+    // replace them with a configuration synchronization step.
+    unset($tasks['install_download_translation']);
+    $key = array_search('install_profile_modules', array_keys($tasks), TRUE);
+    unset($tasks['install_profile_modules']);
+    unset($tasks['install_profile_themes']);
+    unset($tasks['install_install_profile']);
+    $config_tasks = [
+      'install_config_import_batch' => [
+        'display_name' => t('Install configuration'),
+        'type' => 'batch',
+      ],
+      'install_config_download_translations' => [],
+      'install_config_revert_install_changes' => [],
+    ];
+    $tasks = array_slice($tasks, 0, $key, TRUE) +
+      $config_tasks +
+      array_slice($tasks, $key, NULL, TRUE);
+  }
+
+  // Now add any tasks defined by the installation profile.
+  if (!empty($install_state['parameters']['profile'])) {
+    // Load the profile install file, because it is not always loaded when
+    // hook_install_tasks() is invoked (e.g. batch processing).
+    $profile = $install_state['parameters']['profile'];
+    $profile_install_file = $install_state['profiles'][$profile]->getPath() . '/' . $profile . '.install';
+    if (file_exists($profile_install_file)) {
+      include_once \Drupal::root() . '/' . $profile_install_file;
+    }
+    $function = $install_state['parameters']['profile'] . '_install_tasks';
+    if (function_exists($function)) {
+      $result = $function($install_state);
+      if (is_array($result)) {
+        $tasks += $result;
+      }
+    }
+  }
+
+  // Finish by adding the remaining core tasks.
+  $tasks += [
+    'install_finish_translations' => [
+      'display_name' => t('Finish translations'),
+      'display' => $needs_translations,
+      'type' => 'batch',
+      'run' => $needs_translations ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
+    ],
+    'install_finished' => [],
+  ];
+
+  // Allow the installation profile to modify the full list of tasks.
+  if (!empty($install_state['parameters']['profile'])) {
+    $profile = $install_state['parameters']['profile'];
+    if ($install_state['profiles'][$profile]->load()) {
+      $function = $install_state['parameters']['profile'] . '_install_tasks_alter';
+      if (function_exists($function)) {
+        $function($tasks, $install_state);
+      }
+    }
+  }
+
+  // Fill in default parameters for each task before returning the list.
+  foreach ($tasks as $task_name => &$task) {
+    $task += [
+      'display_name' => NULL,
+      'display' => !empty($task['display_name']),
+      'type' => 'normal',
+      'run' => INSTALL_TASK_RUN_IF_NOT_COMPLETED,
+      'function' => $task_name,
+    ];
+  }
+  return $tasks;
+}
+
+/**
+ * Returns a list of tasks that should be displayed to the end user.
+ *
+ * The output of this function is a list suitable for sending to
+ * maintenance-task-list.html.twig.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return
+ *   A list of tasks, with keys equal to the machine-readable task name and
+ *   values equal to the name that should be displayed.
+ *
+ * @see maintenance-task-list.html.twig
+ */
+function install_tasks_to_display($install_state) {
+  $displayed_tasks = [];
+  foreach (install_tasks($install_state) as $name => $task) {
+    if ($task['display']) {
+      $displayed_tasks[$name] = $task['display_name'];
+    }
+  }
+  return $displayed_tasks;
+}
+
+/**
+ * Builds and processes a form for the installer environment.
+ *
+ * Ensures that FormBuilder does not redirect after submitting a form, since the
+ * installer uses a custom step/flow logic via install_run_tasks().
+ *
+ * @param string|array $form_id
+ *   The form ID to build and process.
+ * @param array $install_state
+ *   The current state of the installation.
+ *
+ * @return array|null
+ *   A render array containing the form to render, or NULL in case the form was
+ *   successfully submitted.
+ *
+ * @throws \Drupal\Core\Installer\Exception\InstallerException
+ */
+function install_get_form($form_id, array &$install_state) {
+  // Ensure the form will not redirect, since install_run_tasks() uses a custom
+  // redirection logic.
+  $form_state = (new FormState())
+    ->addBuildInfo('args', [&$install_state])
+    ->disableRedirect();
+  $form_builder = \Drupal::formBuilder();
+  if ($install_state['interactive']) {
+    $form = $form_builder->buildForm($form_id, $form_state);
+    // If the form submission was not successful, the form needs to be rendered,
+    // which means the task is not complete yet.
+    if (!$form_state->isExecuted()) {
+      $install_state['task_not_complete'] = TRUE;
+      return $form;
+    }
+  }
+  else {
+    // For non-interactive installs, submit the form programmatically with the
+    // values taken from the installation state.
+    $install_form_id = $form_builder->getFormId($form_id, $form_state);
+    if (!empty($install_state['forms'][$install_form_id])) {
+      $form_state->setValues($install_state['forms'][$install_form_id]);
+    }
+    $form_builder->submitForm($form_id, $form_state);
+
+    // Throw an exception in case of any form validation error.
+    if ($errors = $form_state->getErrors()) {
+      throw new InstallerException(implode("\n", $errors));
+    }
+  }
+}
+
+/**
+ * Returns the URL that should be redirected to during an installation request.
+ *
+ * The output of this function is suitable for sending to install_goto().
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return
+ *   The URL to redirect to.
+ *
+ * @see install_full_redirect_url()
+ */
+function install_redirect_url($install_state) {
+  return 'core/install.php?' . UrlHelper::buildQuery($install_state['parameters']);
+}
+
+/**
+ * Returns the complete URL redirected to during an installation request.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return
+ *   The complete URL to redirect to.
+ *
+ * @see install_redirect_url()
+ */
+function install_full_redirect_url($install_state) {
+  global $base_url;
+  return $base_url . '/' . install_redirect_url($install_state);
+}
+
+/**
+ * Displays themed installer output and ends the page request.
+ *
+ * Installation tasks should use #title to set the desired page
+ * title, but otherwise this function takes care of theming the overall page
+ * output during every step of the installation.
+ *
+ * @param $output
+ *   The content to display on the main part of the page.
+ * @param $install_state
+ *   An array of information about the current installation state.
+ */
+function install_display_output($output, $install_state) {
+  // Ensure the maintenance theme is initialized.
+  // The regular initialization call in install_begin_request() may not be
+  // reached in case of an early installer error.
+  drupal_maintenance_theme();
+
+  // Prevent install.php from being indexed when installed in a sub folder.
+  // robots.txt rules are not read if the site is within domain.com/subfolder
+  // resulting in /subfolder/install.php being found through search engines.
+  // When settings.php is writeable this can be used via an external database
+  // leading a malicious user to gain php access to the server.
+  $noindex_meta_tag = [
+    '#tag' => 'meta',
+    '#attributes' => [
+      'name' => 'robots',
+      'content' => 'noindex, nofollow',
+    ],
+  ];
+  $output['#attached']['html_head'][] = [$noindex_meta_tag, 'install_meta_robots'];
+
+  // Only show the task list if there is an active task; otherwise, the page
+  // request has ended before tasks have even been started, so there is nothing
+  // meaningful to show.
+  $regions = [];
+  if (isset($install_state['active_task'])) {
+    // Let the theming function know when every step of the installation has
+    // been completed.
+    $active_task = $install_state['installation_finished'] ? NULL : $install_state['active_task'];
+    $task_list = [
+      '#theme' => 'maintenance_task_list',
+      '#items' => install_tasks_to_display($install_state),
+      '#active' => $active_task,
+    ];
+    $regions['sidebar_first'] = $task_list;
+  }
+
+  $bare_html_page_renderer = \Drupal::service('bare_html_page_renderer');
+  $response = $bare_html_page_renderer->renderBarePage($output, $output['#title'], 'install_page', $regions);
+  $default_headers = [
+    'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT',
+    'Last-Modified' => gmdate(DATE_RFC1123, REQUEST_TIME),
+    'Cache-Control' => 'no-cache, must-revalidate',
+    'ETag' => '"' . REQUEST_TIME . '"',
+  ];
+  $response->headers->add($default_headers);
+  $response->send();
+  exit;
+}
+
+/**
+ * Verifies the requirements for installing Drupal.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return
+ *   A themed status report, or an exception if there are requirement errors.
+ */
+function install_verify_requirements(&$install_state) {
+  // Check the installation requirements for Drupal and this profile.
+  $requirements = install_check_requirements($install_state);
+
+  // Verify existence of all required modules.
+  $requirements += drupal_verify_profile($install_state);
+
+  return install_display_requirements($install_state, $requirements);
+}
+
+/**
+ * Installation task; install the base functionality Drupal needs to bootstrap.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ */
+function install_base_system(&$install_state) {
+  // Install system.module.
+  drupal_install_system($install_state);
+
+  // Call HtaccessWriter::ensure() to ensure that all of Drupal's standard
+  // directories (e.g., the public files directory and config directory) have
+  // appropriate .htaccess files. These directories will have already been
+  // created by this point in the installer, since Drupal creates them during
+  // the install_verify_requirements() task. Note that we cannot call
+  // file_ensure_access() any earlier than this, since it relies on
+  // system.module in order to work.
+  \Drupal::service('file.htaccess_writer')->ensure();
+
+  // Prime the drupal_get_filename() static cache with the user module's
+  // exact location.
+  // @todo Remove as part of https://www.drupal.org/node/2186491
+  drupal_get_filename('module', 'user', 'core/modules/user/user.info.yml');
+
+  // Enable the user module so that sessions can be recorded during the
+  // upcoming bootstrap step.
+  \Drupal::service('module_installer')->install(['user'], FALSE);
+
+  // Save the list of other modules to install for the upcoming tasks.
+  // State can be set to the database now that system.module is installed.
+  $modules = $install_state['profile_info']['install'];
+
+  \Drupal::state()->set('install_profile_modules', array_diff($modules, ['system']));
+  $install_state['base_system_verified'] = TRUE;
+}
+
+/**
+ * Verifies and returns the last installation task that was completed.
+ *
+ * @return
+ *   The last completed task, if there is one. An exception is thrown if Drupal
+ *   is already installed.
+ */
+function install_verify_completed_task() {
+  try {
+    $task = \Drupal::state()->get('install_task');
+  }
+  // Do not trigger an error if the database query fails, since the database
+  // might not be set up yet.
+  catch (\Exception $e) {
+  }
+  if (isset($task)) {
+    if ($task == 'done') {
+      throw new AlreadyInstalledException(\Drupal::service('string_translation'));
+    }
+    return $task;
+  }
+}
+
+/**
+ * Verifies that settings.php specifies a valid database connection.
+ *
+ * @param string $site_path
+ *   The site path.
+ *
+ * @return bool
+ *   TRUE if there are no database errors.
+ */
+function install_verify_database_settings($site_path) {
+  if ($database = Database::getConnectionInfo()) {
+    $database = $database['default'];
+    $settings_file = './' . $site_path . '/settings.php';
+    $errors = install_database_errors($database, $settings_file);
+    if (empty($errors)) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Verify that the database is ready (no existing Drupal installation).
+ *
+ * @throws \Drupal\Core\Installer\Exception\AlreadyInstalledException
+ *   Thrown when the database already has a table that would be created by
+ *   installing the System module.
+ */
+function install_verify_database_ready() {
+  $system_schema = system_schema();
+  end($system_schema);
+  $table = key($system_schema);
+
+  $existing_install = FALSE;
+  if ($database = Database::getConnectionInfo()) {
+    try {
+      $existing_install = Database::getConnection()->schema()->tableExists($table);
+    }
+    // Do not trigger an error if the database query fails, since the database
+    // might not be set up yet.
+    catch (\Exception $e) {
+    }
+  }
+  if ($existing_install) {
+    throw new AlreadyInstalledException(\Drupal::service('string_translation'));
+  }
+}
+
+/**
+ * Checks a database connection and returns any errors.
+ */
+function install_database_errors($database, $settings_file) {
+  $errors = [];
+
+  // Check database type.
+  $database_types = drupal_get_database_types();
+  $driver = $database['driver'];
+  if (!isset($database_types[$driver])) {
+    $errors['driver'] = t("In your %settings_file file you have configured @drupal to use a %driver server, however your PHP installation currently does not support this database type.", ['%settings_file' => $settings_file, '@drupal' => drupal_install_profile_distribution_name(), '%driver' => $driver]);
+  }
+  else {
+    // Run driver specific validation
+    $errors += $database_types[$driver]->validateDatabaseSettings($database);
+    if (!empty($errors)) {
+      // No point to try further.
+      return $errors;
+    }
+    // Run tasks associated with the database type. Any errors are caught in the
+    // calling function.
+    Database::addConnectionInfo('default', 'default', $database);
+
+    $errors = db_installer_object($driver, $database['namespace'] ?? NULL)->runTasks();
+  }
+  return $errors;
+}
+
+/**
+ * Selects which profile to install.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state. The chosen
+ *   profile will be added here, if it was not already selected previously, as
+ *   will a list of all available profiles.
+ *
+ * @return
+ *   For interactive installations, a form allowing the profile to be selected,
+ *   if the user has a choice that needs to be made. Otherwise, an exception is
+ *   thrown if a profile cannot be chosen automatically.
+ */
+function install_select_profile(&$install_state) {
+  if (empty($install_state['parameters']['profile'])) {
+    // If there are no profiles at all, installation cannot proceed.
+    if (empty($install_state['profiles'])) {
+      throw new NoProfilesException(\Drupal::service('string_translation'));
+    }
+    // Try to automatically select a profile.
+    if ($profile = _install_select_profile($install_state)) {
+      $install_state['parameters']['profile'] = $profile;
+    }
+    else {
+      // The non-interactive installer requires a profile parameter.
+      if (!$install_state['interactive']) {
+        throw new InstallerException('Missing profile parameter.');
+      }
+      // Otherwise, display a form to select a profile.
+      return install_get_form('Drupal\Core\Installer\Form\SelectProfileForm', $install_state);
+    }
+  }
+}
+
+/**
+ * Determines the installation profile to use in the installer.
+ *
+ * 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.
+ * - A specific profile name is requested in installation parameters:
+ *   - For interactive installations via request query parameters.
+ *   - For non-interactive installations via install_drupal() settings.
+ * - One of the available profiles is a distribution. If multiple profiles are
+ *   distributions, then the first discovered profile will be selected.
+ * - Only one visible profile is available.
+ *
+ * @param array $install_state
+ *   The current installer state, containing a 'profiles' key, which is an
+ *   associative array of profiles with the machine-readable names as keys.
+ *
+ * @return string|null
+ *   The machine-readable name of the selected profile or NULL if no profile was
+ *   selected.
+ *
+ *  @see install_select_profile()
+ */
+function _install_select_profile(&$install_state) {
+  // If there is only one profile available it will always be the one selected.
+  if (count($install_state['profiles']) == 1) {
+    return key($install_state['profiles']);
+  }
+  // If a valid profile has already been selected, return the selection.
+  if (!empty($install_state['parameters']['profile'])) {
+    $profile = $install_state['parameters']['profile'];
+    if (isset($install_state['profiles'][$profile])) {
+      return $profile;
+    }
+  }
+  // If any of the profiles are distribution profiles, return the first one.
+  foreach ($install_state['profiles'] as $profile) {
+    $profile_info = install_profile_info($profile->getName());
+    if (!empty($profile_info['distribution'])) {
+      return $profile->getName();
+    }
+  }
+  // Get all visible (not hidden) profiles.
+  $visible_profiles = array_filter($install_state['profiles'], function ($profile) {
+    $profile_info = install_profile_info($profile->getName());
+    return !isset($profile_info['hidden']) || !$profile_info['hidden'];
+  });
+  // If there is only one visible profile, return it.
+  if (count($visible_profiles) == 1) {
+    return (key($visible_profiles));
+  }
+}
+
+/**
+ * Finds all .po files that are useful to the installer.
+ *
+ * @return
+ *   An associative array of file URIs keyed by language code. URIs as
+ *   returned by FileSystemInterface::scanDirectory().
+ *
+ * @see \Drupal\Core\File\FileSystemInterface::scanDirectory()
+ */
+function install_find_translations() {
+  $translations = [];
+  $files = \Drupal::service('string_translator.file_translation')->findTranslationFiles();
+  // English does not need a translation file.
+  array_unshift($files, (object) ['name' => 'en']);
+  foreach ($files as $uri => $file) {
+    // Strip off the file name component before the language code.
+    $langcode = preg_replace('!^(.+\.)?([^\.]+)$!', '\2', $file->name);
+    // Language codes cannot exceed 12 characters to fit into the {language}
+    // table.
+    if (strlen($langcode) <= 12) {
+      $translations[$langcode] = $uri;
+    }
+  }
+  return $translations;
+}
+
+/**
+ * Selects which language to use during installation.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state. The chosen
+ *   langcode will be added here, if it was not already selected previously, as
+ *   will a list of all available languages.
+ *
+ * @return
+ *   For interactive installations, a form or other page output allowing the
+ *   language to be selected or providing information about language selection,
+ *   if a language has not been chosen. Otherwise, an exception is thrown if a
+ *   language cannot be chosen automatically.
+ */
+function install_select_language(&$install_state) {
+  // Find all available translation files.
+  $files = install_find_translations();
+  $install_state['translations'] += $files;
+
+  // If a valid language code is set, continue with the next installation step.
+  // When translations from the localization server are used, any language code
+  // is accepted because the standard language list is kept in sync with the
+  // languages available at http://localize.drupal.org.
+  // When files from the translation directory are used, we only accept
+  // languages for which a file is available.
+  if (!empty($install_state['parameters']['langcode'])) {
+    $standard_languages = LanguageManager::getStandardLanguageList();
+    $langcode = $install_state['parameters']['langcode'];
+    if ($langcode == 'en' || isset($files[$langcode]) || isset($standard_languages[$langcode])) {
+      $install_state['parameters']['langcode'] = $langcode;
+      return;
+    }
+  }
+
+  if (empty($install_state['parameters']['langcode'])) {
+    // If we are performing an interactive installation, we display a form to
+    // select a right language. If no translation files were found in the
+    // translations directory, the form shows a list of standard languages. If
+    // translation files were found the form shows a select list of the
+    // corresponding languages to choose from.
+    if ($install_state['interactive']) {
+      return install_get_form('Drupal\Core\Installer\Form\SelectLanguageForm', $install_state);
+    }
+    // If we are performing a non-interactive installation. If only one language
+    // (English) is available, assume the user is correct. Otherwise throw an
+    // error.
+    else {
+      if (count($files) == 1) {
+        $install_state['parameters']['langcode'] = current(array_keys($files));
+        return;
+      }
+      else {
+        throw new InstallerException('You must select a language to continue the installation.');
+      }
+    }
+  }
+}
+
+/**
+ * Download a translation file for the selected language.
+ *
+ * @param array $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return string
+ *   A themed status report, or an exception if there are requirement errors.
+ *   Upon successful download the page is reloaded and no output is returned.
+ */
+function install_download_translation(&$install_state) {
+  // Check whether all conditions are met to download. Download the translation
+  // if possible.
+  $requirements = install_check_translations($install_state['parameters']['langcode'], $install_state['server_pattern']);
+  if ($output = install_display_requirements($install_state, $requirements)) {
+    return $output;
+  }
+
+  // The download was successful, reload the page in the new language.
+  $install_state['translations'][$install_state['parameters']['langcode']] = TRUE;
+  if ($install_state['interactive']) {
+    install_goto(install_redirect_url($install_state));
+  }
+}
+
+/**
+ * Attempts to get a file using a HTTP request and to store it locally.
+ *
+ * @param string $uri
+ *   The URI of the file to grab.
+ * @param string $destination
+ *   Stream wrapper URI specifying where the file should be placed. If a
+ *   directory path is provided, the file is saved into that directory under its
+ *   original name. If the path contains a filename as well, that one will be
+ *   used instead.
+ *
+ * @return bool
+ *   TRUE on success, FALSE on failure.
+ */
+function install_retrieve_file($uri, $destination) {
+  $parsed_url = parse_url($uri);
+  /** @var \Drupal\Core\File\FileSystemInterface $file_system */
+  $file_system = \Drupal::service('file_system');
+  if (is_dir($file_system->realpath($destination))) {
+    // Prevent URIs with triple slashes when gluing parts together.
+    $path = str_replace('///', '//', "$destination/") . $file_system->basename($parsed_url['path']);
+  }
+  else {
+    $path = $destination;
+  }
+
+  try {
+    $response = \Drupal::httpClient()->get($uri, ['headers' => ['Accept' => 'text/plain']]);
+    $data = (string) $response->getBody();
+    if (empty($data)) {
+      return FALSE;
+    }
+  }
+  catch (RequestException $e) {
+    return FALSE;
+  }
+  return file_put_contents($path, $data) !== FALSE;
+}
+
+/**
+ * Checks if the localization server can be contacted.
+ *
+ * @param string $uri
+ *   The URI to contact.
+ *
+ * @return string
+ *   TRUE if the URI was contacted successfully, FALSE if not.
+ */
+function install_check_localization_server($uri) {
+  try {
+    \Drupal::httpClient()->head($uri);
+    return TRUE;
+  }
+  catch (RequestException $e) {
+    return FALSE;
+  }
+}
+
+/**
+ * Extracts version information from a drupal core version string.
+ *
+ * @param string $version
+ *   Version info string (e.g., 8.0.0, 8.1.0, 8.0.0-dev, 8.0.0-unstable1,
+ *   8.0.0-alpha2, 8.0.0-beta3, 8.6.x, and 8.0.0-rc4).
+ *
+ * @return array
+ *   Associative array of version info:
+ *   - major: Major version (e.g., "8").
+ *   - minor: Minor version (e.g., "0").
+ *   - patch: Patch version (e.g., "0").
+ *   - extra: Extra version info (e.g., "alpha2").
+ *   - extra_text: The text part of "extra" (e.g., "alpha").
+ *   - extra_number: The number part of "extra" (e.g., "2").
+ */
+function _install_get_version_info($version) {
+  preg_match('/
+    (
+      (?P<major>[0-9]+)    # Major release number.
+      \.          # .
+      (?P<minor>[0-9]+)    # Minor release number.
+      \.          # .
+      (?P<patch>[0-9]+|x)  # Patch release number.
+    )             #
+    (             #
+      -           # - separator for "extra" version information.
+      (?P<extra>   #
+        (?P<extra_text>[a-z]+)  # Release extra text (e.g., "alpha").
+        (?P<extra_number>[0-9]*)  # Release extra number (no separator between text and number).
+      )           #
+      |           # OR no "extra" information.
+    )
+    /sx', $version, $matches);
+
+  return $matches;
+}
+
+/**
+ * Loads information about the chosen profile during installation.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state. The loaded
+ *   profile information will be added here.
+ */
+function install_load_profile(&$install_state) {
+  $profile = $install_state['parameters']['profile'];
+  $install_state['profiles'][$profile]->load();
+  $install_state['profile_info'] = install_profile_info($profile, isset($install_state['parameters']['langcode']) ? $install_state['parameters']['langcode'] : 'en');
+
+  $sync_directory = Settings::get('config_sync_directory');
+  if (!empty($install_state['parameters']['existing_config']) && !empty($sync_directory)) {
+    $install_state['config_install_path'] = $sync_directory;
+  }
+  // If the profile has a config/sync directory copy the information to the
+  // install_state global.
+  elseif (!empty($install_state['profile_info']['config_install_path'])) {
+    $install_state['config_install_path'] = $install_state['profile_info']['config_install_path'];
+  }
+
+  if (!empty($install_state['config_install_path'])) {
+    $sync = new FileStorage($install_state['config_install_path']);
+    $install_state['config']['system.site'] = $sync->read('system.site');
+  }
+}
+
+/**
+ * Performs a full bootstrap of Drupal during installation.
+ */
+function install_bootstrap_full() {
+  // Store the session on the request object and start it.
+  /** @var \Symfony\Component\HttpFoundation\Session\SessionInterface $session */
+  $session = \Drupal::service('session');
+  \Drupal::request()->setSession($session);
+  $session->start();
+}
+
+/**
+ * Installs required modules via a batch process.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return
+ *   The batch definition.
+ */
+function install_profile_modules(&$install_state) {
+  // We need to manually trigger the installation of core-provided entity types,
+  // as those will not be handled by the module installer.
+  install_core_entity_type_definitions();
+
+  $modules = \Drupal::state()->get('install_profile_modules') ?: [];
+  $files = \Drupal::service('extension.list.module')->getList();
+  \Drupal::state()->delete('install_profile_modules');
+
+  // Always install required modules first. Respect the dependencies between
+  // the modules.
+  $required = [];
+  $non_required = [];
+
+  // Add modules that other modules depend on.
+  foreach ($modules as $module) {
+    if ($files[$module]->requires) {
+      $modules = array_merge($modules, array_keys($files[$module]->requires));
+    }
+  }
+  $modules = array_unique($modules);
+  foreach ($modules as $module) {
+    if (!empty($files[$module]->info['required'])) {
+      $required[$module] = $files[$module]->sort;
+    }
+    else {
+      $non_required[$module] = $files[$module]->sort;
+    }
+  }
+  arsort($required);
+  arsort($non_required);
+
+  $operations = [];
+  foreach ($required + $non_required as $module => $weight) {
+    $operations[] = ['_install_module_batch', [$module, $files[$module]->info['name']]];
+  }
+  $batch = [
+    'operations' => $operations,
+    'title' => t('Installing @drupal', ['@drupal' => drupal_install_profile_distribution_name()]),
+    'error_message' => t('The installation has encountered an error.'),
+  ];
+  return $batch;
+}
+
+/**
+ * Installs entity type definitions provided by core.
+ */
+function install_core_entity_type_definitions() {
+  $update_manager = \Drupal::entityDefinitionUpdateManager();
+  foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type) {
+    if ($entity_type->getProvider() == 'core') {
+      $update_manager->installEntityType($entity_type);
+    }
+  }
+}
+
+/**
+ * Installs themes.
+ *
+ * This does not use a batch, since installing themes is faster than modules and
+ * because an installation profile typically installs 1-3 themes only (default
+ * theme, base theme, admin theme).
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ */
+function install_profile_themes(&$install_state) {
+  // Install the themes specified by the installation profile.
+  $themes = $install_state['profile_info']['themes'];
+  \Drupal::service('theme_installer')->install($themes);
+
+  // Ensure that the install profile's theme is used.
+  // @see _drupal_maintenance_theme()
+  \Drupal::theme()->resetActiveTheme();
+}
+
+/**
+ * Installs the install profile.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ */
+function install_install_profile(&$install_state) {
+  \Drupal::service('module_installer')->install([$install_state['parameters']['profile']], FALSE);
+  // Install all available optional config. During installation the module order
+  // is determined by dependencies. If there are no dependencies between modules
+  // then the order in which they are installed is dependent on random factors
+  // like PHP version. Optional configuration therefore might or might not be
+  // created depending on this order. Ensuring that we have installed all of the
+  // optional configuration whose dependencies can be met at this point removes
+  // any disparities that this creates.
+  \Drupal::service('config.installer')->installOptionalConfig();
+
+  // Ensure that the install profile's theme is used.
+  // @see _drupal_maintenance_theme()
+  \Drupal::theme()->resetActiveTheme();
+}
+
+/**
+ * Prepares the system for import and downloads additional translations.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return
+ *   The batch definition, if there are language files to download.
+ */
+function install_download_additional_translations_operations(&$install_state) {
+  \Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
+
+  $langcode = $install_state['parameters']['langcode'];
+  if (!($language = ConfigurableLanguage::load($langcode))) {
+    // Create the language if not already shipped with a profile.
+    $language = ConfigurableLanguage::createFromLangcode($langcode);
+  }
+  $language->save();
+
+  // If a non-English language was selected, change the default language and
+  // remove English.
+  if ($langcode != 'en') {
+    \Drupal::configFactory()->getEditable('system.site')
+      ->set('langcode', $langcode)
+      ->set('default_langcode', $langcode)
+      ->save();
+    \Drupal::service('language.default')->set($language);
+    if (empty($install_state['profile_info']['keep_english'])) {
+      if ($lang = ConfigurableLanguage::load('en')) {
+        $lang->delete();
+      }
+    }
+  }
+
+  // If there is more than one language or the single one is not English, we
+  // should download/import translations.
+  $languages = \Drupal::languageManager()->getLanguages();
+  $operations = [];
+  foreach ($languages as $langcode => $language) {
+    // The installer language was already downloaded. Check downloads for the
+    // other languages if any. Ignore any download errors here, since we
+    // are in the middle of an install process and there is no way back. We
+    // will not import what we cannot download.
+    if ($langcode != 'en' && $langcode != $install_state['parameters']['langcode']) {
+      $operations[] = ['install_check_translations', [$langcode, $install_state['server_pattern']]];
+    }
+  }
+  return $operations;
+}
+
+/**
+ * Imports languages via a batch process during installation.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return
+ *   The batch definition, if there are language files to import.
+ */
+function install_import_translations(&$install_state) {
+  \Drupal::moduleHandler()->loadInclude('locale', 'translation.inc');
+
+  // If there is more than one language or the single one is not English, we
+  // should import translations.
+  $operations = install_download_additional_translations_operations($install_state);
+  $languages = \Drupal::languageManager()->getLanguages();
+  if (count($languages) > 1 || !isset($languages['en'])) {
+    $operations[] = ['_install_prepare_import', [array_keys($languages), $install_state['server_pattern']]];
+
+    // Set up a batch to import translations for drupal core. Translation import
+    // for contrib modules happens in install_import_translations_remaining.
+    foreach ($languages as $language) {
+      if (locale_translation_use_remote_source()) {
+        $operations[] = ['locale_translation_batch_fetch_download', ['drupal', $language->getId()]];
+      }
+      $operations[] = ['locale_translation_batch_fetch_import', ['drupal', $language->getId(), []]];
+    }
+
+    module_load_include('fetch.inc', 'locale');
+    $batch = [
+      'operations' => $operations,
+      'title' => t('Updating translations.'),
+      'progress_message' => '',
+      'error_message' => t('Error importing translation files'),
+      'finished' => 'locale_translation_batch_fetch_finished',
+      'file' => drupal_get_path('module', 'locale') . '/locale.batch.inc',
+    ];
+    return $batch;
+  }
+}
+
+/**
+ * Tells the translation import process that Drupal core is installed.
+ *
+ * @param array $langcodes
+ *   Language codes used for the translations.
+ * @param string $server_pattern
+ *   Server access pattern (to replace language code, version number, etc. in).
+ */
+function _install_prepare_import($langcodes, $server_pattern) {
+  \Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
+  $matches = [];
+
+  foreach ($langcodes as $langcode) {
+    // Get the translation files located in the translations directory.
+    $files = locale_translate_get_interface_translation_files(['drupal'], [$langcode]);
+    // Pick the first file which matches the language, if any.
+    $file = reset($files);
+    if (is_object($file)) {
+      $filename = $file->filename;
+      preg_match('/drupal-([0-9a-z\.-]+)\.' . $langcode . '\.po/', $filename, $matches);
+      // Get the version information.
+      if ($version = $matches[1]) {
+        $info = _install_get_version_info($version);
+        // Picking the first file does not necessarily result in the right file. So
+        // we check if at least the major version number is available.
+        if ($info['major']) {
+          $core = $info['major'] . '.x';
+          $data = [
+            'name' => 'drupal',
+            'project_type' => 'module',
+            'core' => $core,
+            'version' => $version,
+            'server_pattern' => $server_pattern,
+            'status' => 1,
+          ];
+          \Drupal::service('locale.project')->set($data['name'], $data);
+          module_load_include('compare.inc', 'locale');
+          // Reset project information static cache so that it uses the data
+          // set above.
+          locale_translation_clear_cache_projects();
+          locale_translation_check_projects_local(['drupal'], [$langcode]);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Finishes importing files at end of installation.
+ *
+ * If other projects besides Drupal core have been installed, their translation
+ * will be imported here.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return array
+ *   An array of batch definitions.
+ */
+function install_finish_translations(&$install_state) {
+  \Drupal::moduleHandler()->loadInclude('locale', 'fetch.inc');
+  \Drupal::moduleHandler()->loadInclude('locale', 'compare.inc');
+  \Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
+
+  // Build a fresh list of installed projects. When more projects than core are
+  // installed, their translations will be downloaded (if required) and imported
+  // using a batch.
+  $projects = locale_translation_build_projects();
+  $languages = \Drupal::languageManager()->getLanguages();
+  $batches = [];
+  if (count($projects) > 1) {
+    $options = _locale_translation_default_update_options();
+    if ($batch = locale_translation_batch_update_build([], array_keys($languages), $options)) {
+      $batches[] = $batch;
+    }
+  }
+
+  // Creates configuration translations.
+  $batches[] = locale_config_batch_update_components([], array_keys($languages));
+  return $batches;
+}
+
+/**
+ * Performs final installation steps and displays a 'finished' page.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return
+ *   A message informing the user that the installation is complete.
+ */
+function install_finished(&$install_state) {
+  $profile = $install_state['parameters']['profile'];
+
+  // Installation profiles are always loaded last.
+  module_set_weight($profile, 1000);
+
+  // Build the router once after installing all modules.
+  // This would normally happen upon KernelEvents::TERMINATE, but since the
+  // installer does not use an HttpKernel, that event is never triggered.
+  \Drupal::service('router.builder')->rebuild();
+
+  // Run cron to populate update status tables (if available) so that users
+  // will be warned if they've installed an out of date Drupal version.
+  // Will also trigger indexing of profile-supplied content or feeds.
+  \Drupal::service('cron')->run();
+
+  if ($install_state['interactive']) {
+    // Load current user and perform final login tasks.
+    // This has to be done after drupal_flush_all_caches()
+    // to avoid session regeneration.
+    $account = User::load(1);
+    user_login_finalize($account);
+  }
+
+  $success_message = t('Congratulations, you installed @drupal!', [
+    '@drupal' => drupal_install_profile_distribution_name(),
+  ]);
+  \Drupal::messenger()->addStatus($success_message);
+}
+
+/**
+ * Implements callback_batch_operation().
+ *
+ * Performs batch installation of modules.
+ */
+function _install_module_batch($module, $module_name, &$context) {
+  \Drupal::service('module_installer')->install([$module], FALSE);
+  $context['results'][] = $module;
+  $context['message'] = t('Installed %module module.', ['%module' => $module_name]);
+}
+
+/**
+ * Checks installation requirements and reports any errors.
+ *
+ * @param string $langcode
+ *   Language code to check for download.
+ * @param string $server_pattern
+ *   Server access pattern (to replace language code, version number, etc. in).
+ *
+ * @return array|null
+ *   Requirements compliance array. If the translation was downloaded
+ *   successfully then an empty array is returned. Otherwise the requirements
+ *   error with detailed information. NULL if the file already exists for this
+ *   language code.
+ */
+function install_check_translations($langcode, $server_pattern) {
+  $requirements = [];
+
+  $readable = FALSE;
+  $writable = FALSE;
+  // @todo: Make this configurable.
+  $site_path = \Drupal::service('site.path');
+  $files_directory = $site_path . '/files';
+  $translations_directory = $site_path . '/files/translations';
+  $translations_directory_exists = FALSE;
+  $online = FALSE;
+
+  // First attempt to create or make writable the files directory.
+  /** @var \Drupal\Core\File\FileSystemInterface $file_system */
+  $file_system = \Drupal::service('file_system');
+  $file_system->prepareDirectory($files_directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
+
+  // Then, attempt to create or make writable the translations directory.
+  $file_system->prepareDirectory($translations_directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
+
+  // Get values so the requirements errors can be specific.
+  if (drupal_verify_install_file($translations_directory, FILE_EXIST, 'dir')) {
+    $readable = is_readable($translations_directory);
+    $writable = is_writable($translations_directory);
+    $translations_directory_exists = TRUE;
+  }
+
+  // The file already exists, no need to attempt to download.
+  if ($existing_file = glob($translations_directory . '/drupal-*.' . $langcode . '.po')) {
+    return;
+  }
+
+  $version = \Drupal::VERSION;
+  // For dev releases, remove the '-dev' part and trust the translation server
+  // to fall back to the latest stable release for that branch.
+  // @see locale_translation_build_projects()
+  if (preg_match("/^(\d+\.\d+\.).*-dev$/", $version, $matches)) {
+    // Example match: 8.0.0-dev => 8.0.x (Drupal core)
+    $version = $matches[1] . 'x';
+  }
+
+  // Build URL for the translation file and the translation server.
+  $variables = [
+    '%project' => 'drupal',
+    '%version' => $version,
+    '%core' => 'all',
+    '%language' => $langcode,
+  ];
+  $translation_url = strtr($server_pattern, $variables);
+
+  $elements = parse_url($translation_url);
+  $server_url = $elements['scheme'] . '://' . $elements['host'];
+
+  // Build the language name for display.
+  $languages = LanguageManager::getStandardLanguageList();
+  $language = isset($languages[$langcode]) ? $languages[$langcode][0] : $langcode;
+
+  // Check if any of the desired translation files are available or if the
+  // translation server can be reached. In other words, check if we are online
+  // and have an internet connection.
+  if ($translation_available = install_check_localization_server($translation_url)) {
+    $online = TRUE;
+  }
+  if (!$translation_available) {
+    if (install_check_localization_server($server_url)) {
+      $online = TRUE;
+    }
+  }
+
+  // If the translations directory does not exist, throw an error.
+  if (!$translations_directory_exists) {
+    $requirements['translations directory exists'] = [
+      'title'       => t('Translations directory'),
+      'value'       => t('The translations directory does not exist.'),
+      'severity'    => REQUIREMENT_ERROR,
+      'description' => t('The installer requires that you create a translations directory as part of the installation process. Create the directory %translations_directory . More details about installing Drupal are available in <a href=":install_txt">INSTALL.txt</a>.', ['%translations_directory' => $translations_directory, ':install_txt' => base_path() . 'core/INSTALL.txt']),
+    ];
+  }
+  else {
+    $requirements['translations directory exists'] = [
+      'title'       => t('Translations directory'),
+      'value'       => t('The directory %translations_directory exists.', ['%translations_directory' => $translations_directory]),
+    ];
+    // If the translations directory is not readable, throw an error.
+    if (!$readable) {
+      $requirements['translations directory readable'] = [
+        'title'       => t('Translations directory'),
+        'value'       => t('The translations directory is not readable.'),
+        'severity'    => REQUIREMENT_ERROR,
+        'description' => t('The installer requires read permissions to %translations_directory at all times. The <a href=":handbook_url">webhosting issues</a> documentation section offers help on this and other topics.', ['%translations_directory' => $translations_directory, ':handbook_url' => 'https://www.drupal.org/server-permissions']),
+      ];
+    }
+    // If translations directory is not writable, throw an error.
+    if (!$writable) {
+      $requirements['translations directory writable'] = [
+        'title'       => t('Translations directory'),
+        'value'       => t('The translations directory is not writable.'),
+        'severity'    => REQUIREMENT_ERROR,
+        'description' => t('The installer requires write permissions to %translations_directory during the installation process. The <a href=":handbook_url">webhosting issues</a> documentation section offers help on this and other topics.', ['%translations_directory' => $translations_directory, ':handbook_url' => 'https://www.drupal.org/server-permissions']),
+      ];
+    }
+    else {
+      $requirements['translations directory writable'] = [
+        'title'       => t('Translations directory'),
+        'value'       => t('The translations directory is writable.'),
+      ];
+    }
+  }
+
+  // If the translations server can not be contacted, throw an error.
+  if (!$online) {
+    $requirements['online'] = [
+      'title'       => t('Internet'),
+      'value'       => t('The translation server is offline.'),
+      'severity'    => REQUIREMENT_ERROR,
+      'description' => t('The installer requires to contact the translation server to download a translation file. Check your internet connection and verify that your website can reach the translation server at <a href=":server_url">@server_url</a>.', [':server_url' => $server_url, '@server_url' => $server_url]),
+    ];
+  }
+  else {
+    $requirements['online'] = [
+      'title'       => t('Internet'),
+      'value'       => t('The translation server is online.'),
+    ];
+    // If translation file is not found at the translation server, throw an
+    // error.
+    if (!$translation_available) {
+      $requirements['translation available'] = [
+        'title'       => t('Translation'),
+        'value'       => t('The %language translation is not available.', ['%language' => $language]),
+        'severity'    => REQUIREMENT_ERROR,
+        'description' => t('The %language translation file is not available at the translation server. <a href=":url">Choose a different language</a> or select English and translate your website later.', ['%language' => $language, ':url' => $_SERVER['SCRIPT_NAME']]),
+      ];
+    }
+    else {
+      $requirements['translation available'] = [
+        'title'       => t('Translation'),
+        'value'       => t('The %language translation is available.', ['%language' => $language]),
+      ];
+    }
+  }
+
+  if ($translations_directory_exists && $readable && $writable && $translation_available) {
+    $translation_downloaded = install_retrieve_file($translation_url, $translations_directory);
+
+    if (!$translation_downloaded) {
+      $requirements['translation downloaded'] = [
+        'title'       => t('Translation'),
+        'value'       => t('The %language translation could not be downloaded.', ['%language' => $language]),
+        'severity'    => REQUIREMENT_ERROR,
+        'description' => t('The %language translation file could not be downloaded. <a href=":url">Choose a different language</a> or select English and translate your website later.', ['%language' => $language, ':url' => $_SERVER['SCRIPT_NAME']]),
+      ];
+    }
+  }
+
+  return $requirements;
+}
+
+/**
+ * Checks installation requirements and reports any errors.
+ */
+function install_check_requirements($install_state) {
+  $profile = $install_state['parameters']['profile'];
+
+  // Check the profile requirements.
+  $requirements = drupal_check_profile($profile);
+
+  if ($install_state['settings_verified']) {
+    return $requirements;
+  }
+
+  // If Drupal is not set up already, we need to try to create the default
+  // settings and services files.
+  $default_files = [];
+  $default_files['settings.php'] = [
+    'file' => 'settings.php',
+    'file_default' => 'default.settings.php',
+    'title_default' => t('Default settings file'),
+    'description_default' => t('The default settings file does not exist.'),
+    'title' => t('Settings file'),
+  ];
+  $file_system = \Drupal::service('file_system');
+
+  foreach ($default_files as $default_file_info) {
+    $readable = FALSE;
+    $writable = FALSE;
+    $site_path = './' . \Drupal::service('site.path');
+    $file = $site_path . "/{$default_file_info['file']}";
+    $default_file = "./sites/default/{$default_file_info['file_default']}";
+    $exists = FALSE;
+    // Verify that the directory exists.
+    if (drupal_verify_install_file($site_path, FILE_EXIST, 'dir')) {
+      if (drupal_verify_install_file($file, FILE_EXIST)) {
+        // If it does, make sure it is writable.
+        $readable = drupal_verify_install_file($file, FILE_READABLE);
+        $writable = drupal_verify_install_file($file, FILE_WRITABLE);
+        $exists = TRUE;
+      }
+    }
+
+    // If the default $default_file does not exist, or is not readable,
+    // report an error.
+    if (!drupal_verify_install_file($default_file, FILE_EXIST | FILE_READABLE)) {
+      $requirements["default $file file exists"] = [
+        'title' => $default_file_info['title_default'],
+        'value' => $default_file_info['description_default'],
+        'severity' => REQUIREMENT_ERROR,
+        'description' => t('The @drupal installer requires that the %default-file file must not be deleted or modified from the original download.', [
+            '@drupal' => drupal_install_profile_distribution_name(),
+            '%default-file' => $default_file,
+          ]),
+      ];
+    }
+    // Otherwise, if $file does not exist yet, we can try to copy
+    // $default_file to create it.
+    elseif (!$exists) {
+      $copied = drupal_verify_install_file($site_path, FILE_EXIST | FILE_WRITABLE, 'dir') && @copy($default_file, $file);
+      if ($copied) {
+        // If the new $file file has the same owner as $default_file this means
+        // $default_file is owned by the webserver user. This is an inherent
+        // security weakness because it allows a malicious webserver process to
+        // append arbitrary PHP code and then execute it. However, it is also a
+        // common configuration on shared hosting, and there is nothing Drupal
+        // can do to prevent it. In this situation, having $file also owned by
+        // the webserver does not introduce any additional security risk, so we
+        // keep the file in place. Additionally, this situation also occurs when
+        // the test runner is being run be different user than the webserver.
+        if (fileowner($default_file) === fileowner($file) || DRUPAL_TEST_IN_CHILD_SITE) {
+          $readable = drupal_verify_install_file($file, FILE_READABLE);
+          $writable = drupal_verify_install_file($file, FILE_WRITABLE);
+          $exists = TRUE;
+        }
+        // If $file and $default_file have different owners, this probably means
+        // the server is set up "securely" (with the webserver running as its
+        // own user, distinct from the user who owns all the Drupal PHP files),
+        // although with either a group or world writable sites directory.
+        // Keeping $file owned by the webserver would therefore introduce a
+        // security risk. It would also cause a usability problem, since site
+        // owners who do not have root access to the file system would be unable
+        // to edit their settings file later on. We therefore must delete the
+        // file we just created and force the administrator to log on to the
+        // server and create it manually.
+        else {
+          $deleted = @$file_system->unlink($file);
+          // We expect deleting the file to be successful (since we just
+          // created it ourselves above), but if it fails somehow, we set a
+          // variable so we can display a one-time error message to the
+          // administrator at the bottom of the requirements list. We also try
+          // to make the file writable, to eliminate any conflicting error
+          // messages in the requirements list.
+          $exists = !$deleted;
+          if ($exists) {
+            $settings_file_ownership_error = TRUE;
+            $readable = drupal_verify_install_file($file, FILE_READABLE);
+            $writable = drupal_verify_install_file($file, FILE_WRITABLE);
+          }
+        }
+      }
+    }
+
+    // If the $file does not exist, throw an error.
+    if (!$exists) {
+      $requirements["$file file exists"] = [
+        'title' => $default_file_info['title'],
+        'value' => t('The %file does not exist.', ['%file' => $default_file_info['title']]),
+        'severity' => REQUIREMENT_ERROR,
+        'description' => t('The @drupal installer requires that you create a %file as part of the installation process. Copy the %default_file file to %file. More details about installing Drupal are available in <a href=":install_txt">INSTALL.txt</a>.', [
+            '@drupal' => drupal_install_profile_distribution_name(),
+            '%file' => $file,
+            '%default_file' => $default_file,
+            ':install_txt' => base_path() . 'core/INSTALL.txt',
+          ]),
+      ];
+    }
+    else {
+      $requirements["$file file exists"] = [
+        'title' => $default_file_info['title'],
+        'value' => t('The %file exists.', ['%file' => $file]),
+      ];
+      // If the $file is not readable, throw an error.
+      if (!$readable) {
+        $requirements["$file file readable"] = [
+          'title' => $default_file_info['title'],
+          'value' => t('The %file is not readable.', ['%file' => $default_file_info['title']]),
+          'severity' => REQUIREMENT_ERROR,
+          'description' => t('@drupal requires read permissions to %file at all times. The <a href=":handbook_url">webhosting issues</a> documentation section offers help on this and other topics.', [
+              '@drupal' => drupal_install_profile_distribution_name(),
+              '%file' => $file,
+              ':handbook_url' => 'https://www.drupal.org/server-permissions',
+            ]),
+        ];
+      }
+      // If the $file is not writable, throw an error.
+      if (!$writable) {
+        $requirements["$file file writeable"] = [
+          'title' => $default_file_info['title'],
+          'value' => t('The %file is not writable.', ['%file' => $default_file_info['title']]),
+          'severity' => REQUIREMENT_ERROR,
+          'description' => t('The @drupal installer requires write permissions to %file during the installation process. The <a href=":handbook_url">webhosting issues</a> documentation section offers help on this and other topics.', [
+              '@drupal' => drupal_install_profile_distribution_name(),
+              '%file' => $file,
+              ':handbook_url' => 'https://www.drupal.org/server-permissions',
+            ]),
+        ];
+      }
+      else {
+        $requirements["$file file"] = [
+          'title' => $default_file_info['title'],
+          'value' => t('The @file is writable.', ['@file' => $default_file_info['title']]),
+        ];
+      }
+      if (!empty($settings_file_ownership_error)) {
+        $requirements["$file file ownership"] = [
+          'title' => $default_file_info['title'],
+          'value' => t('The @file is owned by the web server.', ['@file' => $default_file_info['title']]),
+          'severity' => REQUIREMENT_ERROR,
+          'description' => t('The @drupal installer failed to create a %file file with proper file ownership. Log on to your web server, remove the existing %file file, and create a new one by copying the %default_file file to %file. More details about installing Drupal are available in <a href=":install_txt">INSTALL.txt</a>. The <a href=":handbook_url">webhosting issues</a> documentation section offers help on this and other topics.', [
+              '@drupal' => drupal_install_profile_distribution_name(),
+              '%file' => $file,
+              '%default_file' => $default_file,
+              ':install_txt' => base_path() . 'core/INSTALL.txt',
+              ':handbook_url' => 'https://www.drupal.org/server-permissions',
+            ]),
+        ];
+      }
+    }
+
+    // Check the database settings if they have been configured in settings.php
+    // before running the Drupal installer.
+    if ($database = Database::getConnectionInfo()) {
+      $request = Request::createFromGlobals();
+      $site_path = empty($install_state['site_path']) ? DrupalKernel::findSitePath($request, FALSE) : $install_state['site_path'];
+      $database = $database['default'];
+      $settings_file = './' . $site_path . '/settings.php';
+
+      $errors = install_database_errors($database, $settings_file);
+      if (count($errors)) {
+        $error_message = SiteSettingsForm::getDatabaseErrorsTemplate($errors);
+        $requirements['database_install_errors'] = [
+          'title' => t('Database settings'),
+          'description' => $error_message,
+          'severity' => REQUIREMENT_ERROR,
+        ];
+      }
+    }
+  }
+  return $requirements;
+}
+
+/**
+ * Displays installation requirements.
+ *
+ * @param array $install_state
+ *   An array of information about the current installation state.
+ * @param array $requirements
+ *   An array of requirements, in the same format as is returned by
+ *   hook_requirements().
+ *
+ * @return
+ *   A themed status report, or an exception if there are requirement errors.
+ *   If there are only requirement warnings, a themed status report is shown
+ *   initially, but the user is allowed to bypass it by providing 'continue=1'
+ *   in the URL. Otherwise, no output is returned, so that the next task can be
+ *   run in the same page request.
+ *
+ * @throws \Drupal\Core\Installer\Exception\InstallerException
+ */
+function install_display_requirements($install_state, $requirements) {
+  // Check the severity of the requirements reported.
+  $severity = drupal_requirements_severity($requirements);
+
+  // If there are errors, always display them. If there are only warnings, skip
+  // them if the user has provided a URL parameter acknowledging the warnings
+  // and indicating a desire to continue anyway. See drupal_requirements_url().
+  if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && empty($install_state['parameters']['continue']))) {
+    if ($install_state['interactive']) {
+      $build['report']['#type'] = 'status_report';
+      $build['report']['#requirements'] = $requirements;
+      if ($severity == REQUIREMENT_WARNING) {
+        $build['#title'] = t('Requirements review');
+        $build['#suffix'] = t('Check the messages and <a href=":retry">retry</a>, or you may choose to <a href=":cont">continue anyway</a>.', [':retry' => drupal_requirements_url(REQUIREMENT_ERROR), ':cont' => drupal_requirements_url($severity)]);
+      }
+      else {
+        $build['#title'] = t('Requirements problem');
+        $build['#suffix'] = t('Check the messages and <a href=":url">try again</a>.', [':url' => drupal_requirements_url($severity)]);
+      }
+      return $build;
+    }
+    else {
+      // Throw an exception showing any unmet requirements.
+      $failures = [];
+      foreach ($requirements as $requirement) {
+        // Skip warnings altogether for non-interactive installations; these
+        // proceed in a single request so there is no good opportunity (and no
+        // good method) to warn the user anyway.
+        if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
+          $failures[] = $requirement['title'] . ': ' . $requirement['value'] . "\n\n" . $requirement['description'];
+        }
+      }
+      if (!empty($failures)) {
+        throw new InstallerException(implode("\n\n", $failures));
+      }
+    }
+  }
+}
+
+/**
+ * Installation task; writes profile to settings.php if possible.
+ *
+ * @param array $install_state
+ *   An array of information about the current installation state.
+ *
+ * @see _install_select_profile()
+ *
+ * @deprecated in drupal:8.3.0 and is removed from drupal:9.0.0. The
+ *    install profile is written to core.extension.
+ */
+function install_write_profile($install_state) {
+  // Only write the install profile to settings.php if it already exists. The
+  // value from settings.php is never used but drupal_rewrite_settings() does
+  // not support removing a setting. If the value is present in settings.php
+  // there will be an informational notice on the status report.
+  $settings_path = \Drupal::service('site.path') . '/settings.php';
+  if (is_writable($settings_path) && array_key_exists('install_profile', Settings::getAll())) {
+    // Remember the profile which was used.
+    $settings['settings']['install_profile'] = (object) [
+      'value' => $install_state['parameters']['profile'],
+      'required' => TRUE,
+    ];
+    drupal_rewrite_settings($settings);
+  }
+}
+
+/**
+ * Creates a batch for the config importer to process.
+ *
+ * @see install_tasks()
+ */
+function install_config_import_batch() {
+  // We need to manually trigger the installation of core-provided entity types,
+  // as those will not be handled by the module installer.
+  // @see install_profile_modules()
+  install_core_entity_type_definitions();
+
+  // Get the sync storage.
+  $sync = \Drupal::service('config.storage.sync');
+  // Match up the site UUIDs, the install_base_system install task will have
+  // installed the system module and created a new UUID.
+  $system_site = $sync->read('system.site');
+  // When installing from configuration it is possible that system.site
+  // configuration is not present. If this occurs a ConfigImporterException will
+  // by thrown when $config_importer->initialize() is called below and the error
+  // will be reported to the user.
+  if ($system_site !== FALSE) {
+    \Drupal::configFactory()->getEditable('system.site')->set('uuid', $system_site['uuid'])->save();
+  }
+
+  // Create the storage comparer and the config importer.
+  $storage_comparer = new StorageComparer($sync, \Drupal::service('config.storage'));
+  $storage_comparer->createChangelist();
+  $config_importer = new ConfigImporter(
+    $storage_comparer,
+    \Drupal::service('event_dispatcher'),
+    \Drupal::service('config.manager'),
+    \Drupal::service('lock.persistent'),
+    \Drupal::service('config.typed'),
+    \Drupal::service('module_handler'),
+    \Drupal::service('module_installer'),
+    \Drupal::service('theme_handler'),
+    \Drupal::service('string_translation'),
+    \Drupal::service('extension.list.module')
+  );
+
+  try {
+    $sync_steps = $config_importer->initialize();
+
+    $batch_builder = new BatchBuilder();
+    $batch_builder
+      ->setFinishCallback([ConfigImporterBatch::class, 'finish'])
+      ->setTitle(t('Importing configuration'))
+      ->setInitMessage(t('Starting configuration import.'))
+      ->setErrorMessage(t('Configuration import has encountered an error.'));
+
+    foreach ($sync_steps as $sync_step) {
+      $batch_builder->addOperation([ConfigImporterBatch::class, 'process'], [$config_importer, $sync_step]);
+    }
+
+    return $batch_builder->toArray();
+  }
+  catch (ConfigImporterException $e) {
+    global $install_state;
+    // There are validation errors.
+    $messenger = \Drupal::messenger();
+    $messenger->addError(t('The configuration synchronization failed validation.'));
+    foreach ($config_importer->getErrors() as $message) {
+      $messenger->addError($message);
+    }
+    install_display_output(['#title' => t('Configuration validation')], $install_state);
+  }
+}
+
+/**
+ * Replaces install_download_translation() during configuration installs.
+ *
+ * @param array $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return string
+ *   A themed status report, or an exception if there are requirement errors.
+ *   Upon successful download the page is reloaded and no output is returned.
+ *
+ * @see install_download_translation()
+ */
+function install_config_download_translations(&$install_state) {
+  $needs_download = isset($install_state['parameters']['langcode']) && !isset($install_state['translations'][$install_state['parameters']['langcode']]) && $install_state['parameters']['langcode'] !== 'en';
+  if ($needs_download) {
+    return install_download_translation($install_state);
+  }
+}
+
+/**
+ * Reverts configuration if hook_install() implementations have made changes.
+ *
+ * This step ensures that the final configuration matches the configuration
+ * provided to the installer.
+ */
+function install_config_revert_install_changes() {
+  global $install_state;
+
+  $storage_comparer = new StorageComparer(\Drupal::service('config.storage.sync'), \Drupal::service('config.storage'));
+  $storage_comparer->createChangelist();
+  if ($storage_comparer->hasChanges()) {
+    $config_importer = new ConfigImporter(
+      $storage_comparer,
+      \Drupal::service('event_dispatcher'),
+      \Drupal::service('config.manager'),
+      \Drupal::service('lock.persistent'),
+      \Drupal::service('config.typed'),
+      \Drupal::service('module_handler'),
+      \Drupal::service('module_installer'),
+      \Drupal::service('theme_handler'),
+      \Drupal::service('string_translation'),
+      \Drupal::service('extension.list.module')
+    );
+    try {
+      $config_importer->import();
+    }
+    catch (ConfigImporterException $e) {
+      global $install_state;
+      $messenger = \Drupal::messenger();
+      // There are validation errors.
+      $messenger->addError(t('The configuration synchronization failed validation.'));
+      foreach ($config_importer->getErrors() as $message) {
+        $messenger->addError($message);
+      }
+      install_display_output(['#title' => t('Configuration validation')], $install_state);
+    }
+
+    // At this point the configuration should match completely.
+    if (\Drupal::moduleHandler()->moduleExists('language')) {
+      // If the English language exists at this point we need to ensure
+      // install_download_additional_translations_operations() does not delete
+      // it.
+      if (ConfigurableLanguage::load('en')) {
+        $install_state['profile_info']['keep_english'] = TRUE;
+      }
+    }
+  }
+}

+ 1254 - 0
web/core/includes/install.inc

@@ -0,0 +1,1254 @@
+<?php
+
+/**
+ * @file
+ * API functions for installing modules and themes.
+ */
+
+use Drupal\Component\Utility\Crypt;
+use Drupal\Component\Utility\OpCodeCache;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Extension\Dependency;
+use Drupal\Core\Extension\ExtensionDiscovery;
+use Drupal\Core\File\FileSystemInterface;
+use Drupal\Core\Installer\InstallerKernel;
+use Drupal\Core\Site\Settings;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
+/**
+ * Requirement severity -- Informational message only.
+ */
+const REQUIREMENT_INFO = -1;
+
+/**
+ * Requirement severity -- Requirement successfully met.
+ */
+const REQUIREMENT_OK = 0;
+
+/**
+ * Requirement severity -- Warning condition; proceed but flag warning.
+ */
+const REQUIREMENT_WARNING = 1;
+
+/**
+ * Requirement severity -- Error condition; abort installation.
+ */
+const REQUIREMENT_ERROR = 2;
+
+/**
+ * File permission check -- File exists.
+ */
+const FILE_EXIST = 1;
+
+/**
+ * File permission check -- File is readable.
+ */
+const FILE_READABLE = 2;
+
+/**
+ * File permission check -- File is writable.
+ */
+const FILE_WRITABLE = 4;
+
+/**
+ * File permission check -- File is executable.
+ */
+const FILE_EXECUTABLE = 8;
+
+/**
+ * File permission check -- File does not exist.
+ */
+const FILE_NOT_EXIST = 16;
+
+/**
+ * File permission check -- File is not readable.
+ */
+const FILE_NOT_READABLE = 32;
+
+/**
+ * File permission check -- File is not writable.
+ */
+const FILE_NOT_WRITABLE = 64;
+
+/**
+ * File permission check -- File is not executable.
+ */
+const FILE_NOT_EXECUTABLE = 128;
+
+/**
+ * Loads .install files for installed modules to initialize the update system.
+ */
+function drupal_load_updates() {
+  /** @var \Drupal\Core\Extension\ModuleExtensionList $extension_list_module */
+  $extension_list_module = \Drupal::service('extension.list.module');
+  foreach (drupal_get_installed_schema_version(NULL, FALSE, TRUE) as $module => $schema_version) {
+    if ($extension_list_module->exists($module) && !$extension_list_module->checkIncompatibility($module)) {
+      if ($schema_version > -1) {
+        module_load_install($module);
+      }
+    }
+  }
+}
+
+/**
+ * Loads the installation profile, extracting its defined distribution name.
+ *
+ * @return
+ *   The distribution name defined in the profile's .info.yml file. Defaults to
+ *   "Drupal" if none is explicitly provided by the installation profile.
+ *
+ * @see install_profile_info()
+ */
+function drupal_install_profile_distribution_name() {
+  // During installation, the profile information is stored in the global
+  // installation state (it might not be saved anywhere yet).
+  $info = [];
+  if (InstallerKernel::installationAttempted()) {
+    global $install_state;
+    if (isset($install_state['profile_info'])) {
+      $info = $install_state['profile_info'];
+    }
+  }
+  // At all other times, we load the profile via standard methods.
+  else {
+    $profile = \Drupal::installProfile();
+    $info = \Drupal::service('extension.list.profile')->getExtensionInfo($profile);
+  }
+  return isset($info['distribution']['name']) ? $info['distribution']['name'] : 'Drupal';
+}
+
+/**
+ * Loads the installation profile, extracting its defined version.
+ *
+ * @return string
+ *   Distribution version defined in the profile's .info.yml file.
+ *   Defaults to \Drupal::VERSION if no version is explicitly provided by the
+ *   installation profile.
+ *
+ * @see install_profile_info()
+ */
+function drupal_install_profile_distribution_version() {
+  // During installation, the profile information is stored in the global
+  // installation state (it might not be saved anywhere yet).
+  if (InstallerKernel::installationAttempted()) {
+    global $install_state;
+    return isset($install_state['profile_info']['version']) ? $install_state['profile_info']['version'] : \Drupal::VERSION;
+  }
+  // At all other times, we load the profile via standard methods.
+  else {
+    $profile = \Drupal::installProfile();
+    $info = \Drupal::service('extension.list.profile')->getExtensionInfo($profile);
+    return $info['version'];
+  }
+}
+
+/**
+ * Detects all supported databases that are compiled into PHP.
+ *
+ * @return
+ *   An array of database types compiled into PHP.
+ */
+function drupal_detect_database_types() {
+  $databases = drupal_get_database_types();
+
+  foreach ($databases as $driver => $installer) {
+    $databases[$driver] = $installer->name();
+  }
+
+  return $databases;
+}
+
+/**
+ * Returns all supported database driver installer objects.
+ *
+ * @return \Drupal\Core\Database\Install\Tasks[]
+ *   An array of available database driver installer objects.
+ */
+function drupal_get_database_types() {
+  $databases = [];
+  $drivers = [];
+
+  // The internal database driver name is any valid PHP identifier.
+  $mask = ExtensionDiscovery::PHP_FUNCTION_PATTERN;
+
+  // Find drivers in the Drupal\Core and Drupal\Driver namespaces.
+  /** @var \Drupal\Core\File\FileSystemInterface $file_system */
+  $file_system = \Drupal::service('file_system');
+  $files = $file_system->scanDirectory(DRUPAL_ROOT . '/core/lib/Drupal/Core/Database/Driver', $mask, ['recurse' => FALSE]);
+  if (is_dir(DRUPAL_ROOT . '/drivers/lib/Drupal/Driver/Database')) {
+    $files += $file_system->scanDirectory(DRUPAL_ROOT . '/drivers/lib/Drupal/Driver/Database/', $mask, ['recurse' => FALSE]);
+  }
+  foreach ($files as $file) {
+    if (file_exists($file->uri . '/Install/Tasks.php')) {
+      // The namespace doesn't need to be added here, because
+      // db_installer_object() will find it.
+      $drivers[$file->filename] = NULL;
+    }
+  }
+
+  // Find drivers in Drupal module namespaces.
+  /** @var \Composer\Autoload\ClassLoader $class_loader */
+  $class_loader = \Drupal::service('class_loader');
+  // We cannot use the file cache because it does not always exist.
+  $extension_discovery = new ExtensionDiscovery(DRUPAL_ROOT, FALSE, []);
+  $modules = $extension_discovery->scan('module');
+  foreach ($modules as $module) {
+    $module_driver_path = DRUPAL_ROOT . '/' . $module->getPath() . '/src/Driver/Database';
+    if (is_dir($module_driver_path)) {
+      $driver_files = $file_system->scanDirectory($module_driver_path, $mask, ['recurse' => FALSE]);
+      foreach ($driver_files as $driver_file) {
+        $tasks_file = $module_driver_path . '/' . $driver_file->filename . '/Install/Tasks.php';
+        if (file_exists($tasks_file)) {
+          $namespace = 'Drupal\\' . $module->getName() . '\\Driver\\Database\\' . $driver_file->filename;
+
+          // The namespace needs to be added for db_installer_object() to find
+          // it.
+          $drivers[$driver_file->filename] = $namespace;
+
+          // The directory needs to be added to the autoloader, because this is
+          // early in the installation process: the module hasn't been enabled
+          // yet and the database connection info array (including its 'autoload'
+          // key) hasn't been created yet.
+          $class_loader->addPsr4($namespace . '\\', $module->getPath() . '/src/Driver/Database/' . $driver_file->filename);
+        }
+      }
+    }
+  }
+
+  foreach ($drivers as $driver => $namespace) {
+    $installer = db_installer_object($driver, $namespace);
+    if ($installer->installable()) {
+      $databases[$driver] = $installer;
+    }
+  }
+
+  // Usability: unconditionally put the MySQL driver on top.
+  if (isset($databases['mysql'])) {
+    $mysql_database = $databases['mysql'];
+    unset($databases['mysql']);
+    $databases = ['mysql' => $mysql_database] + $databases;
+  }
+
+  return $databases;
+}
+
+/**
+ * Replaces values in settings.php with values in the submitted array.
+ *
+ * This function replaces values in place if possible, even for
+ * multidimensional arrays. This way the old settings do not linger,
+ * overridden and also the doxygen on a value remains where it should be.
+ *
+ * @param $settings
+ *   An array of settings that need to be updated. Multidimensional arrays
+ *   are dumped up to a stdClass object. The object can have value, required
+ *   and comment properties.
+ *   @code
+ *   $settings['settings']['config_sync_directory'] = (object) array(
+ *     'value' => 'config_hash/sync',
+ *     'required' => TRUE,
+ *   );
+ *   @endcode
+ *   gets dumped as:
+ *   @code
+ *   $settings['config_sync_directory'] = 'config_hash/sync'
+ *   @endcode
+ */
+function drupal_rewrite_settings($settings = [], $settings_file = NULL) {
+  if (!isset($settings_file)) {
+    $settings_file = \Drupal::service('site.path') . '/settings.php';
+  }
+  // Build list of setting names and insert the values into the global namespace.
+  $variable_names = [];
+  $settings_settings = [];
+  foreach ($settings as $setting => $data) {
+    if ($setting != 'settings') {
+      _drupal_rewrite_settings_global($GLOBALS[$setting], $data);
+    }
+    else {
+      _drupal_rewrite_settings_global($settings_settings, $data);
+    }
+    $variable_names['$' . $setting] = $setting;
+  }
+  $contents = file_get_contents($settings_file);
+  if ($contents !== FALSE) {
+    // Initialize the contents for the settings.php file if it is empty.
+    if (trim($contents) === '') {
+      $contents = "<?php\n";
+    }
+    // Step through each token in settings.php and replace any variables that
+    // are in the passed-in array.
+    $buffer = '';
+    $state = 'default';
+    foreach (token_get_all($contents) as $token) {
+      if (is_array($token)) {
+        list($type, $value) = $token;
+      }
+      else {
+        $type = -1;
+        $value = $token;
+      }
+      // Do not operate on whitespace.
+      if (!in_array($type, [T_WHITESPACE, T_COMMENT, T_DOC_COMMENT])) {
+        switch ($state) {
+          case 'default':
+            if ($type === T_VARIABLE && isset($variable_names[$value])) {
+              // This will be necessary to unset the dumped variable.
+              $parent = &$settings;
+              // This is the current index in parent.
+              $index = $variable_names[$value];
+              // This will be necessary for descending into the array.
+              $current = &$parent[$index];
+              $state = 'candidate_left';
+            }
+            break;
+
+          case 'candidate_left':
+            if ($value == '[') {
+              $state = 'array_index';
+            }
+            if ($value == '=') {
+              $state = 'candidate_right';
+            }
+            break;
+
+          case 'array_index':
+            if (_drupal_rewrite_settings_is_array_index($type, $value)) {
+              $index = trim($value, '\'"');
+              $state = 'right_bracket';
+            }
+            else {
+              // $a[foo()] or $a[$bar] or something like that.
+              throw new Exception('invalid array index');
+            }
+            break;
+
+          case 'right_bracket':
+            if ($value == ']') {
+              if (isset($current[$index])) {
+                // If the new settings has this index, descend into it.
+                $parent = &$current;
+                $current = &$parent[$index];
+                $state = 'candidate_left';
+              }
+              else {
+                // Otherwise, jump back to the default state.
+                $state = 'wait_for_semicolon';
+              }
+            }
+            else {
+              // $a[1 + 2].
+              throw new Exception('] expected');
+            }
+            break;
+
+          case 'candidate_right':
+            if (_drupal_rewrite_settings_is_simple($type, $value)) {
+              $value = _drupal_rewrite_settings_dump_one($current);
+              // Unsetting $current would not affect $settings at all.
+              unset($parent[$index]);
+              // Skip the semicolon because _drupal_rewrite_settings_dump_one() added one.
+              $state = 'semicolon_skip';
+            }
+            else {
+              $state = 'wait_for_semicolon';
+            }
+            break;
+
+          case 'wait_for_semicolon':
+            if ($value == ';') {
+              $state = 'default';
+            }
+            break;
+
+          case 'semicolon_skip':
+            if ($value == ';') {
+              $value = '';
+              $state = 'default';
+            }
+            else {
+              // If the expression was $a = 1 + 2; then we replaced 1 and
+              // the + is unexpected.
+              throw new Exception('Unexpected token after replacing value.');
+            }
+            break;
+        }
+      }
+      $buffer .= $value;
+    }
+    foreach ($settings as $name => $setting) {
+      $buffer .= _drupal_rewrite_settings_dump($setting, '$' . $name);
+    }
+
+    // Write the new settings file.
+    if (file_put_contents($settings_file, $buffer) === FALSE) {
+      throw new Exception("Failed to modify '$settings_file'. Verify the file permissions.");
+    }
+    else {
+      // In case any $settings variables were written, import them into the
+      // Settings singleton.
+      if (!empty($settings_settings)) {
+        $old_settings = Settings::getAll();
+        new Settings($settings_settings + $old_settings);
+      }
+      // The existing settings.php file might have been included already. In
+      // case an opcode cache is enabled, the rewritten contents of the file
+      // will not be reflected in this process. Ensure to invalidate the file
+      // in case an opcode cache is enabled.
+      OpCodeCache::invalidate(DRUPAL_ROOT . '/' . $settings_file);
+    }
+  }
+  else {
+    throw new Exception("Failed to open '$settings_file'. Verify the file permissions.");
+  }
+}
+
+/**
+ * Helper for drupal_rewrite_settings().
+ *
+ * Checks whether this token represents a scalar or NULL.
+ *
+ * @param int $type
+ *   The token type.
+ * @param string $value
+ *   The value of the token.
+ *
+ * @return bool
+ *   TRUE if this token represents a scalar or NULL.
+ *
+ * @see token_name()
+ */
+function _drupal_rewrite_settings_is_simple($type, $value) {
+  $is_integer = $type == T_LNUMBER;
+  $is_float = $type == T_DNUMBER;
+  $is_string = $type == T_CONSTANT_ENCAPSED_STRING;
+  $is_boolean_or_null = $type == T_STRING && in_array(strtoupper($value), ['TRUE', 'FALSE', 'NULL']);
+  return $is_integer || $is_float || $is_string || $is_boolean_or_null;
+}
+
+/**
+ * Helper for drupal_rewrite_settings().
+ *
+ * Checks whether this token represents a valid array index: a number or a
+ * string.
+ *
+ * @param int $type
+ *   The token type.
+ *
+ * @return bool
+ *   TRUE if this token represents a number or a string.
+ *
+ * @see token_name()
+ */
+function _drupal_rewrite_settings_is_array_index($type) {
+  $is_integer = $type == T_LNUMBER;
+  $is_float = $type == T_DNUMBER;
+  $is_string = $type == T_CONSTANT_ENCAPSED_STRING;
+  return $is_integer || $is_float || $is_string;
+}
+
+/**
+ * Helper for drupal_rewrite_settings().
+ *
+ * Makes the new settings global.
+ *
+ * @param array|null $ref
+ *   A reference to a nested index in $GLOBALS.
+ * @param array|object $variable
+ *   The nested value of the setting being copied.
+ */
+function _drupal_rewrite_settings_global(&$ref, $variable) {
+  if (is_object($variable)) {
+    $ref = $variable->value;
+  }
+  else {
+    foreach ($variable as $k => $v) {
+      _drupal_rewrite_settings_global($ref[$k], $v);
+    }
+  }
+}
+
+/**
+ * Helper for drupal_rewrite_settings().
+ *
+ * Dump the relevant value properties.
+ *
+ * @param array|object $variable
+ *   The container for variable values.
+ * @param string $variable_name
+ *   Name of variable.
+ * @return string
+ *   A string containing valid PHP code of the variable suitable for placing
+ *   into settings.php.
+ */
+function _drupal_rewrite_settings_dump($variable, $variable_name) {
+  $return = '';
+  if (is_object($variable)) {
+    if (!empty($variable->required)) {
+      $return .= _drupal_rewrite_settings_dump_one($variable, "$variable_name = ", "\n");
+    }
+  }
+  else {
+    foreach ($variable as $k => $v) {
+      $return .= _drupal_rewrite_settings_dump($v, $variable_name . "['" . $k . "']");
+    }
+  }
+  return $return;
+}
+
+/**
+ * Helper for drupal_rewrite_settings().
+ *
+ * Dump the value of a value property and adds the comment if it exists.
+ *
+ * @param object $variable
+ *   A stdClass object with at least a value property.
+ * @param string $prefix
+ *   A string to prepend to the variable's value.
+ * @param string $suffix
+ *   A string to append to the variable's value.
+ * @return string
+ *   A string containing valid PHP code of the variable suitable for placing
+ *   into settings.php.
+ */
+function _drupal_rewrite_settings_dump_one(\stdClass $variable, $prefix = '', $suffix = '') {
+  $return = $prefix . var_export($variable->value, TRUE) . ';';
+  if (!empty($variable->comment)) {
+    $return .= ' // ' . $variable->comment;
+  }
+  $return .= $suffix;
+  return $return;
+}
+
+/**
+ * Creates the config directory and ensures it is operational.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. There is no
+ *   replacement.
+ *
+ * @see https://www.drupal.org/node/3018145
+ */
+function drupal_install_config_directories() {
+  @trigger_error('drupal_install_config_directories() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. There is no replacement. See https://www.drupal.org/node/3018145.', E_USER_DEPRECATED);
+  global $config_directories, $install_state;
+
+  // If settings.php does not contain a config sync directory name we need to
+  // configure one.
+  if (empty($config_directories[CONFIG_SYNC_DIRECTORY])) {
+    if (empty($install_state['config_install_path'])) {
+      // Add a randomized config directory name to settings.php
+      $config_directories[CONFIG_SYNC_DIRECTORY] = \Drupal::service('site.path') . '/files/config_' . Crypt::randomBytesBase64(55) . '/sync';
+    }
+    else {
+      // Install profiles can contain a config sync directory. If they do,
+      // 'config_install_path' is a path to the directory.
+      $config_directories[CONFIG_SYNC_DIRECTORY] = $install_state['config_install_path'];
+    }
+    $settings['config_directories'][CONFIG_SYNC_DIRECTORY] = (object) [
+      'value' => $config_directories[CONFIG_SYNC_DIRECTORY],
+      'required' => TRUE,
+    ];
+    // Rewrite settings.php, which also sets the value as global variable.
+    drupal_rewrite_settings($settings);
+  }
+
+  // This should never fail, since if the config directory was specified in
+  // settings.php it will have already been created and verified earlier, and
+  // if it wasn't specified in settings.php, it is created here inside the
+  // public files directory, which has already been verified to be writable
+  // itself. But if it somehow fails anyway, the installation cannot proceed.
+  // Bail out using a similar error message as in system_requirements().
+  if (!\Drupal::service('file_system')->prepareDirectory($config_directories[CONFIG_SYNC_DIRECTORY], FileSystemInterface::CREATE_DIRECTORY)
+    && !file_exists($config_directories[CONFIG_SYNC_DIRECTORY])) {
+    throw new Exception("The directory '" . config_get_config_directory(CONFIG_SYNC_DIRECTORY) . "' could not be created. To proceed with the installation, either create the directory or ensure that the installer has the permissions to create it automatically. For more information, see the <a href='https://www.drupal.org/server-permissions'>online handbook</a>.");
+  }
+  elseif (is_writable($config_directories[CONFIG_SYNC_DIRECTORY])) {
+    // Put a README.txt into the sync config directory. This is required so that
+    // they can later be added to git. Since this directory is auto-created, we
+    // have to write out the README rather than just adding it to the drupal core
+    // repo.
+    $text = 'This directory contains configuration to be imported into your Drupal site. To make this configuration active, visit admin/config/development/configuration/sync.' . ' For information about deploying configuration between servers, see https://www.drupal.org/documentation/administer/config';
+    file_put_contents(config_get_config_directory(CONFIG_SYNC_DIRECTORY) . '/README.txt', "$text\n");
+  }
+}
+
+/**
+ * Ensures that the config directory exists and is writable, or can be made so.
+ *
+ * @param string $type
+ *   Type of config directory to return. Drupal core provides 'sync'.
+ *
+ * @return bool
+ *   TRUE if the config directory exists and is writable.
+ *
+ * @deprecated in drupal:8.1.0 and is removed from drupal:9.0.0. Use
+ *   config_get_config_directory() and
+ *  \Drupal\Core\File\FileSystemInterface::prepareDirectory() instead.
+ *
+ * @see https://www.drupal.org/node/2501187
+ */
+function install_ensure_config_directory($type) {
+  @trigger_error('install_ensure_config_directory() is deprecated in Drupal 8.1.0 and will be removed before Drupal 9.0.0. Use config_get_config_directory() and \Drupal\Core\File\FileSystemInterface::prepareDirectory() instead. See https://www.drupal.org/node/2501187.', E_USER_DEPRECATED);
+  // The config directory must be defined in settings.php.
+  global $config_directories;
+  if (!isset($config_directories[$type])) {
+    return FALSE;
+  }
+  // The logic here is similar to that used by system_requirements() for other
+  // directories that the installer creates.
+  else {
+    $config_directory = config_get_config_directory($type);
+    return \Drupal::service('file_system')->prepareDirectory($config_directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
+  }
+}
+
+/**
+ * Verifies that all dependencies are met for a given installation profile.
+ *
+ * @param $install_state
+ *   An array of information about the current installation state.
+ *
+ * @return
+ *   The list of modules to install.
+ */
+function drupal_verify_profile($install_state) {
+  $profile = $install_state['parameters']['profile'];
+  $info = $install_state['profile_info'];
+
+  // Get the list of available modules for the selected installation profile.
+  $listing = new ExtensionDiscovery(\Drupal::root());
+  $present_modules = [];
+  foreach ($listing->scan('module') as $present_module) {
+    $present_modules[] = $present_module->getName();
+  }
+
+  // The installation profile is also a module, which needs to be installed
+  // after all the other dependencies have been installed.
+  $present_modules[] = $profile;
+
+  // Verify that all of the profile's required modules are present.
+  $missing_modules = array_diff($info['install'], $present_modules);
+
+  $requirements = [];
+
+  if ($missing_modules) {
+    $build = [
+      '#theme' => 'item_list',
+      '#context' => ['list_style' => 'comma-list'],
+    ];
+
+    foreach ($missing_modules as $module) {
+      $build['#items'][] = ['#markup' => '<span class="admin-missing">' . Unicode::ucfirst($module) . '</span>'];
+    }
+
+    $modules_list = \Drupal::service('renderer')->renderPlain($build);
+    $requirements['required_modules'] = [
+      'title' => t('Required modules'),
+      'value' => t('Required modules not found.'),
+      'severity' => REQUIREMENT_ERROR,
+      'description' => t('The following modules are required but were not found. Move them into the appropriate modules subdirectory, such as <em>/modules</em>. Missing modules: @modules', ['@modules' => $modules_list]),
+    ];
+  }
+  return $requirements;
+}
+
+/**
+ * Installs the system module.
+ *
+ * Separated from the installation of other modules so core system
+ * functions can be made available while other modules are installed.
+ *
+ * @param array $install_state
+ *   An array of information about the current installation state. This is used
+ *   to set the default language.
+ */
+function drupal_install_system($install_state) {
+  // Remove the service provider of the early installer.
+  unset($GLOBALS['conf']['container_service_providers']['InstallerServiceProvider']);
+  // Add the normal installer service provider.
+  $GLOBALS['conf']['container_service_providers']['InstallerServiceProvider'] = 'Drupal\Core\Installer\NormalInstallerServiceProvider';
+
+  // Get the existing request.
+  $request = \Drupal::request();
+  // Reboot into a full production environment to continue the installation.
+  /** @var \Drupal\Core\Installer\InstallerKernel $kernel */
+  $kernel = \Drupal::service('kernel');
+  $kernel->shutdown();
+  // Have installer rebuild from the disk, rather then building from scratch.
+  $kernel->rebuildContainer(FALSE);
+  // Reboot the kernel with new container.
+  $kernel->boot();
+  $kernel->preHandle($request);
+  // Ensure our request includes the session if appropriate.
+  if (PHP_SAPI !== 'cli') {
+    $request->setSession($kernel->getContainer()->get('session'));
+  }
+
+  // Before having installed the system module and being able to do a module
+  // rebuild, prime the \Drupal\Core\Extension\ModuleExtensionList static cache
+  // with the module's location.
+  // @todo Try to install system as any other module, see
+  //   https://www.drupal.org/node/2719315.
+  \Drupal::service('extension.list.module')->setPathname('system', 'core/modules/system/system.info.yml');
+
+  // Install base system configuration.
+  \Drupal::service('config.installer')->installDefaultConfig('core', 'core');
+
+  // Store the installation profile in configuration to populate the
+  // 'install_profile' container parameter.
+  \Drupal::configFactory()->getEditable('core.extension')
+    ->set('profile', $install_state['parameters']['profile'])
+    ->save();
+
+  // Install System module and rebuild the newly available routes.
+  $kernel->getContainer()->get('module_installer')->install(['system'], FALSE);
+  \Drupal::service('router.builder')->rebuild();
+
+  // Ensure default language is saved.
+  if (isset($install_state['parameters']['langcode'])) {
+    \Drupal::configFactory()->getEditable('system.site')
+      ->set('langcode', (string) $install_state['parameters']['langcode'])
+      ->set('default_langcode', (string) $install_state['parameters']['langcode'])
+      ->save(TRUE);
+  }
+}
+
+/**
+ * Verifies the state of the specified file.
+ *
+ * @param $file
+ *   The file to check for.
+ * @param $mask
+ *   An optional bitmask created from various FILE_* constants.
+ * @param $type
+ *   The type of file. Can be file (default), dir, or link.
+ * @param bool $autofix
+ *   (optional) Determines whether to attempt fixing the permissions according
+ *   to the provided $mask. Defaults to TRUE.
+ *
+ * @return
+ *   TRUE on success or FALSE on failure. A message is set for the latter.
+ */
+function drupal_verify_install_file($file, $mask = NULL, $type = 'file', $autofix = TRUE) {
+  $return = TRUE;
+  // Check for files that shouldn't be there.
+  if (isset($mask) && ($mask & FILE_NOT_EXIST) && file_exists($file)) {
+    return FALSE;
+  }
+  // Verify that the file is the type of file it is supposed to be.
+  if (isset($type) && file_exists($file)) {
+    $check = 'is_' . $type;
+    if (!function_exists($check) || !$check($file)) {
+      $return = FALSE;
+    }
+  }
+
+  // Verify file permissions.
+  if (isset($mask)) {
+    $masks = [FILE_EXIST, FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE];
+    foreach ($masks as $current_mask) {
+      if ($mask & $current_mask) {
+        switch ($current_mask) {
+          case FILE_EXIST:
+            if (!file_exists($file)) {
+              if ($type == 'dir' && $autofix) {
+                drupal_install_mkdir($file, $mask);
+              }
+              if (!file_exists($file)) {
+                $return = FALSE;
+              }
+            }
+            break;
+
+          case FILE_READABLE:
+            if (!is_readable($file)) {
+              $return = FALSE;
+            }
+            break;
+
+          case FILE_WRITABLE:
+            if (!is_writable($file)) {
+              $return = FALSE;
+            }
+            break;
+
+          case FILE_EXECUTABLE:
+            if (!is_executable($file)) {
+              $return = FALSE;
+            }
+            break;
+
+          case FILE_NOT_READABLE:
+            if (is_readable($file)) {
+              $return = FALSE;
+            }
+            break;
+
+          case FILE_NOT_WRITABLE:
+            if (is_writable($file)) {
+              $return = FALSE;
+            }
+            break;
+
+          case FILE_NOT_EXECUTABLE:
+            if (is_executable($file)) {
+              $return = FALSE;
+            }
+            break;
+        }
+      }
+    }
+  }
+  if (!$return && $autofix) {
+    return drupal_install_fix_file($file, $mask);
+  }
+  return $return;
+}
+
+/**
+ * Creates a directory with the specified permissions.
+ *
+ * @param $file
+ *   The name of the directory to create;
+ * @param $mask
+ *   The permissions of the directory to create.
+ * @param $message
+ *   (optional) Whether to output messages. Defaults to TRUE.
+ *
+ * @return
+ *   TRUE/FALSE whether or not the directory was successfully created.
+ */
+function drupal_install_mkdir($file, $mask, $message = TRUE) {
+  $mod = 0;
+  $masks = [FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE];
+  foreach ($masks as $m) {
+    if ($mask & $m) {
+      switch ($m) {
+        case FILE_READABLE:
+          $mod |= 0444;
+          break;
+
+        case FILE_WRITABLE:
+          $mod |= 0222;
+          break;
+
+        case FILE_EXECUTABLE:
+          $mod |= 0111;
+          break;
+      }
+    }
+  }
+
+  if (@\Drupal::service('file_system')->mkdir($file, $mod)) {
+    return TRUE;
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
+ * Attempts to fix file permissions.
+ *
+ * The general approach here is that, because we do not know the security
+ * setup of the webserver, we apply our permission changes to all three
+ * digits of the file permission (i.e. user, group and all).
+ *
+ * To ensure that the values behave as expected (and numbers don't carry
+ * from one digit to the next) we do the calculation on the octal value
+ * using bitwise operations. This lets us remove, for example, 0222 from
+ * 0700 and get the correct value of 0500.
+ *
+ * @param $file
+ *   The name of the file with permissions to fix.
+ * @param $mask
+ *   The desired permissions for the file.
+ * @param $message
+ *   (optional) Whether to output messages. Defaults to TRUE.
+ *
+ * @return
+ *   TRUE/FALSE whether or not we were able to fix the file's permissions.
+ */
+function drupal_install_fix_file($file, $mask, $message = TRUE) {
+  // If $file does not exist, fileperms() issues a PHP warning.
+  if (!file_exists($file)) {
+    return FALSE;
+  }
+
+  $mod = fileperms($file) & 0777;
+  $masks = [FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE];
+
+  // FILE_READABLE, FILE_WRITABLE, and FILE_EXECUTABLE permission strings
+  // can theoretically be 0400, 0200, and 0100 respectively, but to be safe
+  // we set all three access types in case the administrator intends to
+  // change the owner of settings.php after installation.
+  foreach ($masks as $m) {
+    if ($mask & $m) {
+      switch ($m) {
+        case FILE_READABLE:
+          if (!is_readable($file)) {
+            $mod |= 0444;
+          }
+          break;
+
+        case FILE_WRITABLE:
+          if (!is_writable($file)) {
+            $mod |= 0222;
+          }
+          break;
+
+        case FILE_EXECUTABLE:
+          if (!is_executable($file)) {
+            $mod |= 0111;
+          }
+          break;
+
+        case FILE_NOT_READABLE:
+          if (is_readable($file)) {
+            $mod &= ~0444;
+          }
+          break;
+
+        case FILE_NOT_WRITABLE:
+          if (is_writable($file)) {
+            $mod &= ~0222;
+          }
+          break;
+
+        case FILE_NOT_EXECUTABLE:
+          if (is_executable($file)) {
+            $mod &= ~0111;
+          }
+          break;
+      }
+    }
+  }
+
+  // chmod() will work if the web server is running as owner of the file.
+  if (@chmod($file, $mod)) {
+    return TRUE;
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
+ * Sends the user to a different installer page.
+ *
+ * This issues an on-site HTTP redirect. Messages (and errors) are erased.
+ *
+ * @param $path
+ *   An installer path.
+ */
+function install_goto($path) {
+  global $base_url;
+  $headers = [
+    // Not a permanent redirect.
+    'Cache-Control' => 'no-cache',
+  ];
+  $response = new RedirectResponse($base_url . '/' . $path, 302, $headers);
+  $response->send();
+}
+
+/**
+ * Returns the URL of the current script, with modified query parameters.
+ *
+ * This function can be called by low-level scripts (such as install.php and
+ * update.php) and returns the URL of the current script. Existing query
+ * parameters are preserved by default, but new ones can optionally be merged
+ * in.
+ *
+ * This function is used when the script must maintain certain query parameters
+ * over multiple page requests in order to work correctly. In such cases (for
+ * example, update.php, which requires the 'continue=1' parameter to remain in
+ * the URL throughout the update process if there are any requirement warnings
+ * that need to be bypassed), using this function to generate the URL for links
+ * to the next steps of the script ensures that the links will work correctly.
+ *
+ * @param $query
+ *   (optional) An array of query parameters to merge in to the existing ones.
+ *
+ * @return
+ *   The URL of the current script, with query parameters modified by the
+ *   passed-in $query. The URL is not sanitized, so it still needs to be run
+ *   through \Drupal\Component\Utility\UrlHelper::filterBadProtocol() if it will be
+ *   used as an HTML attribute value.
+ *
+ * @see drupal_requirements_url()
+ * @see Drupal\Component\Utility\UrlHelper::filterBadProtocol()
+ */
+function drupal_current_script_url($query = []) {
+  $uri = $_SERVER['SCRIPT_NAME'];
+  $query = array_merge(UrlHelper::filterQueryParameters(\Drupal::request()->query->all()), $query);
+  if (!empty($query)) {
+    $uri .= '?' . UrlHelper::buildQuery($query);
+  }
+  return $uri;
+}
+
+/**
+ * Returns a URL for proceeding to the next page after a requirements problem.
+ *
+ * This function can be called by low-level scripts (such as install.php and
+ * update.php) and returns a URL that can be used to attempt to proceed to the
+ * next step of the script.
+ *
+ * @param $severity
+ *   The severity of the requirements problem, as returned by
+ *   drupal_requirements_severity().
+ *
+ * @return
+ *   A URL for attempting to proceed to the next step of the script. The URL is
+ *   not sanitized, so it still needs to be run through
+ *   \Drupal\Component\Utility\UrlHelper::filterBadProtocol() if it will be used
+ *   as an HTML attribute value.
+ *
+ * @see drupal_current_script_url()
+ * @see \Drupal\Component\Utility\UrlHelper::filterBadProtocol()
+ */
+function drupal_requirements_url($severity) {
+  $query = [];
+  // If there are no errors, only warnings, append 'continue=1' to the URL so
+  // the user can bypass this screen on the next page load.
+  if ($severity == REQUIREMENT_WARNING) {
+    $query['continue'] = 1;
+  }
+  return drupal_current_script_url($query);
+}
+
+/**
+ * Checks an installation profile's requirements.
+ *
+ * @param string $profile
+ *   Name of installation profile to check.
+ *
+ * @return array
+ *   Array of the installation profile's requirements.
+ */
+function drupal_check_profile($profile) {
+  $info = install_profile_info($profile);
+  // Collect requirement testing results.
+  $requirements = [];
+  // Performs an ExtensionDiscovery scan as the system module is unavailable and
+  // we don't yet know where all the modules are located.
+  // @todo Remove as part of https://www.drupal.org/node/2186491
+  $drupal_root = \Drupal::root();
+  $module_list = (new ExtensionDiscovery($drupal_root))->scan('module');
+
+  foreach ($info['install'] as $module) {
+    // If the module is in the module list we know it exists and we can continue
+    // including and registering it.
+    // @see \Drupal\Core\Extension\ExtensionDiscovery::scanDirectory()
+    if (isset($module_list[$module])) {
+      $function = $module . '_requirements';
+      $module_path = $module_list[$module]->getPath();
+      $install_file = "$drupal_root/$module_path/$module.install";
+
+      if (is_file($install_file)) {
+        require_once $install_file;
+      }
+
+      \Drupal::service('class_loader')->addPsr4('Drupal\\' . $module . '\\', \Drupal::root() . "/$module_path/src");
+
+      if (function_exists($function)) {
+        $requirements = array_merge($requirements, $function('install'));
+      }
+    }
+  }
+
+  // Add the profile requirements.
+  $function = $profile . '_requirements';
+  if (function_exists($function)) {
+    $requirements = array_merge($requirements, $function('install'));
+  }
+
+  return $requirements;
+}
+
+/**
+ * Extracts the highest severity from the requirements array.
+ *
+ * @param $requirements
+ *   An array of requirements, in the same format as is returned by
+ *   hook_requirements().
+ *
+ * @return
+ *   The highest severity in the array.
+ */
+function drupal_requirements_severity(&$requirements) {
+  $severity = REQUIREMENT_OK;
+  foreach ($requirements as $requirement) {
+    if (isset($requirement['severity'])) {
+      $severity = max($severity, $requirement['severity']);
+    }
+  }
+  return $severity;
+}
+
+/**
+ * Checks a module's requirements.
+ *
+ * @param $module
+ *   Machine name of module to check.
+ *
+ * @return
+ *   TRUE or FALSE, depending on whether the requirements are met.
+ */
+function drupal_check_module($module) {
+  module_load_install($module);
+  // Check requirements
+  $requirements = \Drupal::moduleHandler()->invoke($module, 'requirements', ['install']);
+  if (is_array($requirements) && drupal_requirements_severity($requirements) == REQUIREMENT_ERROR) {
+    // Print any error messages
+    foreach ($requirements as $requirement) {
+      if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
+        $message = $requirement['description'];
+        if (isset($requirement['value']) && $requirement['value']) {
+          $message = t('@requirements_message (Currently using @item version @version)', ['@requirements_message' => $requirement['description'], '@item' => $requirement['title'], '@version' => $requirement['value']]);
+        }
+        \Drupal::messenger()->addError($message);
+      }
+    }
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/**
+ * Retrieves information about an installation profile from its .info.yml file.
+ *
+ * The information stored in a profile .info.yml file is similar to that stored
+ * in a normal Drupal module .info.yml file. For example:
+ * - name: The real name of the installation profile for display purposes.
+ * - description: A brief description of the profile.
+ * - dependencies: An array of shortnames of other modules that this install
+ *   profile requires.
+ * - install: An array of shortname of other modules to install that are not
+ *   required by this install profile.
+ *
+ * Additional, less commonly-used information that can appear in a
+ * profile.info.yml file but not in a normal Drupal module .info.yml file
+ * includes:
+ *
+ * - distribution: Existence of this key denotes that the installation profile
+ *   is intended to be the only eligible choice in a distribution and will be
+ *   auto-selected during installation, whereas the installation profile
+ *   selection screen will be skipped. If more than one distribution profile is
+ *   found then the first one discovered will be selected.
+ *   The following subproperties may be set:
+ *   - name: The name of the distribution that is being installed, to be shown
+ *     throughout the installation process. If omitted,
+ *     drupal_install_profile_distribution_name() defaults to 'Drupal'.
+ *   - install: Optional parameters to override the installer:
+ *     - theme: The machine name of a theme to use in the installer instead of
+ *       Drupal's default installer theme.
+ *     - finish_url: A destination to visit after the installation of the
+ *       distribution is finished
+ *
+ * Note that this function does an expensive file system scan to get info file
+ * information for dependencies. If you only need information from the info
+ * file itself, use
+ * \Drupal::service('extension.list.profile')->getExtensionInfo().
+ *
+ * Example of .info.yml file:
+ * @code
+ *    name: Minimal
+ *    description: Start fresh, with only a few modules enabled.
+ *    install:
+ *      - block
+ *      - dblog
+ * @endcode
+ *
+ * @param $profile
+ *   Name of profile.
+ * @param $langcode
+ *   Language code (if any).
+ *
+ * @return
+ *   The info array.
+ */
+function install_profile_info($profile, $langcode = 'en') {
+  static $cache = [];
+
+  if (!isset($cache[$profile][$langcode])) {
+    // Set defaults for module info.
+    $defaults = [
+      'dependencies' => [],
+      'install' => [],
+      'themes' => ['stark'],
+      'description' => '',
+      'version' => NULL,
+      'hidden' => FALSE,
+      'php' => DRUPAL_MINIMUM_PHP,
+      'config_install_path' => NULL,
+    ];
+    $profile_path = drupal_get_path('profile', $profile);
+    $info = \Drupal::service('info_parser')->parse("$profile_path/$profile.info.yml");
+    $info += $defaults;
+
+    $dependency_name_function = function ($dependency) {
+      return Dependency::createFromString($dependency)->getName();
+    };
+    // Convert dependencies in [project:module] format.
+    $info['dependencies'] = array_map($dependency_name_function, $info['dependencies']);
+
+    // Convert install key in [project:module] format.
+    $info['install'] = array_map($dependency_name_function, $info['install']);
+
+    // drupal_required_modules() includes the current profile as a dependency.
+    // Remove that dependency, since a module cannot depend on itself.
+    $required = array_diff(drupal_required_modules(), [$profile]);
+
+    $locale = !empty($langcode) && $langcode != 'en' ? ['locale'] : [];
+
+    // Merge dependencies, required modules and locale into install list and
+    // remove any duplicates.
+    $info['install'] = array_unique(array_merge($info['install'], $required, $info['dependencies'], $locale));
+
+    // If the profile has a config/sync directory use that to install drupal.
+    if (is_dir($profile_path . '/config/sync')) {
+      $info['config_install_path'] = $profile_path . '/config/sync';
+    }
+    $cache[$profile][$langcode] = $info;
+  }
+  return $cache[$profile][$langcode];
+}
+
+/**
+ * Returns a database installer object.
+ *
+ * Before calling this function it is important the database installer object
+ * is autoloadable. Database drivers provided by contributed modules are added
+ * to the autoloader in drupal_get_database_types() and Settings::initialize().
+ *
+ * @param $driver
+ *   The name of the driver.
+ * @param string $namespace
+ *   (optional) The database driver namespace.
+ *
+ * @return \Drupal\Core\Database\Install\Tasks
+ *   A class defining the requirements and tasks for installing the database.
+ *
+ * @see drupal_get_database_types()
+ * @see \Drupal\Core\Site\Settings::initialize()
+ */
+function db_installer_object($driver, $namespace = NULL) {
+  // We cannot use Database::getConnection->getDriverClass() here, because
+  // the connection object is not yet functional.
+  if ($namespace) {
+    $task_class = $namespace . "\\Install\\Tasks";
+    return new $task_class();
+  }
+  // Old Drupal 8 style contrib namespace.
+  $task_class = "Drupal\\Driver\\Database\\{$driver}\\Install\\Tasks";
+  if (class_exists($task_class)) {
+    return new $task_class();
+  }
+  else {
+    // Core provided driver.
+    $task_class = "Drupal\\Core\\Database\\Driver\\{$driver}\\Install\\Tasks";
+    return new $task_class();
+  }
+}

+ 187 - 0
web/core/includes/menu.inc

@@ -0,0 +1,187 @@
+<?php
+
+/**
+ * @file
+ * API for the Drupal menu system.
+ */
+
+/**
+ * @addtogroup menu
+ * @{
+ */
+
+use Drupal\Component\Render\FormattableMarkup;
+use Drupal\Core\Render\Element;
+
+/**
+ * Prepares variables for single local task link templates.
+ *
+ * Default template: menu-local-task.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: A render element containing:
+ *     - #link: A menu link array with 'title', 'url', and (optionally)
+ *       'localized_options' keys.
+ *     - #active: A boolean indicating whether the local task is active.
+ */
+function template_preprocess_menu_local_task(&$variables) {
+  $link = $variables['element']['#link'];
+  $link += [
+    'localized_options' => [],
+  ];
+  $link_text = $link['title'];
+
+  if (!empty($variables['element']['#active'])) {
+    $variables['is_active'] = TRUE;
+
+    // Add text to indicate active tab for non-visual users.
+    $active = new FormattableMarkup('<span class="visually-hidden">@label</span>', ['@label' => t('(active tab)')]);
+    $link_text = t('@local-task-title@active', ['@local-task-title' => $link_text, '@active' => $active]);
+  }
+
+  $link['localized_options']['set_active_class'] = TRUE;
+
+  $variables['link'] = [
+    '#type' => 'link',
+    '#title' => $link_text,
+    '#url' => $link['url'],
+    '#options' => $link['localized_options'],
+  ];
+}
+
+/**
+ * Prepares variables for single local action link templates.
+ *
+ * Default template: menu-local-action.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: A render element containing:
+ *     - #link: A menu link array with 'title', 'url', and (optionally)
+ *       'localized_options' keys.
+ */
+function template_preprocess_menu_local_action(&$variables) {
+  $link = $variables['element']['#link'];
+  $link += [
+    'localized_options' => [],
+  ];
+  $link['localized_options']['attributes']['class'][] = 'button';
+  $link['localized_options']['attributes']['class'][] = 'button-action';
+  $link['localized_options']['set_active_class'] = TRUE;
+
+  $variables['link'] = [
+    '#type' => 'link',
+    '#title' => $link['title'],
+    '#options' => $link['localized_options'],
+    '#url' => $link['url'],
+  ];
+}
+
+/**
+ * Returns an array containing the names of system-defined (default) menus.
+ */
+function menu_list_system_menus() {
+  return [
+    'tools' => 'Tools',
+    'admin' => 'Administration',
+    'account' => 'User account menu',
+    'main' => 'Main navigation',
+    'footer' => 'Footer menu',
+  ];
+}
+
+/**
+ * Collects the local tasks (tabs) for the current route.
+ *
+ * @param int $level
+ *   The level of tasks you ask for. Primary tasks are 0, secondary are 1.
+ *
+ * @return array
+ *   An array containing
+ *   - tabs: Local tasks for the requested level.
+ *   - route_name: The route name for the current page used to collect the local
+ *     tasks.
+ *
+ * @see hook_menu_local_tasks_alter()
+ * @see https://www.drupal.org/node/2544940
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+ */
+function menu_local_tasks($level = 0) {
+  /** @var \Drupal\Core\Menu\LocalTaskManagerInterface $manager */
+  $manager = \Drupal::service('plugin.manager.menu.local_task');
+  return $manager->getLocalTasks(\Drupal::routeMatch()->getRouteName(), $level);
+}
+
+/**
+ * Returns the rendered local tasks at the top level.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Menu\LocalTaskManagerInterface::getLocalTasks() instead.
+ *
+ * @see https://www.drupal.org/node/2874695
+ */
+function menu_primary_local_tasks() {
+  @trigger_error(__FUNCTION__ . '() is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Menu\LocalTaskManagerInterface::getLocalTasks() instead. See https://www.drupal.org/node/2874695', E_USER_DEPRECATED);
+  /** @var \Drupal\Core\Menu\LocalTaskManagerInterface $manager */
+  $manager = \Drupal::service('plugin.manager.menu.local_task');
+  $links = $manager->getLocalTasks(\Drupal::routeMatch()->getRouteName(), 0);
+  // Do not display single tabs.
+  return count(Element::getVisibleChildren($links['tabs'])) > 1 ? $links['tabs'] : '';
+}
+
+/**
+ * Returns the rendered local tasks at the second level.
+ *
+ * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Menu\LocalTaskManagerInterface::getLocalTasks() instead.
+ *
+ * @see https://www.drupal.org/node/2874695
+ */
+function menu_secondary_local_tasks() {
+  @trigger_error(__FUNCTION__ . '() is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Menu\LocalTaskManagerInterface::getLocalTasks() instead. See https://www.drupal.org/node/2874695', E_USER_DEPRECATED);
+  /** @var \Drupal\Core\Menu\LocalTaskManagerInterface $manager */
+  $manager = \Drupal::service('plugin.manager.menu.local_task');
+  $links = $manager->getLocalTasks(\Drupal::routeMatch()->getRouteName(), 1);
+  // Do not display single tabs.
+  return count(Element::getVisibleChildren($links['tabs'])) > 1 ? $links['tabs'] : '';
+}
+
+/**
+ * Returns a renderable element for the primary and secondary tabs.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   local_tasks_block block or inline theming instead.
+ *
+ * @see https://www.drupal.org/node/2874695
+ */
+function menu_local_tabs() {
+  @trigger_error(__FUNCTION__ . '() is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use local_tasks_block block or inline theming instead. See https://www.drupal.org/node/2874695', E_USER_DEPRECATED);
+  $build = [
+    '#theme' => 'menu_local_tasks',
+    '#primary' => menu_primary_local_tasks(),
+    '#secondary' => menu_secondary_local_tasks(),
+  ];
+  return !empty($build['#primary']) || !empty($build['#secondary']) ? $build : [];
+}
+
+/**
+ * Clears all cached menu data.
+ *
+ * This should be called any time broad changes
+ * might have been made to the router items or menu links.
+ *
+ * @deprecated in drupal:8.6.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal::cache('menu')->invalidateAll() instead.
+ *
+ * @see https://www.drupal.org/node/2989138
+ */
+function menu_cache_clear_all() {
+  @trigger_error("menu_cache_clear_all() is deprecated in Drupal 8.6.0 and will be removed before Drupal 9.0.0. Use \Drupal::cache('menu')->invalidateAll() instead. See https://www.drupal.org/node/2989138", E_USER_DEPRECATED);
+  \Drupal::cache('menu')->invalidateAll();
+}
+
+/**
+ * @} End of "addtogroup menu".
+ */

+ 240 - 0
web/core/includes/module.inc

@@ -0,0 +1,240 @@
+<?php
+
+/**
+ * @file
+ * API for loading and interacting with Drupal modules.
+ */
+
+use Drupal\Core\Extension\ExtensionDiscovery;
+
+/**
+ * Builds a list of installed themes.
+ *
+ * @param $type
+ *   The type of list to return:
+ *   - theme: All installed themes.
+ *
+ * @return array
+ *   An associative array of themes, keyed by name.
+ *   For $type 'theme', the array values are objects representing the
+ *   respective database row, with the 'info' property already unserialized.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal::service('theme_handler')->listInfo() instead.
+ *
+ * @see https://www.drupal.org/node/2709919
+ * @see \Drupal\Core\Extension\ThemeHandler::listInfo()
+ */
+function system_list($type) {
+  @trigger_error('system_list() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal::service(\'theme_handler\')->listInfo() instead. See https://www.drupal.org/node/2709919', E_USER_DEPRECATED);
+
+  $lists = [
+    'theme' => \Drupal::service('theme_handler')->listInfo(),
+    'filepaths' => [],
+  ];
+  foreach ($lists['theme'] as $name => $theme) {
+    $lists['filepaths'][] = [
+      'type' => 'theme',
+      'name' => $name,
+      'filepath' => $theme->getPathname(),
+    ];
+  }
+  return $lists[$type];
+}
+
+/**
+ * Resets all system_list() caches.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. There
+ *   is no direct replacement. Call each
+ *   \Drupal::service('extension.list.TYPE')->reset() as necessary.
+ *
+ * @see https://www.drupal.org/node/2709919
+ */
+function system_list_reset() {
+  @trigger_error("system_list_reset() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. There is no direct replacement. Call each \Drupal::service('extension.list.TYPE')->reset() as necessary. See https://www.drupal.org/node/2709919.", E_USER_DEPRECATED);
+  \Drupal::service('extension.list.profile')->reset();
+  \Drupal::service('extension.list.module')->reset();
+  \Drupal::service('extension.list.theme_engine')->reset();
+  \Drupal::service('extension.list.theme')->reset();
+}
+
+/**
+ * Registers an extension in runtime registries for execution.
+ *
+ * @param string $type
+ *   The extension type; e.g., 'module' or 'theme'.
+ * @param string $name
+ *   The internal name of the extension; e.g., 'node'.
+ * @param string $uri
+ *   The relative URI of the primary extension file; e.g.,
+ *   'core/modules/node/node.module'.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. There is
+ *   no replacement for this function. Use the following sequence of code to
+ *   achieve the same functionality:
+ *   @code
+ *   $path = \Drupal::service("extension.list.$type")->getPath($name);
+ *   \Drupal::service('class_loader')->addPsr4('Drupal\\' . $name . '\\', \Drupal::root() . '/' . $path . '/src');
+ *   @endcode
+ */
+function system_register($type, $name, $uri) {
+  @trigger_error('system_register() is deprecated in Drupal 8.8.0 and will be removed before Drupal 9.0.0. There is no replacement for this function. To achieve the same functionality use this snippet: $path = \Drupal::service("extension.list.$type")->getPath($name); ' . "\\Drupal::service('class_loader')->addPsr4('Drupal\\\\' . \$name . '\\\\', \\Drupal::root() . '/' . \$path . '/src'); See https://www.drupal.org/node/3035275.", E_USER_DEPRECATED);
+  \Drupal::service('class_loader')->addPsr4('Drupal\\' . $name . '\\', \Drupal::root() . '/' . dirname($uri) . '/src');
+}
+
+/**
+ * Loads a module's installation hooks.
+ *
+ * @param $module
+ *   The name of the module (without the .module extension).
+ *
+ * @return
+ *   The name of the module's install file, if successful; FALSE otherwise.
+ */
+function module_load_install($module) {
+  // Make sure the installation API is available
+  include_once __DIR__ . '/install.inc';
+
+  return module_load_include('install', $module);
+}
+
+/**
+ * Loads a module include file.
+ *
+ * Examples:
+ * @code
+ *   // Load node.admin.inc from the node module.
+ *   module_load_include('inc', 'node', 'node.admin');
+ *   // Load content_types.inc from the node module.
+ *   module_load_include('inc', 'node', 'content_types');
+ * @endcode
+ *
+ * Do not use this function to load an install file, use module_load_install()
+ * instead. Do not use this function in a global context since it requires
+ * Drupal to be fully bootstrapped, use require_once DRUPAL_ROOT . '/path/file'
+ * instead.
+ *
+ * @param $type
+ *   The include file's type (file extension).
+ * @param $module
+ *   The module to which the include file belongs.
+ * @param $name
+ *   (optional) The base file name (without the $type extension). If omitted,
+ *   $module is used; i.e., resulting in "$module.$type" by default.
+ *
+ * @return
+ *   The name of the included file, if successful; FALSE otherwise.
+ *
+ * @todo The module_handler service has a loadInclude() method which performs
+ *   this same task but only for enabled modules. Figure out a way to move this
+ *   functionality entirely into the module_handler while keeping the ability to
+ *   load the files of disabled modules.
+ */
+function module_load_include($type, $module, $name = NULL) {
+  if (!isset($name)) {
+    $name = $module;
+  }
+
+  if (function_exists('drupal_get_path')) {
+    $file = DRUPAL_ROOT . '/' . drupal_get_path('module', $module) . "/$name.$type";
+    if (is_file($file)) {
+      require_once $file;
+      return $file;
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Returns an array of modules required by core.
+ */
+function drupal_required_modules() {
+  $listing = new ExtensionDiscovery(\Drupal::root());
+  $files = $listing->scan('module');
+  $required = [];
+
+  // Unless called by the installer, an installation profile is required and
+  // must always be loaded.
+  if ($profile = \Drupal::installProfile()) {
+    $required[] = $profile;
+  }
+
+  foreach ($files as $name => $file) {
+    $info = \Drupal::service('info_parser')->parse($file->getPathname());
+    if (!empty($info) && !empty($info['required']) && $info['required']) {
+      $required[] = $name;
+    }
+  }
+
+  return $required;
+}
+
+/**
+ * Sets weight of a particular module.
+ *
+ * The weight of uninstalled modules cannot be changed.
+ *
+ * @param string $module
+ *   The name of the module (without the .module extension).
+ * @param int $weight
+ *   An integer representing the weight of the module.
+ */
+function module_set_weight($module, $weight) {
+  $extension_config = \Drupal::configFactory()->getEditable('core.extension');
+  if ($extension_config->get("module.$module") !== NULL) {
+    // Pre-cast the $weight to an integer so that we can save this without using
+    // schema. This is a performance improvement for module installation.
+    $extension_config
+      ->set("module.$module", (int) $weight)
+      ->set('module', module_config_sort($extension_config->get('module')))
+      ->save(TRUE);
+
+    // Prepare the new module list, sorted by weight, including filenames.
+    // @see \Drupal\Core\Extension\ModuleInstaller::install()
+    $module_handler = \Drupal::moduleHandler();
+    $current_module_filenames = $module_handler->getModuleList();
+    $current_modules = array_fill_keys(array_keys($current_module_filenames), 0);
+    $current_modules = module_config_sort(array_merge($current_modules, $extension_config->get('module')));
+    $module_filenames = [];
+    foreach ($current_modules as $name => $weight) {
+      $module_filenames[$name] = $current_module_filenames[$name];
+    }
+    // Update the module list in the extension handler.
+    $module_handler->setModuleList($module_filenames);
+    return;
+  }
+}
+
+/**
+ * Sorts the configured list of enabled modules.
+ *
+ * The list of enabled modules is expected to be ordered by weight and name.
+ * The list is always sorted on write to avoid the overhead on read.
+ *
+ * @param array $data
+ *   An array of module configuration data.
+ *
+ * @return array
+ *   An array of module configuration data sorted by weight and name.
+ */
+function module_config_sort($data) {
+  // PHP array sorting functions such as uasort() do not work with both keys and
+  // values at the same time, so we achieve weight and name sorting by computing
+  // strings with both information concatenated (weight first, name second) and
+  // use that as a regular string sort reference list via array_multisort(),
+  // compound of "[sign-as-integer][padded-integer-weight][name]"; e.g., given
+  // two modules and weights (spaces added for clarity):
+  // - Block with weight -5: 0 0000000000000000005 block
+  // - Node  with weight  0: 1 0000000000000000000 node
+  $sort = [];
+  foreach ($data as $name => $weight) {
+    // Prefix negative weights with 0, positive weights with 1.
+    // +/- signs cannot be used, since + (ASCII 43) is before - (ASCII 45).
+    $prefix = (int) ($weight >= 0);
+    // The maximum weight is PHP_INT_MAX, so pad all weights to 19 digits.
+    $sort[] = $prefix . sprintf('%019d', abs($weight)) . $name;
+  }
+  array_multisort($sort, SORT_STRING, $data);
+  return $data;
+}

+ 348 - 0
web/core/includes/pager.inc

@@ -0,0 +1,348 @@
+<?php
+
+/**
+ * @file
+ * Functions to aid in presenting database results as a set of pages.
+ */
+
+use Drupal\Core\Template\Attribute;
+use Drupal\Core\Url;
+use Drupal\Component\Utility\Html;
+
+/**
+ * Returns the current page being requested for display within a pager.
+ *
+ * @param int $element
+ *   (optional) An integer to distinguish between multiple pagers on one page.
+ *
+ * @return int
+ *   The number of the current requested page, within the pager represented by
+ *   $element. This is determined from the URL query parameter
+ *   \Drupal::request()->query->get('page'), or 0 by default. Note that this
+ *   number may differ from the actual page being displayed. For example, if a
+ *   search for "example text" brings up three pages of results, but a user
+ *   visits search/node/example+text?page=10, this function will return 10,
+ *   even though the default pager implementation adjusts for this and still
+ *   displays the third page of search results at that URL.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Pager\RequestPagerInterface->findPage() instead.
+ *
+ * @see https://www.drupal.org/node/2779457
+ * @see \Drupal\Core\Pager\PagerParametersInterface::findPage()
+ */
+function pager_find_page($element = 0) {
+  @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Pager\RequestPagerInterface->findPage() instead. See https://www.drupal.org/node/2779457', E_USER_DEPRECATED);
+  /* @var $pager_parameters \Drupal\Core\Pager\PagerParametersInterface */
+  $pager_parameters = \Drupal::service('pager.parameters');
+  return $pager_parameters->findPage($element);
+}
+
+/**
+ * Initializes a pager.
+ *
+ * This function sets up the necessary global variables so that the render
+ * system will correctly process #type 'pager' render arrays to output pagers
+ * that correspond to the items being displayed.
+ *
+ * If the items being displayed result from a database query performed using
+ * Drupal's database API, and if you have control over the construction of the
+ * database query, you do not need to call this function directly; instead, you
+ * can simply extend the query object with the 'PagerSelectExtender' extender
+ * before executing it. For example:
+ * @code
+ *   $query = \Drupal::database()->select('some_table')
+ *     ->extend('Drupal\Core\Database\Query\PagerSelectExtender');
+ * @endcode
+ *
+ * However, if you are using a different method for generating the items to be
+ * paged through, then you should call this function in preparation.
+ *
+ * The following example shows how this function can be used in a controller
+ * that invokes an external datastore with an SQL-like syntax:
+ * @code
+ *   // First find the total number of items and initialize the pager.
+ *   $where = "status = 1";
+ *   $total = mymodule_select("SELECT COUNT(*) FROM data " . $where)->result();
+ *   $num_per_page = \Drupal::config('mymodule.settings')->get('num_per_page');
+ *   $page = pager_default_initialize($total, $num_per_page);
+ *
+ *   // Next, retrieve the items for the current page and put them into a
+ *   // render array.
+ *   $offset = $num_per_page * $page;
+ *   $result = mymodule_select("SELECT * FROM data " . $where . " LIMIT %d, %d", $offset, $num_per_page)->fetchAll();
+ *   $render = [];
+ *   $render[] = [
+ *     '#theme' => 'mymodule_results',
+ *     '#result' => $result,
+ *   ];
+ *
+ *   // Finally, add the pager to the render array, and return.
+ *   $render[] = ['#type' => 'pager'];
+ *   return $render;
+ * @endcode
+ *
+ * A second example involves a controller that invokes an external search
+ * service where the total number of matching results is provided as part of
+ * the returned set (so that we do not need a separate query in order to obtain
+ * this information). Here, we call pager_find_page() to calculate the desired
+ * offset before the search is invoked:
+ * @code
+ *   // Perform the query, using the requested offset from pager_find_page().
+ *   // This comes from a URL parameter, so here we are assuming that the URL
+ *   // parameter corresponds to an actual page of results that will exist
+ *   // within the set.
+ *   $page = pager_find_page();
+ *   $num_per_page = \Drupal::config('mymodule.settings')->get('num_per_page');
+ *   $offset = $num_per_page * $page;
+ *   $result = mymodule_remote_search($keywords, $offset, $num_per_page);
+ *
+ *   // Now that we have the total number of results, initialize the pager.
+ *   pager_default_initialize($result->total, $num_per_page);
+ *
+ *   // Create a render array with the search results.
+ *   $render = [];
+ *   $render[] = [
+ *     '#theme' => 'search_results',
+ *     '#results' => $result->data,
+ *     '#type' => 'remote',
+ *   ];
+ *
+ *   // Finally, add the pager to the render array, and return.
+ *   $render[] = ['#type' => 'pager'];
+ *   return $render;
+ * @endcode
+ *
+ * @param int $total
+ *   The total number of items to be paged.
+ * @param int $limit
+ *   The number of items the calling code will display per page.
+ * @param int $element
+ *   (optional) An integer to distinguish between multiple pagers on one page.
+ *
+ * @return int
+ *   The number of the current page, within the pager represented by $element.
+ *   This is determined from the URL query parameter
+ *   \Drupal::request()->query->get('page), or 0 by default. However, if a page
+ *   that does not correspond to the actual range of the result set was
+ *   requested, this function will return the closest page actually within the
+ *   result set.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Pager\PagerManagerInterface->defaultInitialize() instead.
+ *
+ * @see https://www.drupal.org/node/2779457
+ * @see \Drupal\Core\Pager\PagerManagerInterface::createPager()
+ */
+function pager_default_initialize($total, $limit, $element = 0) {
+  @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface->createPager() instead. See https://www.drupal.org/node/2779457', E_USER_DEPRECATED);
+  /* @var $pager_manager \Drupal\Core\Pager\PagerManagerInterface */
+  $pager_manager = \Drupal::service('pager.manager');
+  $pager = $pager_manager->createPager($total, $limit, $element);
+  return $pager->getCurrentPage();
+}
+
+/**
+ * Compose a URL query parameter array for pager links.
+ *
+ * @return array
+ *   A URL query parameter array that consists of all components of the current
+ *   page request except for those pertaining to paging.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Pager\RequestPagerInterface->getQueryParameters() instead.
+ *
+ * @see https://www.drupal.org/node/2779457
+ * @see \Drupal\Core\Pager\PagerParametersInterface::getQueryParameters()
+ */
+function pager_get_query_parameters() {
+  @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Pager\RequestPagerInterface->getQueryParameters() instead. See https://www.drupal.org/node/2779457', E_USER_DEPRECATED);
+  /* @var $pager_params \Drupal\Core\Pager\PagerParametersInterface */
+  $pager_params = \Drupal::service('pager.parameters');
+  return $pager_params->getQueryParameters();
+}
+
+/**
+ * Prepares variables for pager templates.
+ *
+ * Default template: pager.html.twig.
+ *
+ * Menu callbacks that display paged query results should use #type => pager
+ * to retrieve a pager control so that users can view other results. Format a
+ * list of nearby pages with additional query results.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - pager: A render element containing:
+ *     - #tags: An array of labels for the controls in the pager.
+ *     - #element: An optional integer to distinguish between multiple pagers on
+ *       one page.
+ *     - #parameters: An associative array of query string parameters to append
+ *       to the pager links.
+ *     - #route_parameters: An associative array of the route parameters.
+ *     - #quantity: The number of pages in the list.
+ */
+function template_preprocess_pager(&$variables) {
+  $element = $variables['pager']['#element'];
+  $parameters = $variables['pager']['#parameters'];
+  $quantity = empty($variables['pager']['#quantity']) ? 0 : $variables['pager']['#quantity'];
+  $route_name = $variables['pager']['#route_name'];
+  $route_parameters = isset($variables['pager']['#route_parameters']) ? $variables['pager']['#route_parameters'] : [];
+
+  /* @var $pager_manager \Drupal\Core\Pager\PagerManagerInterface */
+  $pager_manager = \Drupal::service('pager.manager');
+
+  $pager = $pager_manager->getPager($element);
+
+  // Nothing to do if there is no pager.
+  if (!isset($pager)) {
+    return;
+  }
+
+  $pager_max = $pager->getTotalPages();
+
+  // Nothing to do if there is only one page.
+  if ($pager_max <= 1) {
+    return;
+  }
+
+  $tags = $variables['pager']['#tags'];
+
+  // Calculate various markers within this pager piece:
+  // Middle is used to "center" pages around the current page.
+  $pager_middle = ceil($quantity / 2);
+  $current_page = $pager->getCurrentPage();
+  // The current pager is the page we are currently paged to.
+  $pager_current = $current_page + 1;
+  // The first pager is the first page listed by this pager piece (re quantity).
+  $pager_first = $pager_current - $pager_middle + 1;
+  // The last is the last page listed by this pager piece (re quantity).
+  $pager_last = $pager_current + $quantity - $pager_middle;
+  // End of marker calculations.
+
+  // Prepare for generation loop.
+  $i = $pager_first;
+  if ($pager_last > $pager_max) {
+    // Adjust "center" if at end of query.
+    $i = $i + ($pager_max - $pager_last);
+    $pager_last = $pager_max;
+  }
+  if ($i <= 0) {
+    // Adjust "center" if at start of query.
+    $pager_last = $pager_last + (1 - $i);
+    $i = 1;
+  }
+  // End of generation loop preparation.
+
+  // Create the "first" and "previous" links if we are not on the first page.
+  if ($current_page > 0) {
+    $items['first'] = [];
+    $items['first']['attributes'] = new Attribute();
+    $options = [
+      'query' => $pager_manager->getUpdatedParameters($parameters, $element, 0),
+    ];
+    $items['first']['href'] = Url::fromRoute($route_name, $route_parameters, $options)->toString();
+    if (isset($tags[0])) {
+      $items['first']['text'] = $tags[0];
+    }
+
+    $items['previous'] = [];
+    $items['previous']['attributes'] = new Attribute();
+    $options = [
+      'query' => $pager_manager->getUpdatedParameters($parameters, $element, $current_page - 1),
+    ];
+    $items['previous']['href'] = Url::fromRoute($route_name, $route_parameters, $options)->toString();
+    if (isset($tags[1])) {
+      $items['previous']['text'] = $tags[1];
+    }
+  }
+
+  if ($i != $pager_max) {
+    // Add an ellipsis if there are further previous pages.
+    if ($i > 1) {
+      $variables['ellipses']['previous'] = TRUE;
+    }
+    // Now generate the actual pager piece.
+    for (; $i <= $pager_last && $i <= $pager_max; $i++) {
+      $options = [
+        'query' => $pager_manager->getUpdatedParameters($parameters, $element, $i - 1),
+      ];
+      $items['pages'][$i]['href'] = Url::fromRoute($route_name, $route_parameters, $options)->toString();
+      $items['pages'][$i]['attributes'] = new Attribute();
+      if ($i == $pager_current) {
+        $variables['current'] = $i;
+      }
+    }
+    // Add an ellipsis if there are further next pages.
+    if ($i < $pager_max + 1) {
+      $variables['ellipses']['next'] = TRUE;
+    }
+  }
+
+  // Create the "next" and "last" links if we are not on the last page.
+  if ($current_page < ($pager_max - 1)) {
+    $items['next'] = [];
+    $items['next']['attributes'] = new Attribute();
+    $options = [
+      'query' => $pager_manager->getUpdatedParameters($parameters, $element, $current_page + 1),
+    ];
+    $items['next']['href'] = Url::fromRoute($route_name, $route_parameters, $options)->toString();
+    if (isset($tags[3])) {
+      $items['next']['text'] = $tags[3];
+    }
+
+    $items['last'] = [];
+    $items['last']['attributes'] = new Attribute();
+    $options = [
+      'query' => $pager_manager->getUpdatedParameters($parameters, $element, $pager_max - 1),
+    ];
+    $items['last']['href'] = Url::fromRoute($route_name, $route_parameters, $options)->toString();
+    if (isset($tags[4])) {
+      $items['last']['text'] = $tags[4];
+    }
+  }
+
+  $variables['items'] = $items;
+  $variables['heading_id'] = Html::getUniqueId('pagination-heading');
+
+  // The rendered link needs to play well with any other query parameter used
+  // on the page, like exposed filters, so for the cacheability all query
+  // parameters matter.
+  $variables['#cache']['contexts'][] = 'url.query_args';
+}
+
+/**
+ * Gets the URL query parameter array of a pager link.
+ *
+ * Adds to or adjusts the 'page' URL query parameter so that if you follow the
+ * link, you'll get page $index for pager $element on the page.
+ *
+ * The 'page' URL query parameter is a comma-delimited string, where each value
+ * is the target content page for the corresponding pager $element. For
+ * instance, if we have 5 pagers on a single page, and we want to have a link
+ * to a page that should display the 6th content page for the 3rd pager, and
+ * the 1st content page for all the other pagers, then the URL query will look
+ * like this: ?page=0,0,5,0,0 (page numbering starts at zero).
+ *
+ * @param array $query
+ *   An associative array of URL query parameters to add to.
+ * @param int $element
+ *   An integer to distinguish between multiple pagers on one page.
+ * @param int $index
+ *   The index of the target page, for the given element, in the pager array.
+ *
+ * @return array
+ *   The altered $query parameter array.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Pager\PagerManagerInterface::getUpdatedParameters() instead.
+ *
+ * @see https://www.drupal.org/node/2779457
+ * @see \Drupal\Core\Pager\PagerManagerInterface::getUpdatedParameters()
+ */
+function pager_query_add_page(array $query, $element, $index) {
+  @trigger_error(__FUNCTION__ . ' is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Pager\PagerManagerInterface->getUpdatedParameters() instead. See https://www.drupal.org/node/2779457', E_USER_DEPRECATED);
+  /* @var $pager_manager \Drupal\Core\Pager\PagerManagerInterface */
+  $pager_manager = \Drupal::service('pager.manager');
+  return $pager_manager->getUpdatedParameters($query, $element, $index);
+}

+ 233 - 0
web/core/includes/schema.inc

@@ -0,0 +1,233 @@
+<?php
+
+/**
+ * @file
+ * Schema API handling functions.
+ */
+
+use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema;
+
+/**
+ * @addtogroup schemaapi
+ * @{
+ */
+
+/**
+ * Indicates that a module has not been installed yet.
+ */
+const SCHEMA_UNINSTALLED = -1;
+
+/**
+ * Returns an array of available schema versions for a module.
+ *
+ * @param string $module
+ *   A module name.
+ *
+ * @return array|bool
+ *   If the module has updates, an array of available updates sorted by
+ *   version. Otherwise, FALSE.
+ */
+function drupal_get_schema_versions($module) {
+  $updates = &drupal_static(__FUNCTION__, NULL);
+  if (!isset($updates[$module])) {
+    $updates = [];
+    foreach (\Drupal::moduleHandler()->getModuleList() as $loaded_module => $filename) {
+      $updates[$loaded_module] = [];
+    }
+
+    // Prepare regular expression to match all possible defined hook_update_N().
+    $regexp = '/^(?<module>.+)_update_(?<version>\d+)$/';
+    $functions = get_defined_functions();
+    // Narrow this down to functions ending with an integer, since all
+    // hook_update_N() functions end this way, and there are other
+    // possible functions which match '_update_'. We use preg_grep() here
+    // instead of foreaching through all defined functions, since the loop
+    // through all PHP functions can take significant page execution time
+    // and this function is called on every administrative page via
+    // system_requirements().
+    foreach (preg_grep('/_\d+$/', $functions['user']) as $function) {
+      // If this function is a module update function, add it to the list of
+      // module updates.
+      if (preg_match($regexp, $function, $matches)) {
+        $updates[$matches['module']][] = $matches['version'];
+      }
+    }
+    // Ensure that updates are applied in numerical order.
+    foreach ($updates as &$module_updates) {
+      sort($module_updates, SORT_NUMERIC);
+    }
+  }
+  return empty($updates[$module]) ? FALSE : $updates[$module];
+}
+
+/**
+ * Returns the currently installed schema version for a module.
+ *
+ * @param string $module
+ *   A module name.
+ * @param bool $reset
+ *   Set to TRUE after installing or uninstalling an extension.
+ * @param bool $array
+ *   Set to TRUE if you want to get information about all modules in the
+ *   system.
+ *
+ * @return string|int
+ *   The currently installed schema version, or SCHEMA_UNINSTALLED if the
+ *   module is not installed.
+ */
+function drupal_get_installed_schema_version($module, $reset = FALSE, $array = FALSE) {
+  $versions = &drupal_static(__FUNCTION__, []);
+
+  if ($reset) {
+    $versions = [];
+  }
+
+  if (!$versions) {
+    if (!$versions = \Drupal::keyValue('system.schema')->getAll()) {
+      $versions = [];
+    }
+  }
+
+  if ($array) {
+    return $versions;
+  }
+  else {
+    return isset($versions[$module]) ? $versions[$module] : SCHEMA_UNINSTALLED;
+  }
+}
+
+/**
+ * Updates the installed version information for a module.
+ *
+ * @param string $module
+ *   A module name.
+ * @param string $version
+ *   The new schema version.
+ */
+function drupal_set_installed_schema_version($module, $version) {
+  \Drupal::keyValue('system.schema')->set($module, $version);
+  // Reset the static cache of module schema versions.
+  drupal_get_installed_schema_version(NULL, TRUE);
+}
+
+/**
+ * Creates all tables defined in a module's hook_schema().
+ *
+ * @param string $module
+ *   The module for which the tables will be created.
+ */
+function drupal_install_schema($module) {
+  $schema = drupal_get_module_schema($module);
+  _drupal_schema_initialize($schema, $module, FALSE);
+
+  foreach ($schema as $name => $table) {
+    \Drupal::database()->schema()->createTable($name, $table);
+  }
+}
+
+/**
+ * Removes all tables defined in a module's hook_schema().
+ *
+ * @param string $module
+ *   The module for which the tables will be removed.
+ */
+function drupal_uninstall_schema($module) {
+  $tables = drupal_get_module_schema($module);
+  _drupal_schema_initialize($tables, $module, FALSE);
+  $schema = \Drupal::database()->schema();
+  foreach ($tables as $table) {
+    if ($schema->tableExists($table['name'])) {
+      $schema->dropTable($table['name']);
+    }
+  }
+}
+
+/**
+ * Returns a module's schema.
+ *
+ * This function can be used to retrieve a schema specification in
+ * hook_schema(), so it allows you to derive your tables from existing
+ * specifications.
+ *
+ * @param string $module
+ *   The module to which the table belongs.
+ * @param string $table
+ *   The name of the table. If not given, the module's complete schema
+ *   is returned.
+ */
+function drupal_get_module_schema($module, $table = NULL) {
+  // Load the .install file to get hook_schema.
+  module_load_install($module);
+  $schema = \Drupal::moduleHandler()->invoke($module, 'schema');
+
+  if (isset($table)) {
+    if (isset($schema[$table])) {
+      return $schema[$table];
+    }
+    return [];
+  }
+  elseif (!empty($schema)) {
+    return $schema;
+  }
+  return [];
+}
+
+/**
+ * Fills in required default values for table definitions from hook_schema().
+ *
+ * @param array $schema
+ *   The schema definition array as it was returned by the module's
+ *   hook_schema().
+ * @param string $module
+ *   The module for which hook_schema() was invoked.
+ * @param bool $remove_descriptions
+ *   (optional) Whether to additionally remove 'description' keys of all tables
+ *   and fields to improve performance of serialize() and unserialize().
+ *   Defaults to TRUE.
+ */
+function _drupal_schema_initialize(&$schema, $module, $remove_descriptions = TRUE) {
+  // Set the name and module key for all tables.
+  foreach ($schema as $name => &$table) {
+    if (empty($table['module'])) {
+      $table['module'] = $module;
+    }
+    if (!isset($table['name'])) {
+      $table['name'] = $name;
+    }
+    if ($remove_descriptions) {
+      unset($table['description']);
+      foreach ($table['fields'] as &$field) {
+        unset($field['description']);
+      }
+    }
+  }
+}
+
+/**
+ * Typecasts values to proper data types.
+ *
+ * MySQL PDO silently casts, e.g. FALSE and '' to 0, when inserting the value
+ * into an integer column, but PostgreSQL PDO does not. Look up the schema
+ * information and use that to correctly typecast the value.
+ *
+ * @param array $info
+ *   An array describing the schema field info.
+ * @param mixed $value
+ *   The value to be converted.
+ *
+ * @return mixed
+ *   The converted value.
+ *
+ * @deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::castValue() instead.
+ *
+ * @see https://www.drupal.org/node/3051983
+ */
+function drupal_schema_get_field_value(array $info, $value) {
+  @trigger_error('drupal_schema_get_field_value() is deprecated in drupal:8.8.0. It will be removed from drupal:9.0.0. Use \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::castValue($info, $value) instead. See https://www.drupal.org/node/3051983', E_USER_DEPRECATED);
+  return SqlContentEntityStorageSchema::castValue($info, $value);
+}
+
+/**
+ * @} End of "addtogroup schemaapi".
+ */

+ 112 - 0
web/core/includes/tablesort.inc

@@ -0,0 +1,112 @@
+<?php
+
+/**
+ * @file
+ * Functions to aid in the creation of sortable tables.
+ *
+ * All tables created when rendering a '#type' => 'table' have the option of
+ * having column headers that the user can click on to sort the table by that
+ * column.
+ */
+
+use Drupal\Core\Utility\TableSort;
+
+/**
+ * Initializes the table sort context.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Utility\TableSort::getContextFromRequest() instead.
+ *
+ * @see \Drupal\Core\Utility\TableSortInterface::getContextFromRequest()
+ * @see https://www.drupal.org/node/3009182
+ */
+function tablesort_init($header) {
+  @trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\Utility\TableSort::getContextFromRequest() instead. See https://www.drupal.org/node/3009182', E_USER_DEPRECATED);
+  return TableSort::getContextFromRequest($header, \Drupal::request());
+}
+
+/**
+ * Formats a column header.
+ *
+ * If the cell in question is the column header for the current sort criterion,
+ * it gets special formatting. All possible sort criteria become links.
+ *
+ * @param string $cell_content
+ *   The cell content to format. Passed by reference.
+ * @param array $cell_attributes
+ *   The cell attributes. Passed by reference.
+ * @param array $header
+ *   An array of column headers in the format described in '#type' => 'table'.
+ * @param array $ts
+ *   The current table sort context as returned from tablesort_init().
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Utility\TableSort::header() instead.
+ *
+ * @see \Drupal\Core\Utility\TableSortInterface::header()
+ * @see https://www.drupal.org/node/3009182
+ */
+function tablesort_header(&$cell_content, array &$cell_attributes, array $header, array $ts) {
+  @trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\Utility\TableSort::header() instead. See https://www.drupal.org/node/3009182', E_USER_DEPRECATED);
+  TableSort::header($cell_content, $cell_attributes, $header, $ts);
+}
+
+/**
+ * Composes a URL query parameter array for table sorting links.
+ *
+ * @return
+ *   A URL query parameter array that consists of all components of the current
+ *   page request except for those pertaining to table sorting.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Utility\TableSort::getQueryParameters() instead.
+ *
+ * @see \Drupal\Core\Utility\TableSort::getQueryParameters()
+ * @see https://www.drupal.org/node/3009182
+ */
+function tablesort_get_query_parameters() {
+  @trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\Utility\TableSort::getQueryParameters() instead. See https://www.drupal.org/node/3009182', E_USER_DEPRECATED);
+  return TableSort::getQueryParameters(\Drupal::request());
+}
+
+/**
+ * Determines the current sort criterion.
+ *
+ * @param $headers
+ *   An array of column headers in the format described in '#type' => 'table'.
+ *
+ * @return
+ *   An associative array describing the criterion, containing the keys:
+ *   - "name": The localized title of the table column.
+ *   - "sql": The name of the database field to sort on.
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Utility\TableSort::getOrder() instead.
+ *
+ * @see \Drupal\Core\Utility\TableSortInterface::getOrder()
+ * @see https://www.drupal.org/node/3009182
+ */
+function tablesort_get_order($headers) {
+  @trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\Utility\TableSort::getOrder() instead. See https://www.drupal.org/node/3009182', E_USER_DEPRECATED);
+  return TableSort::getOrder($headers, \Drupal::request());
+}
+
+/**
+ * Determines the current sort direction.
+ *
+ * @param $headers
+ *   An array of column headers in the format described in '#type' => 'table'.
+ *
+ * @return
+ *   The current sort direction ("asc" or "desc").
+ *
+ * @deprecated in drupal:8.7.0 and is removed from drupal:9.0.0. Use
+ *   \Drupal\Core\Utility\TableSort::getSort() instead.
+ *
+ * @see \Drupal\Core\Utility\TableSortInterface::getSort()
+ * @see https://www.drupal.org/node/3009182
+ */
+function tablesort_get_sort($headers) {
+  @trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.7.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\Utility\TableSort::getSort() instead. See https://www.drupal.org/node/3009182', E_USER_DEPRECATED);
+  return TableSort::getSort($headers, \Drupal::request());
+}

+ 1895 - 0
web/core/includes/theme.inc

@@ -0,0 +1,1895 @@
+<?php
+
+/**
+ * @file
+ * The theme system, which controls the output of Drupal.
+ *
+ * The theme system allows for nearly all output of the Drupal system to be
+ * customized by user themes.
+ */
+
+use Drupal\Core\Url;
+use Drupal\Component\Serialization\Json;
+use Drupal\Component\Utility\Crypt;
+use Drupal\Component\Utility\Html;
+use Drupal\Component\Render\MarkupInterface;
+use Drupal\Core\Cache\CacheableDependencyInterface;
+use Drupal\Core\Config\Config;
+use Drupal\Core\Config\StorageException;
+use Drupal\Core\Render\AttachmentsInterface;
+use Drupal\Core\Render\BubbleableMetadata;
+use Drupal\Core\Render\RenderableInterface;
+use Drupal\Core\Template\Attribute;
+use Drupal\Core\Template\AttributeHelper;
+use Drupal\Core\Theme\ThemeSettings;
+use Drupal\Core\Render\Element;
+use Drupal\Core\Render\Markup;
+use Drupal\Core\Utility\TableSort;
+
+/**
+ * @defgroup content_flags Content markers
+ * @{
+ * Markers used by mark.html.twig and node_mark() to designate content.
+ *
+ * @see mark.html.twig
+ * @see node_mark()
+ */
+
+/**
+ * Mark content as read.
+ */
+const MARK_READ = 0;
+
+/**
+ * Mark content as being new.
+ */
+const MARK_NEW = 1;
+
+/**
+ * Mark content as being updated.
+ */
+const MARK_UPDATED = 2;
+
+/**
+ * A responsive table class; hide table cell on narrow devices.
+ *
+ * Indicates that a column has medium priority and thus can be hidden on narrow
+ * width devices and shown on medium+ width devices (i.e. tablets and desktops).
+ */
+const RESPONSIVE_PRIORITY_MEDIUM = 'priority-medium';
+
+/**
+ * A responsive table class; only show table cell on wide devices.
+ *
+ * Indicates that a column has low priority and thus can be hidden on narrow
+ * and medium viewports and shown on wide devices (i.e. desktops).
+ */
+const RESPONSIVE_PRIORITY_LOW = 'priority-low';
+
+/**
+ * @} End of "defgroup content_flags".
+ */
+
+/**
+ * Gets the theme registry.
+ *
+ * @param bool $complete
+ *   Optional boolean to indicate whether to return the complete theme registry
+ *   array or an instance of the Drupal\Core\Utility\ThemeRegistry class.
+ *   If TRUE, the complete theme registry array will be returned. This is useful
+ *   if you want to foreach over the whole registry, use array_* functions or
+ *   inspect it in a debugger. If FALSE, an instance of the
+ *   Drupal\Core\Utility\ThemeRegistry class will be returned, this provides an
+ *   ArrayObject which allows it to be accessed with array syntax and isset(),
+ *   and should be more lightweight than the full registry. Defaults to TRUE.
+ *
+ * @return
+ *   The complete theme registry array, or an instance of the
+ *   Drupal\Core\Utility\ThemeRegistry class.
+ */
+function theme_get_registry($complete = TRUE) {
+  $theme_registry = \Drupal::service('theme.registry');
+  if ($complete) {
+    return $theme_registry->get();
+  }
+  else {
+    return $theme_registry->getRuntime();
+  }
+}
+
+/**
+ * Returns an array of default theme features.
+ *
+ * @see \Drupal\Core\Extension\ThemeExtensionList::$defaults
+ */
+function _system_default_theme_features() {
+  return [
+    'favicon',
+    'logo',
+    'node_user_picture',
+    'comment_user_picture',
+    'comment_user_verification',
+  ];
+}
+
+/**
+ * Forces the system to rebuild the theme registry.
+ *
+ * This function should be called when modules are added to the system, or when
+ * a dynamic system needs to add more theme hooks.
+ */
+function drupal_theme_rebuild() {
+  \Drupal::service('theme.registry')->reset();
+}
+
+/**
+ * Allows themes and/or theme engines to discover overridden theme functions.
+ *
+ * @param array $cache
+ *   The existing cache of theme hooks to test against.
+ * @param array $prefixes
+ *   An array of prefixes to test, in reverse order of importance.
+ *
+ * @return array
+ *   The functions found, suitable for returning from hook_theme;
+ */
+function drupal_find_theme_functions($cache, $prefixes) {
+  $implementations = [];
+  $grouped_functions = \Drupal::service('theme.registry')->getPrefixGroupedUserFunctions($prefixes);
+
+  foreach ($cache as $hook => $info) {
+    foreach ($prefixes as $prefix) {
+      // Find theme functions that implement possible "suggestion" variants of
+      // registered theme hooks and add those as new registered theme hooks.
+      // The 'pattern' key defines a common prefix that all suggestions must
+      // start with. The default is the name of the hook followed by '__'. An
+      // 'base hook' key is added to each entry made for a found suggestion,
+      // so that common functionality can be implemented for all suggestions of
+      // the same base hook. To keep things simple, deep hierarchy of
+      // suggestions is not supported: each suggestion's 'base hook' key
+      // refers to a base hook, not to another suggestion, and all suggestions
+      // are found using the base hook's pattern, not a pattern from an
+      // intermediary suggestion.
+      $pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
+      // Grep only the functions which are within the prefix group.
+      list($first_prefix,) = explode('_', $prefix, 2);
+      if (!isset($info['base hook']) && !empty($pattern) && isset($grouped_functions[$first_prefix])) {
+        $matches = preg_grep('/^' . $prefix . '_' . $pattern . '/', $grouped_functions[$first_prefix]);
+        if ($matches) {
+          foreach ($matches as $match) {
+            $new_hook = substr($match, strlen($prefix) + 1);
+            $arg_name = isset($info['variables']) ? 'variables' : 'render element';
+            $implementations[$new_hook] = [
+              'function' => $match,
+              $arg_name => $info[$arg_name],
+              'base hook' => $hook,
+            ];
+          }
+        }
+      }
+      // Find theme functions that implement registered theme hooks and include
+      // that in what is returned so that the registry knows that the theme has
+      // this implementation.
+      if (function_exists($prefix . '_' . $hook)) {
+        $implementations[$hook] = [
+          'function' => $prefix . '_' . $hook,
+        ];
+      }
+    }
+  }
+
+  return $implementations;
+}
+
+/**
+ * Allows themes and/or theme engines to easily discover overridden templates.
+ *
+ * @param $cache
+ *   The existing cache of theme hooks to test against.
+ * @param $extension
+ *   The extension that these templates will have.
+ * @param $path
+ *   The path to search.
+ */
+function drupal_find_theme_templates($cache, $extension, $path) {
+  $implementations = [];
+
+  // Collect paths to all sub-themes grouped by base themes. These will be
+  // used for filtering. This allows base themes to have sub-themes in its
+  // folder hierarchy without affecting the base themes template discovery.
+  $theme_paths = [];
+  foreach (\Drupal::service('theme_handler')->listInfo() as $theme_info) {
+    if (!empty($theme_info->base_theme)) {
+      $theme_paths[$theme_info->base_theme][$theme_info->getName()] = $theme_info->getPath();
+    }
+  }
+  foreach ($theme_paths as $basetheme => $subthemes) {
+    foreach ($subthemes as $subtheme => $subtheme_path) {
+      if (isset($theme_paths[$subtheme])) {
+        $theme_paths[$basetheme] = array_merge($theme_paths[$basetheme], $theme_paths[$subtheme]);
+      }
+    }
+  }
+  $theme = \Drupal::theme()->getActiveTheme()->getName();
+  $subtheme_paths = isset($theme_paths[$theme]) ? $theme_paths[$theme] : [];
+
+  // Escape the periods in the extension.
+  $regex = '/' . str_replace('.', '\.', $extension) . '$/';
+  // Get a listing of all template files in the path to search.
+  $files = [];
+  if (is_dir($path)) {
+    $files = \Drupal::service('file_system')->scanDirectory($path, $regex, ['key' => 'filename']);
+  }
+
+  // Find templates that implement registered theme hooks and include that in
+  // what is returned so that the registry knows that the theme has this
+  // implementation.
+  foreach ($files as $template => $file) {
+    // Ignore sub-theme templates for the current theme.
+    if (strpos($file->uri, str_replace($subtheme_paths, '', $file->uri)) !== 0) {
+      continue;
+    }
+    // Remove the extension from the filename.
+    $template = str_replace($extension, '', $template);
+    // Transform - in filenames to _ to match function naming scheme
+    // for the purposes of searching.
+    $hook = strtr($template, '-', '_');
+    if (isset($cache[$hook])) {
+      $implementations[$hook] = [
+        'template' => $template,
+        'path' => dirname($file->uri),
+      ];
+    }
+
+    // Match templates based on the 'template' filename.
+    foreach ($cache as $hook => $info) {
+      if (isset($info['template'])) {
+        if ($template === $info['template']) {
+          $implementations[$hook] = [
+            'template' => $template,
+            'path' => dirname($file->uri),
+          ];
+        }
+      }
+    }
+  }
+
+  // Find templates that implement possible "suggestion" variants of registered
+  // theme hooks and add those as new registered theme hooks. See
+  // drupal_find_theme_functions() for more information about suggestions and
+  // the use of 'pattern' and 'base hook'.
+  $patterns = array_keys($files);
+  foreach ($cache as $hook => $info) {
+    $pattern = isset($info['pattern']) ? $info['pattern'] : ($hook . '__');
+    if (!isset($info['base hook']) && !empty($pattern)) {
+      // Transform _ in pattern to - to match file naming scheme
+      // for the purposes of searching.
+      $pattern = strtr($pattern, '_', '-');
+
+      $matches = preg_grep('/^' . $pattern . '/', $patterns);
+      if ($matches) {
+        foreach ($matches as $match) {
+          $file = $match;
+          // Remove the extension from the filename.
+          $file = str_replace($extension, '', $file);
+          // Put the underscores back in for the hook name and register this
+          // pattern.
+          $arg_name = isset($info['variables']) ? 'variables' : 'render element';
+          $implementations[strtr($file, '-', '_')] = [
+            'template' => $file,
+            'path' => dirname($files[$match]->uri),
+            $arg_name => $info[$arg_name],
+            'base hook' => $hook,
+          ];
+        }
+      }
+    }
+  }
+  return $implementations;
+}
+
+/**
+ * Retrieves a setting for the current theme or for a given theme.
+ *
+ * The final setting is obtained from the last value found in the following
+ * sources:
+ * - the saved values from the global theme settings form
+ * - the saved values from the theme's settings form
+ * To only retrieve the default global theme setting, an empty string should be
+ * given for $theme.
+ *
+ * @param $setting_name
+ *   The name of the setting to be retrieved.
+ * @param $theme
+ *   The name of a given theme; defaults to the current theme.
+ *
+ * @return
+ *   The value of the requested setting, NULL if the setting does not exist.
+ */
+function theme_get_setting($setting_name, $theme = NULL) {
+  /** @var \Drupal\Core\Theme\ThemeSettings[] $cache */
+  $cache = &drupal_static(__FUNCTION__, []);
+
+  // If no key is given, use the current theme if we can determine it.
+  if (!isset($theme)) {
+    $theme = \Drupal::theme()->getActiveTheme()->getName();
+  }
+
+  if (empty($cache[$theme])) {
+    // Create a theme settings object.
+    $cache[$theme] = new ThemeSettings($theme);
+    // Get the global settings from configuration.
+    $cache[$theme]->setData(\Drupal::config('system.theme.global')->get());
+
+    // Get the values for the theme-specific settings from the .info.yml files
+    // of the theme and all its base themes.
+    $themes = \Drupal::service('theme_handler')->listInfo();
+    if (isset($themes[$theme])) {
+      $theme_object = $themes[$theme];
+
+      // Retrieve configured theme-specific settings, if any.
+      try {
+        if ($theme_settings = \Drupal::config($theme . '.settings')->get()) {
+          $cache[$theme]->merge($theme_settings);
+        }
+      }
+      catch (StorageException $e) {
+      }
+
+      // If the theme does not support a particular feature, override the global
+      // setting and set the value to NULL.
+      if (!empty($theme_object->info['features'])) {
+        foreach (_system_default_theme_features() as $feature) {
+          if (!in_array($feature, $theme_object->info['features'])) {
+            $cache[$theme]->set('features.' . $feature, NULL);
+          }
+        }
+      }
+
+      // Generate the path to the logo image.
+      if ($cache[$theme]->get('logo.use_default')) {
+        $logo = \Drupal::service('theme.initialization')->getActiveThemeByName($theme)->getLogo();
+        $cache[$theme]->set('logo.url', file_url_transform_relative(file_create_url($logo)));
+      }
+      elseif ($logo_path = $cache[$theme]->get('logo.path')) {
+        $cache[$theme]->set('logo.url', file_url_transform_relative(file_create_url($logo_path)));
+      }
+
+      // Generate the path to the favicon.
+      if ($cache[$theme]->get('features.favicon')) {
+        $favicon_path = $cache[$theme]->get('favicon.path');
+        if ($cache[$theme]->get('favicon.use_default')) {
+          if (file_exists($favicon = $theme_object->getPath() . '/favicon.ico')) {
+            $cache[$theme]->set('favicon.url', file_url_transform_relative(file_create_url($favicon)));
+          }
+          else {
+            $cache[$theme]->set('favicon.url', file_url_transform_relative(file_create_url('core/misc/favicon.ico')));
+          }
+        }
+        elseif ($favicon_path) {
+          $cache[$theme]->set('favicon.url', file_url_transform_relative(file_create_url($favicon_path)));
+        }
+        else {
+          $cache[$theme]->set('features.favicon', FALSE);
+        }
+      }
+    }
+  }
+
+  return $cache[$theme]->get($setting_name);
+}
+
+/**
+ * Escapes and renders variables for theme functions.
+ *
+ * This method is used in theme functions to ensure that the result is safe for
+ * output inside HTML fragments. This mimics the behavior of the auto-escape
+ * functionality in Twig.
+ *
+ * Note: This function should be kept in sync with
+ * \Drupal\Core\Template\TwigExtension::escapeFilter().
+ *
+ * @param mixed $arg
+ *   The string, object, or render array to escape if needed.
+ *
+ * @return string
+ *   The rendered string, safe for use in HTML. The string is not safe when used
+ *   as any part of an HTML attribute name or value.
+ *
+ * @throws \Exception
+ *   Thrown when an object is passed in which cannot be printed.
+ *
+ * @see \Drupal\Core\Template\TwigExtension::escapeFilter()
+ *
+ * @todo Discuss deprecating this in https://www.drupal.org/node/2575081.
+ * @todo Refactor this to keep it in sync with Twig filtering in
+ *   https://www.drupal.org/node/2575065
+ */
+function theme_render_and_autoescape($arg) {
+  // If it's a renderable, then it'll be up to the generated render array it
+  // returns to contain the necessary cacheability & attachment metadata. If
+  // it doesn't implement CacheableDependencyInterface or AttachmentsInterface
+  // then there is nothing to do here.
+  if (!($arg instanceof RenderableInterface) && ($arg instanceof CacheableDependencyInterface || $arg instanceof AttachmentsInterface)) {
+    $arg_bubbleable = [];
+    BubbleableMetadata::createFromObject($arg)
+      ->applyTo($arg_bubbleable);
+    \Drupal::service('renderer')->render($arg_bubbleable);
+  }
+
+  if ($arg instanceof MarkupInterface) {
+    return (string) $arg;
+  }
+  $return = NULL;
+
+  if (is_scalar($arg)) {
+    $return = (string) $arg;
+  }
+  elseif (is_object($arg)) {
+    if ($arg instanceof RenderableInterface) {
+      $arg = $arg->toRenderable();
+    }
+    elseif (method_exists($arg, '__toString')) {
+      $return = (string) $arg;
+    }
+    // You can't throw exceptions in the magic PHP __toString methods, see
+    // http://php.net/manual/language.oop5.magic.php#object.tostring so
+    // we also support a toString method.
+    elseif (method_exists($arg, 'toString')) {
+      $return = $arg->toString();
+    }
+    else {
+      throw new \Exception('Object of type ' . get_class($arg) . ' cannot be printed.');
+    }
+  }
+
+  // We have a string or an object converted to a string: Escape it!
+  if (isset($return)) {
+    return $return instanceof MarkupInterface ? $return : Html::escape($return);
+  }
+
+  // This is a normal render array, which is safe by definition, with special
+  // simple cases already handled.
+
+  // Early return if this element was pre-rendered (no need to re-render).
+  if (isset($arg['#printed']) && $arg['#printed'] == TRUE && isset($arg['#markup']) && strlen($arg['#markup']) > 0) {
+    return (string) $arg['#markup'];
+  }
+  $arg['#printed'] = FALSE;
+  return (string) \Drupal::service('renderer')->render($arg);
+}
+
+/**
+ * Converts theme settings to configuration.
+ *
+ * @see system_theme_settings_submit()
+ *
+ * @param array $theme_settings
+ *   An array of theme settings from system setting form or a Drupal 7 variable.
+ * @param \Drupal\Core\Config\Config $config
+ *   The configuration object to update.
+ *
+ * @return
+ *   The Config object with updated data.
+ */
+function theme_settings_convert_to_config(array $theme_settings, Config $config) {
+  foreach ($theme_settings as $key => $value) {
+    if ($key == 'default_logo') {
+      $config->set('logo.use_default', $value);
+    }
+    elseif ($key == 'logo_path') {
+      $config->set('logo.path', $value);
+    }
+    elseif ($key == 'default_favicon') {
+      $config->set('favicon.use_default', $value);
+    }
+    elseif ($key == 'favicon_path') {
+      $config->set('favicon.path', $value);
+    }
+    elseif ($key == 'favicon_mimetype') {
+      $config->set('favicon.mimetype', $value);
+    }
+    elseif (substr($key, 0, 7) == 'toggle_') {
+      $config->set('features.' . mb_substr($key, 7), $value);
+    }
+    elseif (!in_array($key, ['theme', 'logo_upload'])) {
+      $config->set($key, $value);
+    }
+  }
+  return $config;
+}
+
+/**
+ * Prepares variables for time templates.
+ *
+ * Default template: time.html.twig.
+ *
+ * @param array $variables
+ *   An associative array possibly containing:
+ *   - attributes['timestamp']:
+ *   - timestamp:
+ *   - text:
+ */
+function template_preprocess_time(&$variables) {
+  /** @var \Drupal\Core\Datetime\DateFormatterInterface $date_formatter */
+  $date_formatter = \Drupal::service('date.formatter');
+  // Format the 'datetime' attribute based on the timestamp.
+  // @see http://www.w3.org/TR/html5-author/the-time-element.html#attr-time-datetime
+  if (!isset($variables['attributes']['datetime']) && isset($variables['timestamp'])) {
+    $variables['attributes']['datetime'] = $date_formatter->format($variables['timestamp'], 'html_datetime', '', 'UTC');
+  }
+
+  // If no text was provided, try to auto-generate it.
+  if (!isset($variables['text'])) {
+    // Format and use a human-readable version of the timestamp, if any.
+    if (isset($variables['timestamp'])) {
+      $variables['text'] = $date_formatter->format($variables['timestamp']);
+    }
+    // Otherwise, use the literal datetime attribute.
+    elseif (isset($variables['attributes']['datetime'])) {
+      $variables['text'] = $variables['attributes']['datetime'];
+    }
+  }
+}
+
+/**
+ * Prepares variables for datetime form element templates.
+ *
+ * The datetime form element serves as a wrapper around the date element type,
+ * which creates a date and a time component for a date.
+ *
+ * Default template: datetime-form.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #title, #value, #options, #description, #required,
+ *     #attributes.
+ *
+ * @see form_process_datetime()
+ */
+function template_preprocess_datetime_form(&$variables) {
+  $element = $variables['element'];
+
+  $variables['attributes'] = [];
+  if (isset($element['#id'])) {
+    $variables['attributes']['id'] = $element['#id'];
+  }
+  if (!empty($element['#attributes']['class'])) {
+    $variables['attributes']['class'] = (array) $element['#attributes']['class'];
+  }
+
+  $variables['content'] = $element;
+}
+
+/**
+ * Prepares variables for datetime form wrapper templates.
+ *
+ * Default template: datetime-wrapper.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #title, #children, #required, #attributes.
+ */
+function template_preprocess_datetime_wrapper(&$variables) {
+  $element = $variables['element'];
+
+  if (!empty($element['#title'])) {
+    $variables['title'] = $element['#title'];
+    // If the element title is a string, wrap it a render array so that markup
+    // will not be escaped (but XSS-filtered).
+    if (is_string($variables['title']) && $variables['title'] !== '') {
+      $variables['title'] = ['#markup' => $variables['title']];
+    }
+  }
+
+  // Suppress error messages.
+  $variables['errors'] = NULL;
+
+  $variables['description'] = NULL;
+  if (!empty($element['#description'])) {
+    $description_attributes = [];
+    if (!empty($element['#id'])) {
+      $description_attributes['id'] = $element['#id'] . '--description';
+    }
+    $variables['description'] = $element['#description'];
+    $variables['description_attributes'] = new Attribute($description_attributes);
+  }
+
+  $variables['required'] = FALSE;
+  // For required datetime fields 'form-required' & 'js-form-required' classes
+  // are appended to the label attributes.
+  if (!empty($element['#required'])) {
+    $variables['required'] = TRUE;
+  }
+  $variables['content'] = $element['#children'];
+}
+
+/**
+ * Prepares variables for links templates.
+ *
+ * Default template: links.html.twig.
+ *
+ * Unfortunately links templates duplicate the "active" class handling of l()
+ * and LinkGenerator::generate() because it needs to be able to set the "active"
+ * class not on the links themselves (<a> tags), but on the list items (<li>
+ * tags) that contain the links. This is necessary for CSS to be able to style
+ * list items differently when the link is active, since CSS does not yet allow
+ * one to style list items only if it contains a certain element with a certain
+ * class. I.e. we cannot yet convert this jQuery selector to a CSS selector:
+ * jQuery('li:has("a.is-active")')
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - links: An array of links to be themed. Each link itself is an array, with
+ *     the following elements:
+ *     - title: The link text.
+ *     - url: (optional) The \Drupal\Core\Url object to link to. If the 'url'
+ *       element is supplied, the 'title' and 'url' are used to generate a link
+ *       through \Drupal::linkGenerator()->generate(). All data from the link
+ *       array other than 'title' and 'url' are added as #options on
+ *       the URL object. See \Drupal\Core\Url::fromUri() for details on the
+ *       options. If no 'url' is supplied, the 'title' is printed as plain text.
+ *     - attributes: (optional) Attributes for the anchor, or for the <span>
+ *       tag used in its place if no 'url' is supplied. If element 'class' is
+ *       included, it must be an array of one or more class names.
+ *   - attributes: A keyed array of attributes for the <ul> containing the list
+ *     of links.
+ *   - set_active_class: (optional) Whether each link should compare the
+ *     route_name + route_parameters or url (path), language, and query options
+ *     to the current URL, to determine whether the link is "active". If so,
+ *     attributes will be added to the HTML elements for both the link and the
+ *     list item that contains it, which will result in an "is-active" class
+ *     being added to both. The class is added via JavaScript for authenticated
+ *     users (in the active-link library), and via PHP for anonymous users (in
+ *     the \Drupal\Core\EventSubscriber\ActiveLinkResponseFilter class).
+ *   - heading: (optional) A heading to precede the links. May be an
+ *     associative array or a string. If it's an array, it can have the
+ *     following elements:
+ *     - text: The heading text.
+ *     - level: The heading level (e.g. 'h2', 'h3').
+ *     - attributes: (optional) An array of the CSS attributes for the heading.
+ *     When using a string it will be used as the text of the heading and the
+ *     level will default to 'h2'. Headings should be used on navigation menus
+ *     and any list of links that consistently appears on multiple pages. To
+ *     make the heading invisible use the 'visually-hidden' CSS class. Do not
+ *     use 'display:none', which removes it from screen readers and assistive
+ *     technology. Headings allow screen reader and keyboard only users to
+ *     navigate to or skip the links. See
+ *     http://juicystudio.com/article/screen-readers-display-none.php and
+ *     http://www.w3.org/TR/WCAG-TECHS/H42.html for more information.
+ *
+ * @see \Drupal\Core\Utility\LinkGenerator
+ * @see \Drupal\Core\Utility\LinkGenerator::generate()
+ * @see system_page_attachments()
+ */
+function template_preprocess_links(&$variables) {
+  $links = $variables['links'];
+  $heading = &$variables['heading'];
+
+  if (!empty($links)) {
+    // Prepend the heading to the list, if any.
+    if (!empty($heading)) {
+      // Convert a string heading into an array, using a <h2> tag by default.
+      if (is_string($heading)) {
+        $heading = ['text' => $heading];
+      }
+      // Merge in default array properties into $heading.
+      $heading += [
+        'level' => 'h2',
+        'attributes' => [],
+      ];
+      // Convert the attributes array into an Attribute object.
+      $heading['attributes'] = new Attribute($heading['attributes']);
+    }
+
+    $variables['links'] = [];
+    foreach ($links as $key => $link) {
+      $item = [];
+      $link += [
+        'ajax' => NULL,
+        'url' => NULL,
+      ];
+
+      $li_attributes = [];
+      $keys = ['title', 'url'];
+      $link_element = [
+        '#type' => 'link',
+        '#title' => $link['title'],
+        '#options' => array_diff_key($link, array_combine($keys, $keys)),
+        '#url' => $link['url'],
+        '#ajax' => $link['ajax'],
+      ];
+
+      // Handle links and ensure that the active class is added on the LIs, but
+      // only if the 'set_active_class' option is not empty. Links templates
+      // duplicate the "is-active" class handling of l() and
+      // LinkGenerator::generate() because they need to be able to set the
+      // "is-active" class not on the links themselves (<a> tags), but on the
+      // list items (<li> tags) that contain the links. This is necessary for
+      // CSS to be able to style list items differently when the link is active,
+      // since CSS does not yet allow one to style list items only if they
+      // contain a certain element with a certain class. That is, we cannot yet
+      // convert this jQuery selector to a CSS selector:
+      // jQuery('li:has("a.is-active")')
+      if (isset($link['url'])) {
+        if (!empty($variables['set_active_class'])) {
+
+          // Also enable set_active_class for the contained link.
+          $link_element['#options']['set_active_class'] = TRUE;
+
+          if (!empty($link['language'])) {
+            $li_attributes['hreflang'] = $link['language']->getId();
+          }
+
+          // Add a "data-drupal-link-query" attribute to let the
+          // drupal.active-link library know the query in a standardized manner.
+          // Only add the data- attribute. The "is-active" class will be
+          // calculated using JavaScript, to prevent breaking the render cache.
+          if (!empty($link['query'])) {
+            $query = $link['query'];
+            ksort($query);
+            $li_attributes['data-drupal-link-query'] = Json::encode($query);
+          }
+
+          /** @var \Drupal\Core\Url $url */
+          $url = $link['url'];
+          if ($url->isRouted()) {
+            // Add a "data-drupal-link-system-path" attribute to let the
+            // drupal.active-link library know the path in a standardized
+            // manner. Only add the data- attribute. The "is-active" class will
+            // be calculated using JavaScript, to prevent breaking the render
+            // cache.
+            $system_path = $url->getInternalPath();
+            // @todo System path is deprecated - use the route name and parameters.
+            // Special case for the front page.
+            $li_attributes['data-drupal-link-system-path'] = $system_path == '' ? '<front>' : $system_path;
+          }
+        }
+
+        $item['link'] = $link_element;
+      }
+
+      // Handle title-only text items.
+      $item['text'] = $link['title'];
+      if (isset($link['attributes'])) {
+        $item['text_attributes'] = new Attribute($link['attributes']);
+      }
+
+      // Handle list item attributes.
+      $item['attributes'] = new Attribute($li_attributes);
+
+      // Add the item to the list of links.
+      $variables['links'][$key] = $item;
+    }
+  }
+}
+
+/**
+ * Prepares variables for image templates.
+ *
+ * Default template: image.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - uri: Either the path of the image file (relative to base_path()) or a
+ *     full URL.
+ *   - width: The width of the image (if known).
+ *   - height: The height of the image (if known).
+ *   - alt: The alternative text for text-based browsers. HTML 4 and XHTML 1.0
+ *     always require an alt attribute. The HTML 5 draft allows the alt
+ *     attribute to be omitted in some cases. Therefore, this variable defaults
+ *     to an empty string, but can be set to NULL for the attribute to be
+ *     omitted. Usually, neither omission nor an empty string satisfies
+ *     accessibility requirements, so it is strongly encouraged for code
+ *     building variables for image.html.twig templates to pass a meaningful
+ *     value for this variable.
+ *     - http://www.w3.org/TR/REC-html40/struct/objects.html#h-13.8
+ *     - http://www.w3.org/TR/xhtml1/dtds.html
+ *     - http://dev.w3.org/html5/spec/Overview.html#alt
+ *   - title: The title text is displayed when the image is hovered in some
+ *     popular browsers.
+ *   - attributes: Associative array of attributes to be placed in the img tag.
+ *   - srcset: Array of multiple URIs and sizes/multipliers.
+ *   - sizes: The sizes attribute for viewport-based selection of images.
+ *     - http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content.html#introduction-3:viewport-based-selection-2
+ */
+function template_preprocess_image(&$variables) {
+  if (!empty($variables['uri'])) {
+    $variables['attributes']['src'] = file_url_transform_relative(file_create_url($variables['uri']));
+  }
+  // Generate a srcset attribute conforming to the spec at
+  // http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#attr-img-srcset
+  if (!empty($variables['srcset'])) {
+    $srcset = [];
+    foreach ($variables['srcset'] as $src) {
+      // URI is mandatory.
+      $source = file_url_transform_relative(file_create_url($src['uri']));
+      if (isset($src['width']) && !empty($src['width'])) {
+        $source .= ' ' . $src['width'];
+      }
+      elseif (isset($src['multiplier']) && !empty($src['multiplier'])) {
+        $source .= ' ' . $src['multiplier'];
+      }
+      $srcset[] = $source;
+    }
+    $variables['attributes']['srcset'] = implode(', ', $srcset);
+  }
+
+  foreach (['width', 'height', 'alt', 'title', 'sizes'] as $key) {
+    if (isset($variables[$key])) {
+      // If the property has already been defined in the attributes,
+      // do not override, including NULL.
+      if (AttributeHelper::attributeExists($key, $variables['attributes'])) {
+        continue;
+      }
+      $variables['attributes'][$key] = $variables[$key];
+    }
+  }
+}
+
+/**
+ * Prepares variables for table templates.
+ *
+ * Default template: table.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - header: An array containing the table headers. Each element of the array
+ *     can be either a localized string or an associative array with the
+ *     following keys:
+ *     - data: The localized title of the table column, as a string or render
+ *       array.
+ *     - field: The database field represented in the table column (required
+ *       if user is to be able to sort on this column).
+ *     - sort: A default sort order for this column ("asc" or "desc"). Only
+ *       one column should be given a default sort order because table sorting
+ *       only applies to one column at a time.
+ *     - initial_click_sort: Set the initial sort of the column when clicked.
+ *       Defaults to "asc".
+ *     - class: An array of values for the 'class' attribute. In particular,
+ *       the least important columns that can be hidden on narrow and medium
+ *       width screens should have a 'priority-low' class, referenced with the
+ *       RESPONSIVE_PRIORITY_LOW constant. Columns that should be shown on
+ *       medium+ wide screens should be marked up with a class of
+ *       'priority-medium', referenced by with the RESPONSIVE_PRIORITY_MEDIUM
+ *       constant. Themes may hide columns with one of these two classes on
+ *       narrow viewports to save horizontal space.
+ *     - Any HTML attributes, such as "colspan", to apply to the column header
+ *       cell.
+ *   - rows: An array of table rows. Every row is an array of cells, or an
+ *     associative array with the following keys:
+ *     - data: An array of cells.
+ *     - Any HTML attributes, such as "class", to apply to the table row.
+ *     - no_striping: A Boolean indicating that the row should receive no
+ *       'even / odd' styling. Defaults to FALSE.
+ *     Each cell can be either a string or an associative array with the
+ *     following keys:
+ *     - data: The string or render array to display in the table cell.
+ *     - header: Indicates this cell is a header.
+ *     - Any HTML attributes, such as "colspan", to apply to the table cell.
+ *     Here's an example for $rows:
+ *     @code
+ *     $rows = array(
+ *       // Simple row
+ *       array(
+ *         'Cell 1', 'Cell 2', 'Cell 3'
+ *       ),
+ *       // Row with attributes on the row and some of its cells.
+ *       array(
+ *         'data' => array('Cell 1', array('data' => 'Cell 2', 'colspan' => 2)), 'class' => array('funky')
+ *       ),
+ *     );
+ *     @endcode
+ *   - footer: An array of table rows which will be printed within a <tfoot>
+ *     tag, in the same format as the rows element (see above).
+ *   - attributes: An array of HTML attributes to apply to the table tag.
+ *   - caption: A localized string to use for the <caption> tag.
+ *   - colgroups: An array of column groups. Each element of the array can be
+ *     either:
+ *     - An array of columns, each of which is an associative array of HTML
+ *       attributes applied to the <col> element.
+ *     - An array of attributes applied to the <colgroup> element, which must
+ *       include a "data" attribute. To add attributes to <col> elements,
+ *       set the "data" attribute with an array of columns, each of which is an
+ *       associative array of HTML attributes.
+ *     Here's an example for $colgroup:
+ *     @code
+ *     $colgroup = array(
+ *       // <colgroup> with one <col> element.
+ *       array(
+ *         array(
+ *           'class' => array('funky'), // Attribute for the <col> element.
+ *         ),
+ *       ),
+ *       // <colgroup> with attributes and inner <col> elements.
+ *       array(
+ *         'data' => array(
+ *           array(
+ *             'class' => array('funky'), // Attribute for the <col> element.
+ *           ),
+ *         ),
+ *         'class' => array('jazzy'), // Attribute for the <colgroup> element.
+ *       ),
+ *     );
+ *     @endcode
+ *     These optional tags are used to group and set properties on columns
+ *     within a table. For example, one may easily group three columns and
+ *     apply same background style to all.
+ *   - sticky: Use a "sticky" table header.
+ *   - empty: The message to display in an extra row if table does not have any
+ *     rows.
+ */
+function template_preprocess_table(&$variables) {
+  // Format the table columns:
+  if (!empty($variables['colgroups'])) {
+    foreach ($variables['colgroups'] as &$colgroup) {
+      // Check if we're dealing with a simple or complex column
+      if (isset($colgroup['data'])) {
+        $cols = $colgroup['data'];
+        unset($colgroup['data']);
+        $colgroup_attributes = $colgroup;
+      }
+      else {
+        $cols = $colgroup;
+        $colgroup_attributes = [];
+      }
+      $colgroup = [];
+      $colgroup['attributes'] = new Attribute($colgroup_attributes);
+      $colgroup['cols'] = [];
+
+      // Build columns.
+      if (is_array($cols) && !empty($cols)) {
+        foreach ($cols as $col_key => $col) {
+          $colgroup['cols'][$col_key]['attributes'] = new Attribute($col);
+        }
+      }
+    }
+  }
+
+  // Build an associative array of responsive classes keyed by column.
+  $responsive_classes = [];
+
+  // Format the table header:
+  $ts = [];
+  $header_columns = 0;
+  if (!empty($variables['header'])) {
+    $ts = TableSort::getContextFromRequest($variables['header'], \Drupal::request());
+
+    // Use a separate index with responsive classes as headers
+    // may be associative.
+    $responsive_index = -1;
+    foreach ($variables['header'] as $col_key => $cell) {
+      // Increase the responsive index.
+      $responsive_index++;
+
+      if (!is_array($cell)) {
+        $header_columns++;
+        $cell_content = $cell;
+        $cell_attributes = new Attribute();
+        $is_header = TRUE;
+      }
+      else {
+        if (isset($cell['colspan'])) {
+          $header_columns += $cell['colspan'];
+        }
+        else {
+          $header_columns++;
+        }
+        $cell_content = '';
+        if (isset($cell['data'])) {
+          $cell_content = $cell['data'];
+          unset($cell['data']);
+        }
+        // Flag the cell as a header or not and remove the flag.
+        $is_header = isset($cell['header']) ? $cell['header'] : TRUE;
+        unset($cell['header']);
+
+        // Track responsive classes for each column as needed. Only the header
+        // cells for a column are marked up with the responsive classes by a
+        // module developer or themer. The responsive classes on the header cells
+        // must be transferred to the content cells.
+        if (!empty($cell['class']) && is_array($cell['class'])) {
+          if (in_array(RESPONSIVE_PRIORITY_MEDIUM, $cell['class'])) {
+            $responsive_classes[$responsive_index] = RESPONSIVE_PRIORITY_MEDIUM;
+          }
+          elseif (in_array(RESPONSIVE_PRIORITY_LOW, $cell['class'])) {
+            $responsive_classes[$responsive_index] = RESPONSIVE_PRIORITY_LOW;
+          }
+        }
+
+        TableSort::header($cell_content, $cell, $variables['header'], $ts);
+
+        // TableSort::header() removes the 'sort', 'initial_click_sort' and
+        // 'field' keys.
+        $cell_attributes = new Attribute($cell);
+      }
+      $variables['header'][$col_key] = [];
+      $variables['header'][$col_key]['tag'] = $is_header ? 'th' : 'td';
+      $variables['header'][$col_key]['attributes'] = $cell_attributes;
+      $variables['header'][$col_key]['content'] = $cell_content;
+    }
+  }
+  $variables['header_columns'] = $header_columns;
+
+  // Rows and footer have the same structure.
+  $sections = ['rows' , 'footer'];
+  foreach ($sections as $section) {
+    if (!empty($variables[$section])) {
+      foreach ($variables[$section] as $row_key => $row) {
+        $cells = $row;
+        $row_attributes = [];
+
+        // Check if we're dealing with a simple or complex row
+        if (isset($row['data'])) {
+          $cells = $row['data'];
+          $variables['no_striping'] = isset($row['no_striping']) ? $row['no_striping'] : FALSE;
+
+          // Set the attributes array and exclude 'data' and 'no_striping'.
+          $row_attributes = $row;
+          unset($row_attributes['data']);
+          unset($row_attributes['no_striping']);
+        }
+
+        // Build row.
+        $variables[$section][$row_key] = [];
+        $variables[$section][$row_key]['attributes'] = new Attribute($row_attributes);
+        $variables[$section][$row_key]['cells'] = [];
+        if (!empty($cells)) {
+          // Reset the responsive index.
+          $responsive_index = -1;
+          foreach ($cells as $col_key => $cell) {
+            // Increase the responsive index.
+            $responsive_index++;
+
+            if (!is_array($cell)) {
+              $cell_content = $cell;
+              $cell_attributes = [];
+              $is_header = FALSE;
+            }
+            else {
+              $cell_content = '';
+              if (isset($cell['data'])) {
+                $cell_content = $cell['data'];
+                unset($cell['data']);
+              }
+
+              // Flag the cell as a header or not and remove the flag.
+              $is_header = !empty($cell['header']);
+              unset($cell['header']);
+
+              $cell_attributes = $cell;
+            }
+            // Active table sort information.
+            if (isset($variables['header'][$col_key]['data']) && $variables['header'][$col_key]['data'] == $ts['name'] && !empty($variables['header'][$col_key]['field'])) {
+              $variables[$section][$row_key]['cells'][$col_key]['active_table_sort'] = TRUE;
+            }
+            // Copy RESPONSIVE_PRIORITY_LOW/RESPONSIVE_PRIORITY_MEDIUM
+            // class from header to cell as needed.
+            if (isset($responsive_classes[$responsive_index])) {
+              $cell_attributes['class'][] = $responsive_classes[$responsive_index];
+            }
+            $variables[$section][$row_key]['cells'][$col_key]['tag'] = $is_header ? 'th' : 'td';
+            $variables[$section][$row_key]['cells'][$col_key]['attributes'] = new Attribute($cell_attributes);
+            $variables[$section][$row_key]['cells'][$col_key]['content'] = $cell_content;
+          }
+        }
+      }
+    }
+  }
+  if (empty($variables['no_striping'])) {
+    $variables['attributes']['data-striping'] = 1;
+  }
+}
+
+/**
+ * Prepares variables for item list templates.
+ *
+ * Default template: item-list.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - items: An array of items to be displayed in the list. Each item can be
+ *     either a string or a render array. If #type, #theme, or #markup
+ *     properties are not specified for child render arrays, they will be
+ *     inherited from the parent list, allowing callers to specify larger
+ *     nested lists without having to explicitly specify and repeat the
+ *     render properties for all nested child lists.
+ *   - title: A title to be prepended to the list.
+ *   - list_type: The type of list to return (e.g. "ul", "ol").
+ *   - wrapper_attributes: HTML attributes to be applied to the list wrapper.
+ *
+ * @see https://www.drupal.org/node/1842756
+ */
+function template_preprocess_item_list(&$variables) {
+  $variables['wrapper_attributes'] = new Attribute($variables['wrapper_attributes']);
+  foreach ($variables['items'] as &$item) {
+    $attributes = [];
+    // If the item value is an array, then it is a render array.
+    if (is_array($item)) {
+      // List items support attributes via the '#wrapper_attributes' property.
+      if (isset($item['#wrapper_attributes'])) {
+        $attributes = $item['#wrapper_attributes'];
+      }
+      // Determine whether there are any child elements in the item that are not
+      // fully-specified render arrays. If there are any, then the child
+      // elements present nested lists and we automatically inherit the render
+      // array properties of the current list to them.
+      foreach (Element::children($item) as $key) {
+        $child = &$item[$key];
+        // If this child element does not specify how it can be rendered, then
+        // we need to inherit the render properties of the current list.
+        if (!isset($child['#type']) && !isset($child['#theme']) && !isset($child['#markup'])) {
+          // Since item-list.html.twig supports both strings and render arrays
+          // as items, the items of the nested list may have been specified as
+          // the child elements of the nested list, instead of #items. For
+          // convenience, we automatically move them into #items.
+          if (!isset($child['#items'])) {
+            // This is the same condition as in
+            // \Drupal\Core\Render\Element::children(), which cannot be used
+            // here, since it triggers an error on string values.
+            foreach ($child as $child_key => $child_value) {
+              if (is_int($child_key) || $child_key === '' || $child_key[0] !== '#') {
+                $child['#items'][$child_key] = $child_value;
+                unset($child[$child_key]);
+              }
+            }
+          }
+          // Lastly, inherit the original theme variables of the current list.
+          $child['#theme'] = $variables['theme_hook_original'];
+          $child['#list_type'] = $variables['list_type'];
+        }
+      }
+    }
+
+    // Set the item's value and attributes for the template.
+    $item = [
+      'value' => $item,
+      'attributes' => new Attribute($attributes),
+    ];
+  }
+}
+
+/**
+ * Prepares variables for container templates.
+ *
+ * Default template: container.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: An associative array containing the properties of the element.
+ *     Properties used: #id, #attributes, #children.
+ */
+function template_preprocess_container(&$variables) {
+  $variables['has_parent'] = FALSE;
+  $element = $variables['element'];
+  // Ensure #attributes is set.
+  $element += ['#attributes' => []];
+
+  // Special handling for form elements.
+  if (isset($element['#array_parents'])) {
+    // Assign an html ID.
+    if (!isset($element['#attributes']['id'])) {
+      $element['#attributes']['id'] = $element['#id'];
+    }
+    $variables['has_parent'] = TRUE;
+  }
+
+  $variables['children'] = $element['#children'];
+  $variables['attributes'] = $element['#attributes'];
+}
+
+/**
+ * Prepares variables for maintenance task list templates.
+ *
+ * Default template: maintenance-task-list.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - items: An associative array of maintenance tasks.
+ *     It's the caller's responsibility to ensure this array's items contain no
+ *     dangerous HTML such as <script> tags.
+ *   - active: The key for the currently active maintenance task.
+ */
+function template_preprocess_maintenance_task_list(&$variables) {
+  $items = $variables['items'];
+  $active = $variables['active'];
+
+  $done = isset($items[$active]) || $active == NULL;
+  foreach ($items as $k => $item) {
+    $variables['tasks'][$k]['item'] = $item;
+    $variables['tasks'][$k]['attributes'] = new Attribute();
+    if ($active == $k) {
+      $variables['tasks'][$k]['attributes']->addClass('is-active');
+      $variables['tasks'][$k]['status'] = t('active');
+      $done = FALSE;
+    }
+    else {
+      if ($done) {
+        $variables['tasks'][$k]['attributes']->addClass('done');
+        $variables['tasks'][$k]['status'] = t('done');
+      }
+    }
+  }
+}
+
+/**
+ * Adds a default set of helper variables for preprocessors and templates.
+ *
+ * This function is called for every theme hook. It is the first in the
+ * sequence of preprocessing functions called when preparing variables for a
+ * template.
+ *
+ * See the @link themeable Default theme implementations topic @endlink for
+ * details.
+ */
+function template_preprocess(&$variables, $hook, $info) {
+  // Merge in variables that don't depend on hook and don't change during a
+  // single page request.
+  // Use the advanced drupal_static() pattern, since this is called very often.
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast['default_variables'] = &drupal_static(__FUNCTION__);
+  }
+  $default_variables = &$drupal_static_fast['default_variables'];
+  if (!isset($default_variables)) {
+    $default_variables = _template_preprocess_default_variables();
+  }
+  $variables += $default_variables;
+
+  // When theming a render element, merge its #attributes into
+  // $variables['attributes'].
+  if (isset($info['render element'])) {
+    $key = $info['render element'];
+    if (isset($variables[$key]['#attributes'])) {
+      $variables['attributes'] = AttributeHelper::mergeCollections($variables['attributes'], $variables[$key]['#attributes']);
+    }
+  }
+}
+
+/**
+ * Returns hook-independent variables to template_preprocess().
+ */
+function _template_preprocess_default_variables() {
+  // Variables that don't depend on a database connection.
+  $variables = [
+    'attributes' => [],
+    'title_attributes' => [],
+    'content_attributes' => [],
+    'title_prefix' => [],
+    'title_suffix' => [],
+    'db_is_active' => !defined('MAINTENANCE_MODE'),
+    'is_admin' => FALSE,
+    'logged_in' => FALSE,
+  ];
+
+  // Give modules a chance to alter the default template variables.
+  \Drupal::moduleHandler()->alter('template_preprocess_default_variables', $variables);
+
+  // Tell all templates where they are located.
+  $variables['directory'] = \Drupal::theme()->getActiveTheme()->getPath();
+
+  return $variables;
+}
+
+/**
+ * Prepares variables for HTML document templates.
+ *
+ * Default template: html.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - page: A render element representing the page.
+ */
+function template_preprocess_html(&$variables) {
+  $variables['page'] = $variables['html']['page'];
+  unset($variables['html']['page']);
+  $variables['page_top'] = NULL;
+  if (isset($variables['html']['page_top'])) {
+    $variables['page_top'] = $variables['html']['page_top'];
+    unset($variables['html']['page_top']);
+  }
+  $variables['page_bottom'] = NULL;
+  if (isset($variables['html']['page_bottom'])) {
+    $variables['page_bottom'] = $variables['html']['page_bottom'];
+    unset($variables['html']['page_bottom']);
+  }
+
+  $variables['html_attributes'] = new Attribute();
+
+  // <html> element attributes.
+  $language_interface = \Drupal::languageManager()->getCurrentLanguage();
+  $variables['html_attributes']['lang'] = $language_interface->getId();
+  $variables['html_attributes']['dir'] = $language_interface->getDirection();
+
+  if (isset($variables['db_is_active']) && !$variables['db_is_active']) {
+    $variables['db_offline'] = TRUE;
+  }
+
+  // Add a variable for the root path. This can be used to create a class and
+  // theme the page depending on the current path (e.g. node, admin, user) as
+  // well as more specific data like path-frontpage.
+  $is_front_page = \Drupal::service('path.matcher')->isFrontPage();
+
+  if ($is_front_page) {
+    $variables['root_path'] = FALSE;
+  }
+  else {
+    $system_path = \Drupal::service('path.current')->getPath();
+    $variables['root_path'] = explode('/', $system_path)[1];
+  }
+
+  $site_config = \Drupal::config('system.site');
+  // Construct page title.
+  if (isset($variables['page']['#title']) && is_array($variables['page']['#title'])) {
+    // Do an early render if the title is a render array.
+    $variables['page']['#title'] = (string) \Drupal::service('renderer')->render($variables['page']['#title']);
+  }
+  if (!empty($variables['page']['#title'])) {
+    $head_title = [
+      // Marking the title as safe since it has had the tags stripped.
+      'title' => Markup::create(trim(strip_tags($variables['page']['#title']))),
+      'name' => $site_config->get('name'),
+    ];
+  }
+  // @todo Remove once views is not bypassing the view subscriber anymore.
+  //   @see https://www.drupal.org/node/2068471
+  elseif ($is_front_page) {
+    $head_title = [
+      'title' => t('Home'),
+      'name' => $site_config->get('name'),
+    ];
+  }
+  else {
+    $head_title = ['name' => $site_config->get('name')];
+    if ($site_config->get('slogan')) {
+      $head_title['slogan'] = strip_tags($site_config->get('slogan'));
+    }
+  }
+
+  $variables['head_title'] = $head_title;
+  // @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
+  $variables['head_title_array'] = $head_title;
+
+  // Create placeholder strings for these keys.
+  // @see \Drupal\Core\Render\HtmlResponseSubscriber
+  $types = [
+    'styles' => 'css',
+    'scripts' => 'js',
+    'scripts_bottom' => 'js-bottom',
+    'head' => 'head',
+  ];
+  $variables['placeholder_token'] = Crypt::randomBytesBase64(55);
+  foreach ($types as $type => $placeholder_name) {
+    $placeholder = '<' . $placeholder_name . '-placeholder token="' . $variables['placeholder_token'] . '">';
+    $variables['#attached']['html_response_attachment_placeholders'][$type] = $placeholder;
+  }
+}
+
+/**
+ * Prepares variables for the page template.
+ *
+ * Default template: page.html.twig.
+ *
+ * See the page.html.twig template for the list of variables.
+ */
+function template_preprocess_page(&$variables) {
+  $language_interface = \Drupal::languageManager()->getCurrentLanguage();
+
+  foreach (\Drupal::theme()->getActiveTheme()->getRegions() as $region) {
+    if (!isset($variables['page'][$region])) {
+      $variables['page'][$region] = [];
+    }
+  }
+
+  $variables['base_path'] = base_path();
+  $variables['front_page'] = Url::fromRoute('<front>')->toString();
+  $variables['language'] = $language_interface;
+
+  // An exception might be thrown.
+  try {
+    $variables['is_front'] = \Drupal::service('path.matcher')->isFrontPage();
+  }
+  catch (Exception $e) {
+    // If the database is not yet available, set default values for these
+    // variables.
+    $variables['is_front'] = FALSE;
+    $variables['db_is_active'] = FALSE;
+  }
+
+  if ($node = \Drupal::routeMatch()->getParameter('node')) {
+    $variables['node'] = $node;
+  }
+}
+
+/**
+ * Generate an array of suggestions from path arguments.
+ *
+ * This is typically called for adding to the suggestions in
+ * hook_theme_suggestions_HOOK_alter() or adding to 'attributes' class key
+ * variables from within preprocess functions, when wanting to base the
+ * additional suggestions or classes on the path of the current page.
+ *
+ * @param $args
+ *   An array of path arguments.
+ * @param $base
+ *   A string identifying the base 'thing' from which more specific suggestions
+ *   are derived. For example, 'page' or 'html'.
+ * @param $delimiter
+ *   The string used to delimit increasingly specific information. The default
+ *   of '__' is appropriate for theme hook suggestions. '-' is appropriate for
+ *   extra classes.
+ *
+ * @return
+ *   An array of suggestions, suitable for adding to
+ *   hook_theme_suggestions_HOOK_alter() or to $variables['attributes']['class']
+ *   if the suggestions represent extra CSS classes.
+ */
+function theme_get_suggestions($args, $base, $delimiter = '__') {
+
+  // Build a list of suggested theme hooks in order of
+  // specificity. One suggestion is made for every element of the current path,
+  // though numeric elements are not carried to subsequent suggestions. For
+  // example, for $base='page', http://www.example.com/node/1/edit would result
+  // in the following suggestions:
+  //
+  // page__node
+  // page__node__%
+  // page__node__1
+  // page__node__edit
+
+  $suggestions = [];
+  $prefix = $base;
+  foreach ($args as $arg) {
+    // Remove slashes or null per SA-CORE-2009-003 and change - (hyphen) to _
+    // (underscore).
+    //
+    // When we discover templates in @see drupal_find_theme_templates,
+    // hyphens (-) are converted to underscores (_) before the theme hook
+    // is registered. We do this because the hyphens used for delimiters
+    // in hook suggestions cannot be used in the function names of the
+    // associated preprocess functions. Any page templates designed to be used
+    // on paths that contain a hyphen are also registered with these hyphens
+    // converted to underscores so here we must convert any hyphens in path
+    // arguments to underscores here before fetching theme hook suggestions
+    // to ensure the templates are appropriately recognized.
+    $arg = str_replace(["/", "\\", "\0", '-'], ['', '', '', '_'], $arg);
+    // The percent acts as a wildcard for numeric arguments since
+    // asterisks are not valid filename characters on many filesystems.
+    if (is_numeric($arg)) {
+      $suggestions[] = $prefix . $delimiter . '%';
+    }
+    $suggestions[] = $prefix . $delimiter . $arg;
+    if (!is_numeric($arg)) {
+      $prefix .= $delimiter . $arg;
+    }
+  }
+  if (\Drupal::service('path.matcher')->isFrontPage()) {
+    // Front templates should be based on root only, not prefixed arguments.
+    $suggestions[] = $base . $delimiter . 'front';
+  }
+
+  return $suggestions;
+}
+
+/**
+ * Prepares variables for maintenance page templates.
+ *
+ * Default template: maintenance-page.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - content - An array of page content.
+ *
+ * @see system_page_attachments()
+ */
+function template_preprocess_maintenance_page(&$variables) {
+  // @todo Rename the templates to page--maintenance + page--install.
+  template_preprocess_page($variables);
+
+  // @see system_page_attachments()
+  $variables['#attached']['library'][] = 'system/maintenance';
+
+  // Maintenance page and install page need branding info in variables because
+  // there is no blocks.
+  $site_config = \Drupal::config('system.site');
+  $variables['logo'] = theme_get_setting('logo.url');
+  $variables['site_name'] = $site_config->get('name');
+  $variables['site_slogan'] = $site_config->get('slogan');
+
+  // Maintenance page and install page need page title in variable because there
+  // are no blocks.
+  $variables['title'] = $variables['page']['#title'];
+}
+
+/**
+ * Prepares variables for install page templates.
+ *
+ * Default template: install-page.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - content - An array of page content.
+ *
+ * @see template_preprocess_maintenance_page()
+ */
+function template_preprocess_install_page(&$variables) {
+  template_preprocess_maintenance_page($variables);
+
+  // Override the site name that is displayed on the page, since Drupal is
+  // still in the process of being installed.
+  $distribution_name = drupal_install_profile_distribution_name();
+  $variables['site_name'] = $distribution_name;
+  $variables['site_version'] = drupal_install_profile_distribution_version();
+}
+
+/**
+ * Prepares variables for region templates.
+ *
+ * Default template: region.html.twig.
+ *
+ * Prepares the values passed to the theme_region function to be passed into a
+ * pluggable template engine. Uses the region name to generate a template file
+ * suggestions.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - elements: An associative array containing properties of the region.
+ */
+function template_preprocess_region(&$variables) {
+  // Create the $content variable that templates expect.
+  $variables['content'] = $variables['elements']['#children'];
+  $variables['region'] = $variables['elements']['#region'];
+}
+
+/**
+ * Prepares variables for field templates.
+ *
+ * Default template: field.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: A render element representing the field.
+ *   - attributes: A string containing the attributes for the wrapping div.
+ *   - title_attributes: A string containing the attributes for the title.
+ */
+function template_preprocess_field(&$variables, $hook) {
+  $element = $variables['element'];
+
+  // Creating variables for the template.
+  $variables['entity_type'] = $element['#entity_type'];
+  $variables['field_name'] = $element['#field_name'];
+  $variables['field_type'] = $element['#field_type'];
+  $variables['label_display'] = $element['#label_display'];
+
+  $variables['label_hidden'] = ($element['#label_display'] == 'hidden');
+  // Always set the field label - allow themes to decide whether to display it.
+  // In addition the label should be rendered but hidden to support screen
+  // readers.
+  $variables['label'] = $element['#title'];
+
+  $variables['multiple'] = $element['#is_multiple'];
+
+  static $default_attributes;
+  if (!isset($default_attributes)) {
+    $default_attributes = new Attribute();
+  }
+
+  // Merge attributes when a single-value field has a hidden label.
+  if ($element['#label_display'] == 'hidden' && !$variables['multiple'] && !empty($element['#items'][0]->_attributes)) {
+    $variables['attributes'] = AttributeHelper::mergeCollections($variables['attributes'], (array) $element['#items'][0]->_attributes);
+  }
+
+  // We want other preprocess functions and the theme implementation to have
+  // fast access to the field item render arrays. The item render array keys
+  // (deltas) should always be numerically indexed starting from 0, and looping
+  // on those keys is faster than calling Element::children() or looping on all
+  // keys within $element, since that requires traversal of all element
+  // properties.
+  $variables['items'] = [];
+  $delta = 0;
+  while (!empty($element[$delta])) {
+    $variables['items'][$delta]['content'] = $element[$delta];
+
+    // Modules (e.g., rdf.module) can add field item attributes (to
+    // $item->_attributes) within hook_entity_prepare_view(). Some field
+    // formatters move those attributes into some nested formatter-specific
+    // element in order have them rendered on the desired HTML element (e.g., on
+    // the <a> element of a field item being rendered as a link). Other field
+    // formatters leave them within $element['#items'][$delta]['_attributes'] to
+    // be rendered on the item wrappers provided by field.html.twig.
+    $variables['items'][$delta]['attributes'] = !empty($element['#items'][$delta]->_attributes) ? new Attribute($element['#items'][$delta]->_attributes) : clone($default_attributes);
+    $delta++;
+  }
+}
+
+/**
+ * Prepares variables for individual form element templates.
+ *
+ * Default template: field-multiple-value-form.html.twig.
+ *
+ * Combines multiple values into a table with drag-n-drop reordering.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - element: A render element representing the form element.
+ */
+function template_preprocess_field_multiple_value_form(&$variables) {
+  $element = $variables['element'];
+  $variables['multiple'] = $element['#cardinality_multiple'];
+  $variables['attributes'] = $element['#attributes'];
+
+  if ($variables['multiple']) {
+    $table_id = Html::getUniqueId($element['#field_name'] . '_values');
+    $order_class = $element['#field_name'] . '-delta-order';
+    $header_attributes = new Attribute(['class' => ['label']]);
+    if (!empty($element['#required'])) {
+      $header_attributes['class'][] = 'js-form-required';
+      $header_attributes['class'][] = 'form-required';
+    }
+    $header = [
+      [
+        'data' => [
+          '#prefix' => '<h4' . $header_attributes . '>',
+          '#markup' => $element['#title'],
+          '#suffix' => '</h4>',
+        ],
+        'colspan' => 2,
+        'class' => ['field-label'],
+      ],
+      t('Order', [], ['context' => 'Sort order']),
+    ];
+    $rows = [];
+
+    // Sort items according to '_weight' (needed when the form comes back after
+    // preview or failed validation).
+    $items = [];
+    $variables['button'] = [];
+    foreach (Element::children($element) as $key) {
+      if ($key === 'add_more') {
+        $variables['button'] = &$element[$key];
+      }
+      else {
+        $items[] = &$element[$key];
+      }
+    }
+    usort($items, '_field_multiple_value_form_sort_helper');
+
+    // Add the items as table rows.
+    foreach ($items as $item) {
+      $item['_weight']['#attributes']['class'] = [$order_class];
+
+      // Remove weight form element from item render array so it can be rendered
+      // in a separate table column.
+      $delta_element = $item['_weight'];
+      unset($item['_weight']);
+
+      $cells = [
+        ['data' => '', 'class' => ['field-multiple-drag']],
+        ['data' => $item],
+        ['data' => $delta_element, 'class' => ['delta-order']],
+      ];
+      $rows[] = [
+        'data' => $cells,
+        'class' => ['draggable'],
+      ];
+    }
+
+    $variables['table'] = [
+      '#type' => 'table',
+      '#header' => $header,
+      '#rows' => $rows,
+      '#attributes' => [
+        'id' => $table_id,
+        'class' => ['field-multiple-table'],
+      ],
+      '#tabledrag' => [
+        [
+          'action' => 'order',
+          'relationship' => 'sibling',
+          'group' => $order_class,
+        ],
+      ],
+    ];
+
+    if (!empty($element['#description'])) {
+      $description_id = $element['#attributes']['aria-describedby'];
+      $description_attributes['id'] = $description_id;
+      $variables['description']['attributes'] = new Attribute($description_attributes);
+      $variables['description']['content'] = $element['#description'];
+
+      // Add the description's id to the table aria attributes.
+      $variables['table']['#attributes']['aria-describedby'] = $element['#attributes']['aria-describedby'];
+    }
+  }
+  else {
+    $variables['elements'] = [];
+    foreach (Element::children($element) as $key) {
+      $variables['elements'][] = $element[$key];
+    }
+  }
+}
+
+/**
+ * Prepares variables for breadcrumb templates.
+ *
+ * Default template: breadcrumb.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - links: A list of \Drupal\Core\Link objects which should be rendered.
+ */
+function template_preprocess_breadcrumb(&$variables) {
+  $variables['breadcrumb'] = [];
+  /** @var \Drupal\Core\Link $link */
+  foreach ($variables['links'] as $key => $link) {
+    $variables['breadcrumb'][$key] = ['text' => $link->getText(), 'url' => $link->getUrl()->toString()];
+  }
+}
+
+/**
+ * Callback for usort() within template_preprocess_field_multiple_value_form().
+ *
+ * Sorts using ['_weight']['#value']
+ */
+function _field_multiple_value_form_sort_helper($a, $b) {
+  $a_weight = (is_array($a) && isset($a['_weight']['#value']) ? $a['_weight']['#value'] : 0);
+  $b_weight = (is_array($b) && isset($b['_weight']['#value']) ? $b['_weight']['#value'] : 0);
+  return $a_weight - $b_weight;
+}
+
+/**
+ * Provides theme registration for themes across .inc files.
+ */
+function drupal_common_theme() {
+  return [
+    // From theme.inc.
+    'html' => [
+      'render element' => 'html',
+    ],
+    'page' => [
+      'render element' => 'page',
+    ],
+    'page_title' => [
+      'variables' => ['title' => NULL],
+    ],
+    'region' => [
+      'render element' => 'elements',
+    ],
+    'time' => [
+      'variables' => ['timestamp' => NULL, 'text' => NULL, 'attributes' => []],
+    ],
+    'datetime_form' => [
+      'render element' => 'element',
+    ],
+    'datetime_wrapper' => [
+      'render element' => 'element',
+    ],
+    'status_messages' => [
+      'variables' => ['status_headings' => [], 'message_list' => NULL],
+    ],
+    'links' => [
+      'variables' => ['links' => [], 'attributes' => ['class' => ['links']], 'heading' => [], 'set_active_class' => FALSE],
+    ],
+    'dropbutton_wrapper' => [
+      'variables' => ['children' => NULL],
+    ],
+    'image' => [
+      // HTML 4 and XHTML 1.0 always require an alt attribute. The HTML 5 draft
+      // allows the alt attribute to be omitted in some cases. Therefore,
+      // default the alt attribute to an empty string, but allow code providing
+      // variables to image.html.twig templates to pass explicit NULL for it to
+      // be omitted. Usually, neither omission nor an empty string satisfies
+      // accessibility requirements, so it is strongly encouraged for code
+      // building variables for image.html.twig templates to pass a meaningful
+      // value for the alt variable.
+      // - http://www.w3.org/TR/REC-html40/struct/objects.html#h-13.8
+      // - http://www.w3.org/TR/xhtml1/dtds.html
+      // - http://dev.w3.org/html5/spec/Overview.html#alt
+      // The title attribute is optional in all cases, so it is omitted by
+      // default.
+      'variables' => ['uri' => NULL, 'width' => NULL, 'height' => NULL, 'alt' => '', 'title' => NULL, 'attributes' => [], 'sizes' => NULL, 'srcset' => [], 'style_name' => NULL],
+    ],
+    'breadcrumb' => [
+      'variables' => ['links' => []],
+    ],
+    'table' => [
+      'variables' => ['header' => NULL, 'rows' => NULL, 'footer' => NULL, 'attributes' => [], 'caption' => NULL, 'colgroups' => [], 'sticky' => FALSE, 'responsive' => TRUE, 'empty' => ''],
+    ],
+    'tablesort_indicator' => [
+      'variables' => ['style' => NULL],
+    ],
+    'mark' => [
+      'variables' => ['status' => MARK_NEW],
+    ],
+    'item_list' => [
+      'variables' => ['items' => [], 'title' => '', 'list_type' => 'ul', 'wrapper_attributes' => [], 'attributes' => [], 'empty' => NULL, 'context' => []],
+    ],
+    'feed_icon' => [
+      'variables' => ['url' => NULL, 'title' => NULL],
+    ],
+    'progress_bar' => [
+      'variables' => ['label' => NULL, 'percent' => NULL, 'message' => NULL],
+    ],
+    'indentation' => [
+      'variables' => ['size' => 1],
+    ],
+    // From theme.maintenance.inc.
+    'maintenance_page' => [
+      'render element' => 'page',
+    ],
+    'install_page' => [
+      'render element' => 'page',
+    ],
+    'maintenance_task_list' => [
+      'variables' => ['items' => NULL, 'active' => NULL, 'variant' => NULL],
+    ],
+    'authorize_report' => [
+      'variables' => ['messages' => [], 'attributes' => []],
+      'includes' => ['core/includes/theme.maintenance.inc'],
+      'template' => 'authorize-report',
+    ],
+    // From pager.inc.
+    'pager' => [
+      'render element' => 'pager',
+    ],
+    // From menu.inc.
+    'menu' => [
+      'variables' => ['menu_name' => NULL, 'items' => [], 'attributes' => []],
+    ],
+    'menu_local_task' => [
+      'render element' => 'element',
+    ],
+    'menu_local_action' => [
+      'render element' => 'element',
+    ],
+    'menu_local_tasks' => [
+      'variables' => ['primary' => [], 'secondary' => []],
+    ],
+    // From form.inc.
+    'input' => [
+      'render element' => 'element',
+    ],
+    'select' => [
+      'render element' => 'element',
+    ],
+    'fieldset' => [
+      'render element' => 'element',
+    ],
+    'details' => [
+      'render element' => 'element',
+    ],
+    'radios' => [
+      'render element' => 'element',
+    ],
+    'checkboxes' => [
+      'render element' => 'element',
+    ],
+    'form' => [
+      'render element' => 'element',
+    ],
+    'textarea' => [
+      'render element' => 'element',
+    ],
+    'form_element' => [
+      'render element' => 'element',
+    ],
+    'form_element_label' => [
+      'render element' => 'element',
+    ],
+    'vertical_tabs' => [
+      'render element' => 'element',
+    ],
+    'container' => [
+      'render element' => 'element',
+    ],
+    // From field system.
+    'field' => [
+      'render element' => 'element',
+    ],
+    'field_multiple_value_form' => [
+      'render element' => 'element',
+    ],
+  ];
+}

+ 137 - 0
web/core/includes/theme.maintenance.inc

@@ -0,0 +1,137 @@
+<?php
+
+/**
+ * @file
+ * Theming for maintenance pages.
+ */
+
+use Drupal\Core\Installer\InstallerKernel;
+use Drupal\Core\Site\Settings;
+
+/**
+ * Sets up the theming system for maintenance page.
+ *
+ * Used for site installs, updates and when the site is in maintenance mode.
+ * It also applies when the database is unavailable or bootstrap was not
+ * complete. Seven is always used for the initial install and update
+ * operations. In other cases, Bartik is used, but this can be overridden by
+ * setting a "maintenance_theme" key in the $settings variable in settings.php.
+ */
+function _drupal_maintenance_theme() {
+  // If the theme is already set, assume the others are set too, and do nothing.
+  if (\Drupal::theme()->hasActiveTheme()) {
+    return;
+  }
+
+  require_once __DIR__ . '/theme.inc';
+  require_once __DIR__ . '/common.inc';
+  require_once __DIR__ . '/unicode.inc';
+  require_once __DIR__ . '/file.inc';
+  require_once __DIR__ . '/module.inc';
+  require_once __DIR__ . '/database.inc';
+
+  // Install and update pages are treated differently to prevent theming overrides.
+  if (defined('MAINTENANCE_MODE') && (MAINTENANCE_MODE == 'install' || MAINTENANCE_MODE == 'update')) {
+    if (InstallerKernel::installationAttempted()) {
+      $custom_theme = $GLOBALS['install_state']['theme'];
+    }
+    else {
+      $custom_theme = Settings::get('maintenance_theme', 'seven');
+    }
+  }
+  else {
+    // Use the maintenance theme if specified, otherwise attempt to use the
+    // default site theme.
+    try {
+      $custom_theme = Settings::get('maintenance_theme', '');
+      if (!$custom_theme) {
+        $config = \Drupal::config('system.theme');
+        $custom_theme = $config->get('default');
+      }
+    }
+    catch (\Exception $e) {
+      // Whatever went wrong (often a database connection problem), we are
+      // about to fall back to a sensible theme so there is no need for special
+      // handling.
+    }
+    if (!$custom_theme) {
+      // We have been unable to identify the configured theme, so fall back to
+      // a safe default. Bartik is reasonably user friendly and fairly generic.
+      $custom_theme = 'bartik';
+    }
+  }
+
+  $themes = \Drupal::service('theme_handler')->listInfo();
+
+  // If no themes are installed yet, or if the requested custom theme is not
+  // installed, retrieve all available themes.
+  /** @var \Drupal\Core\Theme\ThemeInitialization $theme_init */
+  $theme_init = \Drupal::service('theme.initialization');
+  $theme_handler = \Drupal::service('theme_handler');
+  if (empty($themes) || !isset($themes[$custom_theme])) {
+    $themes = \Drupal::service('extension.list.theme')->getList();
+    $theme_handler->addTheme($themes[$custom_theme]);
+  }
+
+  // \Drupal\Core\Extension\ThemeHandlerInterface::listInfo() triggers a
+  // \Drupal\Core\Extension\ModuleHandler::alter() in maintenance mode, but we
+  // can't let themes alter the .info.yml data until we know a theme's base
+  // themes. So don't set active theme until after
+  // \Drupal\Core\Extension\ThemeHandlerInterface::listInfo() builds its cache.
+  $theme = $custom_theme;
+
+  // Find all our ancestor themes and put them in an array.
+  // @todo This is just a workaround. Find a better way how to handle themes
+  //   on maintenance pages, see https://www.drupal.org/node/2322619.
+  // This code is basically a duplicate of
+  // \Drupal\Core\Theme\ThemeInitialization::getActiveThemeByName.
+  $base_themes = [];
+  $ancestor = $theme;
+  while ($ancestor && isset($themes[$ancestor]->base_theme)) {
+    $base_themes[] = $themes[$themes[$ancestor]->base_theme];
+    $ancestor = $themes[$ancestor]->base_theme;
+    if ($ancestor) {
+      // Ensure that the base theme is added and installed.
+      $theme_handler->addTheme($themes[$ancestor]);
+    }
+  }
+  \Drupal::theme()->setActiveTheme($theme_init->getActiveTheme($themes[$custom_theme], $base_themes));
+  // Prime the theme registry.
+  Drupal::service('theme.registry');
+}
+
+/**
+ * Prepares variables for authorize.php operation report templates.
+ *
+ * This report displays the results of an operation run via authorize.php.
+ *
+ * Default template: authorize-report.html.twig.
+ *
+ * @param array $variables
+ *   An associative array containing:
+ *   - messages: An array of result messages.
+ */
+function template_preprocess_authorize_report(&$variables) {
+  $messages = [];
+  if (!empty($variables['messages'])) {
+    foreach ($variables['messages'] as $heading => $logs) {
+      $items = [];
+      foreach ($logs as $number => $log_message) {
+        if ($number === '#abort') {
+          continue;
+        }
+        $class = 'authorize-results__' . ($log_message['success'] ? 'success' : 'failure');
+        $items[] = [
+          '#wrapper_attributes' => ['class' => [$class]],
+          '#markup' => $log_message['message'],
+        ];
+      }
+      $messages[] = [
+        '#theme' => 'item_list',
+        '#items' => $items,
+        '#title' => $heading,
+      ];
+    }
+  }
+  $variables['messages'] = $messages;
+}

+ 126 - 0
web/core/includes/unicode.inc

@@ -0,0 +1,126 @@
+<?php
+
+/**
+ * @file
+ * Provides Unicode-related conversions and operations.
+ */
+
+use Drupal\Component\Utility\Unicode;
+
+/**
+ * Returns Unicode library status and errors.
+ */
+
+/**
+ * Moves unicode_requirements() logic to system_requirements().
+ *
+ * @deprecated in drupal:8.4.0 and is removed from drupal:9.0.0.
+ *
+ * @see https://www.drupal.org/node/2884698
+ */
+function unicode_requirements() {
+  @trigger_error('unicode_requirements() is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. There is no replacement; system_requirements() now includes the logic instead. See https://www.drupal.org/node/2884698', E_USER_DEPRECATED);
+
+  $libraries = [
+    Unicode::STATUS_SINGLEBYTE => t('Standard PHP'),
+    Unicode::STATUS_MULTIBYTE => t('PHP Mbstring Extension'),
+    Unicode::STATUS_ERROR => t('Error'),
+  ];
+  $severities = [
+    Unicode::STATUS_SINGLEBYTE => REQUIREMENT_WARNING,
+    Unicode::STATUS_MULTIBYTE => NULL,
+    Unicode::STATUS_ERROR => REQUIREMENT_ERROR,
+  ];
+  $failed_check = Unicode::check();
+  $library = Unicode::getStatus();
+
+  $requirements['unicode'] = [
+    'title' => t('Unicode library'),
+    'value' => $libraries[$library],
+    'severity' => $severities[$library],
+  ];
+  switch ($failed_check) {
+    case 'mb_strlen':
+      $requirements['unicode']['description'] = t('Operations on Unicode strings are emulated on a best-effort basis. Install the <a href="http://php.net/mbstring">PHP mbstring extension</a> for improved Unicode support.');
+      break;
+
+    case 'mbstring.func_overload':
+      $requirements['unicode']['description'] = t('Multibyte string function overloading in PHP is active and must be disabled. Check the php.ini <em>mbstring.func_overload</em> setting. Please refer to the <a href="http://php.net/mbstring">PHP mbstring documentation</a> for more information.');
+      break;
+
+    case 'mbstring.encoding_translation':
+      $requirements['unicode']['description'] = t('Multibyte string input conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.encoding_translation</em> setting. Please refer to the <a href="http://php.net/mbstring">PHP mbstring documentation</a> for more information.');
+      break;
+
+    case 'mbstring.http_input':
+      $requirements['unicode']['description'] = t('Multibyte string input conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.http_input</em> setting. Please refer to the <a href="http://php.net/mbstring">PHP mbstring documentation</a> for more information.');
+      break;
+
+    case 'mbstring.http_output':
+      $requirements['unicode']['description'] = t('Multibyte string output conversion in PHP is active and must be disabled. Check the php.ini <em>mbstring.http_output</em> setting. Please refer to the <a href="http://php.net/mbstring">PHP mbstring documentation</a> for more information.');
+      break;
+  }
+
+  return $requirements;
+}
+
+/**
+ * Prepares a new XML parser.
+ *
+ * This is a wrapper around xml_parser_create() which extracts the encoding
+ * from the XML data first and sets the output encoding to UTF-8. This function
+ * should be used instead of xml_parser_create(), because PHP 4's XML parser
+ * doesn't check the input encoding itself. "Starting from PHP 5, the input
+ * encoding is automatically detected, so that the encoding parameter specifies
+ * only the output encoding."
+ *
+ * This is also where unsupported encodings will be converted. Callers should
+ * take this into account: $data might have been changed after the call.
+ *
+ * @param $data
+ *   The XML data which will be parsed later.
+ *
+ * @return
+ *   An XML parser object or FALSE on error.
+ *
+ * @ingroup php_wrappers
+ *
+ * @deprecated in drupal:8.3.0 and is removed from drupal:9.0.0. Use
+ *   xml_parser_create() and
+ *   xml_parser_set_option($xml_parser, XML_OPTION_TARGET_ENCODING, 'utf-8')
+ *   instead.
+ */
+function drupal_xml_parser_create(&$data) {
+  // Default XML encoding is UTF-8
+  $encoding = 'utf-8';
+  $bom = FALSE;
+
+  // Check for UTF-8 byte order mark (PHP5's XML parser doesn't handle it).
+  if (!strncmp($data, "\xEF\xBB\xBF", 3)) {
+    $bom = TRUE;
+    $data = substr($data, 3);
+  }
+
+  // Check for an encoding declaration in the XML prolog if no BOM was found.
+  if (!$bom && preg_match('/^<\?xml[^>]+encoding="(.+?)"/', $data, $match)) {
+    $encoding = $match[1];
+  }
+
+  // Unsupported encodings are converted here into UTF-8.
+  $php_supported = ['utf-8', 'iso-8859-1', 'us-ascii'];
+  if (!in_array(strtolower($encoding), $php_supported)) {
+    $out = Unicode::convertToUtf8($data, $encoding);
+    if ($out !== FALSE) {
+      $encoding = 'utf-8';
+      $data = preg_replace('/^(<\?xml[^>]+encoding)="(.+?)"/', '\\1="utf-8"', $out);
+    }
+    else {
+      \Drupal::logger('php')->warning('Could not convert XML encoding %s to UTF-8.', ['%s' => $encoding]);
+      return FALSE;
+    }
+  }
+
+  $xml_parser = xml_parser_create($encoding);
+  xml_parser_set_option($xml_parser, XML_OPTION_TARGET_ENCODING, 'utf-8');
+  return $xml_parser;
+}

+ 786 - 0
web/core/includes/update.inc

@@ -0,0 +1,786 @@
+<?php
+
+/**
+ * @file
+ * Drupal database update API.
+ *
+ * This file contains functions to perform database updates for a Drupal
+ * installation. It is included and used extensively by update.php.
+ */
+
+use Drupal\Component\Graph\Graph;
+use Drupal\Core\Extension\Exception\UnknownExtensionException;
+use Drupal\Core\Update\UpdateKernel;
+use Drupal\Core\Utility\Error;
+
+/**
+ * Disables any extensions that are incompatible with the current core version.
+ *
+ * @deprecated in Drupal 8.8.5 and is removed from Drupal 9.0.0.
+ *
+ * @see https://www.drupal.org/node/3026100
+ */
+function update_fix_compatibility() {
+  @trigger_error(__FUNCTION__ . '() is deprecated in Drupal 8.8.5 and will be removed before Drupal 9.0.0. There is no replacement. See https://www.drupal.org/node/3026100', E_USER_DEPRECATED);
+  // Fix extension objects if the update is being done via Drush 8. In non-Drush
+  // environments this will already be fixed by the UpdateKernel this point.
+  UpdateKernel::fixSerializedExtensionObjects(\Drupal::getContainer());
+
+  $extension_config = \Drupal::configFactory()->getEditable('core.extension');
+  $save = FALSE;
+  foreach (['module', 'theme'] as $type) {
+    foreach ($extension_config->get($type) as $name => $weight) {
+      if (update_check_incompatibility($name, $type)) {
+        $extension_config->clear("$type.$name");
+        $save = TRUE;
+      }
+    }
+  }
+  if ($save) {
+    $extension_config->set('module', module_config_sort($extension_config->get('module')));
+    $extension_config->save();
+  }
+}
+
+/**
+ * Tests the compatibility of a module or theme.
+ */
+function update_check_incompatibility($name, $type = 'module') {
+  static $themes, $modules;
+
+  // Store values of expensive functions for future use.
+  if (empty($themes) || empty($modules)) {
+    // We need to do a full rebuild here to make sure the database reflects any
+    // code changes that were made in the filesystem before the update script
+    // was initiated.
+    $themes = \Drupal::service('theme_handler')->rebuildThemeData();
+    $modules = \Drupal::service('extension.list.module')->reset()->getList();
+  }
+
+  if ($type == 'module' && isset($modules[$name])) {
+    $file = $modules[$name];
+  }
+  elseif ($type == 'theme' && isset($themes[$name])) {
+    $file = $themes[$name];
+  }
+  if (!isset($file)
+      || $file->info['core_incompatible']
+      || version_compare(phpversion(), $file->info['php']) < 0) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * Returns whether the minimum schema requirement has been satisfied.
+ *
+ * @return array
+ *   A requirements info array.
+ */
+function update_system_schema_requirements() {
+  $requirements = [];
+
+  $system_schema = drupal_get_installed_schema_version('system');
+
+  $requirements['minimum schema']['title'] = 'Minimum schema version';
+  if ($system_schema >= \Drupal::CORE_MINIMUM_SCHEMA_VERSION) {
+    $requirements['minimum schema'] += [
+      'value' => 'The installed schema version meets the minimum.',
+      'description' => 'Schema version: ' . $system_schema,
+    ];
+  }
+  else {
+    $requirements['minimum schema'] += [
+      'value' => 'The installed schema version does not meet the minimum.',
+      'severity' => REQUIREMENT_ERROR,
+      'description' => 'Your system schema version is ' . $system_schema . '. Updating directly from a schema version prior to 8000 is not supported. You must upgrade your site to Drupal 8 first, see https://www.drupal.org/docs/8/upgrade.',
+    ];
+  }
+
+  return $requirements;
+}
+
+/**
+ * Checks update requirements and reports errors and (optionally) warnings.
+ */
+function update_check_requirements() {
+  // Because this is one of the earliest points in the update process,
+  // detect and fix missing schema versions for modules here to ensure
+  // it runs on all update code paths.
+  _update_fix_missing_schema();
+
+  // Check requirements of all loaded modules.
+  $requirements = \Drupal::moduleHandler()->invokeAll('requirements', ['update']);
+  $requirements += update_system_schema_requirements();
+  return $requirements;
+}
+
+/**
+ * Helper to detect and fix 'missing' schema information.
+ *
+ * Repairs the case where a module has no schema version recorded.
+ * This has to be done prior to updates being run, otherwise the update
+ * system would detect and attempt to run all historical updates for a
+ * module.
+ *
+ * @todo: remove in a major version after
+ * https://www.drupal.org/project/drupal/issues/3130037 has been fixed.
+ */
+function _update_fix_missing_schema() {
+  $versions = \Drupal::keyValue('system.schema')->getAll();
+  $module_handler = \Drupal::moduleHandler();
+  $enabled_modules = $module_handler->getModuleList();
+
+  foreach (array_keys($enabled_modules) as $module) {
+    // All modules should have a recorded schema version, but when they
+    // don't, detect and fix the problem.
+    if (!isset($versions[$module])) {
+      // Ensure the .install file is loaded.
+      module_load_install($module);
+      $all_updates = drupal_get_schema_versions($module);
+      // If the schema version of a module hasn't been recorded, we cannot
+      // know the actual schema version a module is at, because
+      // no updates will ever have been run on the site and it was not set
+      // correctly when the module was installed, so instead set it to
+      // the same as the last update. This means that updates will proceed
+      // again the next time the module is updated and a new update is
+      // added. Updates added in between the module being installed and the
+      // schema version being fixed here (if any have been added) will never
+      // be run, but we have no way to identify which updates these are.
+      if ($all_updates) {
+        $last_update = max($all_updates);
+      }
+      else {
+        $last_update = \Drupal::CORE_MINIMUM_SCHEMA_VERSION;
+      }
+      // If the module implements hook_update_last_removed() use the
+      // value of that if it's higher than the schema versions found so
+      // far.
+      if ($last_removed = $module_handler->invoke($module, 'update_last_removed')) {
+        $last_update = max($last_update, $last_removed);
+      }
+      drupal_set_installed_schema_version($module, $last_update);
+      $args = ['%module' => $module, '%last_update_hook' => $module . '_update_' . $last_update . '()'];
+      \Drupal::messenger()->addWarning(t('Schema information for module %module was missing from the database. You should manually review the module updates and your database to check if any updates have been skipped up to, and including, %last_update_hook.', $args));
+      \Drupal::logger('update')->warning('Schema information for module %module was missing from the database. You should manually review the module updates and your database to check if any updates have been skipped up to, and including, %last_update_hook.', $args);
+    }
+  }
+}
+
+/**
+ * Forces a module to a given schema version.
+ *
+ * This function is rarely necessary.
+ *
+ * @param string $module
+ *   Name of the module.
+ * @param string $schema_version
+ *   The schema version the module should be set to.
+ */
+function update_set_schema($module, $schema_version) {
+  \Drupal::keyValue('system.schema')->set($module, $schema_version);
+  \Drupal::service('extension.list.profile')->reset();
+  \Drupal::service('extension.list.module')->reset();
+  \Drupal::service('extension.list.theme_engine')->reset();
+  \Drupal::service('extension.list.theme')->reset();
+  drupal_static_reset('drupal_get_installed_schema_version');
+}
+
+/**
+ * Implements callback_batch_operation().
+ *
+ * Performs one update and stores the results for display on the results page.
+ *
+ * If an update function completes successfully, it should return a message
+ * as a string indicating success, for example:
+ * @code
+ * return t('New index added successfully.');
+ * @endcode
+ *
+ * Alternatively, it may return nothing. In that case, no message
+ * will be displayed at all.
+ *
+ * If it fails for whatever reason, it should throw an instance of
+ * Drupal\Core\Utility\UpdateException with an appropriate error message, for
+ * example:
+ * @code
+ * use Drupal\Core\Utility\UpdateException;
+ * throw new UpdateException('Description of what went wrong');
+ * @endcode
+ *
+ * If an exception is thrown, the current update and all updates that depend on
+ * it will be aborted. The schema version will not be updated in this case, and
+ * all the aborted updates will continue to appear on update.php as updates
+ * that have not yet been run.
+ *
+ * If an update function needs to be re-run as part of a batch process, it
+ * should accept the $sandbox array by reference as its first parameter
+ * and set the #finished property to the percentage completed that it is, as a
+ * fraction of 1.
+ *
+ * @param $module
+ *   The module whose update will be run.
+ * @param $number
+ *   The update number to run.
+ * @param $dependency_map
+ *   An array whose keys are the names of all update functions that will be
+ *   performed during this batch process, and whose values are arrays of other
+ *   update functions that each one depends on.
+ * @param $context
+ *   The batch context array.
+ *
+ * @see update_resolve_dependencies()
+ */
+function update_do_one($module, $number, $dependency_map, &$context) {
+  $function = $module . '_update_' . $number;
+
+  // If this update was aborted in a previous step, or has a dependency that
+  // was aborted in a previous step, go no further.
+  if (!empty($context['results']['#abort']) && array_intersect($context['results']['#abort'], array_merge($dependency_map, [$function]))) {
+    return;
+  }
+
+  $ret = [];
+  if (function_exists($function)) {
+    try {
+      $ret['results']['query'] = $function($context['sandbox']);
+      $ret['results']['success'] = TRUE;
+    }
+    // @TODO We may want to do different error handling for different
+    // exception types, but for now we'll just log the exception and
+    // return the message for printing.
+    // @see https://www.drupal.org/node/2564311
+    catch (Exception $e) {
+      watchdog_exception('update', $e);
+
+      $variables = Error::decodeException($e);
+      unset($variables['backtrace']);
+      $ret['#abort'] = ['success' => FALSE, 'query' => t('%type: @message in %function (line %line of %file).', $variables)];
+    }
+  }
+
+  if (isset($context['sandbox']['#finished'])) {
+    $context['finished'] = $context['sandbox']['#finished'];
+    unset($context['sandbox']['#finished']);
+  }
+
+  if (!isset($context['results'][$module])) {
+    $context['results'][$module] = [];
+  }
+  if (!isset($context['results'][$module][$number])) {
+    $context['results'][$module][$number] = [];
+  }
+  $context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret);
+
+  if (!empty($ret['#abort'])) {
+    // Record this function in the list of updates that were aborted.
+    $context['results']['#abort'][] = $function;
+  }
+
+  // Record the schema update if it was completed successfully.
+  if ($context['finished'] == 1 && empty($ret['#abort'])) {
+    drupal_set_installed_schema_version($module, $number);
+  }
+
+  $context['message'] = t('Updating @module', ['@module' => $module]);
+}
+
+/**
+ * Executes a single hook_post_update_NAME().
+ *
+ * @param string $function
+ *   The function name, that should be executed.
+ * @param array $context
+ *   The batch context array.
+ */
+function update_invoke_post_update($function, &$context) {
+  $ret = [];
+
+  // If this update was aborted in a previous step, or has a dependency that was
+  // aborted in a previous step, go no further.
+  if (!empty($context['results']['#abort'])) {
+    return;
+  }
+
+  list($module, $name) = explode('_post_update_', $function, 2);
+  module_load_include('php', $module, $module . '.post_update');
+  if (function_exists($function)) {
+    try {
+      $ret['results']['query'] = $function($context['sandbox']);
+      $ret['results']['success'] = TRUE;
+
+      if (!isset($context['sandbox']['#finished']) || (isset($context['sandbox']['#finished']) && $context['sandbox']['#finished'] >= 1)) {
+        \Drupal::service('update.post_update_registry')->registerInvokedUpdates([$function]);
+      }
+    }
+    // @TODO We may want to do different error handling for different exception
+    // types, but for now we'll just log the exception and return the message
+    // for printing.
+    // @see https://www.drupal.org/node/2564311
+    catch (Exception $e) {
+      watchdog_exception('update', $e);
+
+      $variables = Error::decodeException($e);
+      unset($variables['backtrace']);
+      $ret['#abort'] = [
+        'success' => FALSE,
+        'query' => t('%type: @message in %function (line %line of %file).', $variables),
+      ];
+    }
+  }
+
+  if (isset($context['sandbox']['#finished'])) {
+    $context['finished'] = $context['sandbox']['#finished'];
+    unset($context['sandbox']['#finished']);
+  }
+  if (!isset($context['results'][$module][$name])) {
+    $context['results'][$module][$name] = [];
+  }
+  $context['results'][$module][$name] = array_merge($context['results'][$module][$name], $ret);
+
+  if (!empty($ret['#abort'])) {
+    // Record this function in the list of updates that were aborted.
+    $context['results']['#abort'][] = $function;
+  }
+
+  $context['message'] = t('Post updating @module', ['@module' => $module]);
+}
+
+/**
+ * Returns a list of all the pending database updates.
+ *
+ * @return
+ *   An associative array keyed by module name which contains all information
+ *   about database updates that need to be run, and any updates that are not
+ *   going to proceed due to missing requirements. The system module will
+ *   always be listed first.
+ *
+ *   The subarray for each module can contain the following keys:
+ *   - start: The starting update that is to be processed. If this does not
+ *       exist then do not process any updates for this module as there are
+ *       other requirements that need to be resolved.
+ *   - warning: Any warnings about why this module can not be updated.
+ *   - pending: An array of all the pending updates for the module including
+ *       the update number and the description from source code comment for
+ *       each update function. This array is keyed by the update number.
+ */
+function update_get_update_list() {
+  // Make sure that the system module is first in the list of updates.
+  $ret = ['system' => []];
+
+  $modules = drupal_get_installed_schema_version(NULL, FALSE, TRUE);
+  /** @var \Drupal\Core\Extension\ExtensionList $extension_list */
+  $extension_list = \Drupal::service('extension.list.module');
+  /** @var array $installed_module_info */
+  $installed_module_info = $extension_list->getAllInstalledInfo();
+  foreach ($modules as $module => $schema_version) {
+    // Skip uninstalled and incompatible modules.
+    try {
+      if ($schema_version == SCHEMA_UNINSTALLED || $extension_list->checkIncompatibility($module)) {
+        continue;
+      }
+    }
+    // It is possible that the system schema has orphaned entries, so the
+    // incompatibility checking might throw an exception.
+    catch (UnknownExtensionException $e) {
+      $args = [
+        '%name' => $module,
+        ':url' => 'https://www.drupal.org/node/3137656',
+      ];
+      \Drupal::messenger()->addWarning(t('Module %name has an entry in the system.schema key/value storage, but is missing from your site. <a href=":url">More information about this error</a>.', $args));
+      \Drupal::logger('system')->notice('Module %name has an entry in the system.schema key/value storage, but is missing from your site. <a href=":url">More information about this error</a>.', $args);
+      continue;
+    }
+    // There might be orphaned entries for modules that are in the filesystem
+    // but not installed. Also skip those, but warn site admins about it.
+    if (empty($installed_module_info[$module])) {
+      $args = [
+        '%name' => $module,
+        ':url' => 'https://www.drupal.org/node/3137656',
+      ];
+      \Drupal::messenger()->addWarning(t('Module %name has an entry in the system.schema key/value storage, but is not installed. <a href=":url">More information about this error</a>.', $args));
+      \Drupal::logger('system')->notice('Module %name has an entry in the system.schema key/value storage, but is not installed. <a href=":url">More information about this error</a>.', $args);
+      continue;
+    }
+
+    // Display a requirements error if the user somehow has a schema version
+    // from the previous Drupal major version.
+    if ($schema_version < \Drupal::CORE_MINIMUM_SCHEMA_VERSION) {
+      $ret[$module]['warning'] = '<em>' . $module . '</em> module cannot be updated. Its schema version is ' . $schema_version . ', which is from an earlier major release of Drupal. You will need to <a href="https://www.drupal.org/node/2127611">migrate the data for this module</a> instead.';
+      continue;
+    }
+    // Otherwise, get the list of updates defined by this module.
+    $updates = drupal_get_schema_versions($module);
+    if ($updates !== FALSE) {
+      foreach ($updates as $update) {
+        if ($update == \Drupal::CORE_MINIMUM_SCHEMA_VERSION) {
+          $ret[$module]['warning'] = '<em>' . $module . '</em> module cannot be updated. It contains an update numbered as ' . \Drupal::CORE_MINIMUM_SCHEMA_VERSION . ' which is reserved for the earliest installation of a module in Drupal ' . \Drupal::CORE_COMPATIBILITY . ', before any updates. In order to update <em>' . $module . '</em> module, you will need to install a version of the module with valid updates.';
+          continue 2;
+        }
+        if ($update > $schema_version) {
+          // The description for an update comes from its Doxygen.
+          $func = new ReflectionFunction($module . '_update_' . $update);
+          $description = str_replace(["\n", '*', '/'], '', $func->getDocComment());
+          $ret[$module]['pending'][$update] = "$update - $description";
+          if (!isset($ret[$module]['start'])) {
+            $ret[$module]['start'] = $update;
+          }
+        }
+      }
+      if (!isset($ret[$module]['start']) && isset($ret[$module]['pending'])) {
+        $ret[$module]['start'] = $schema_version;
+      }
+    }
+  }
+
+  if (empty($ret['system'])) {
+    unset($ret['system']);
+  }
+  return $ret;
+}
+
+/**
+ * Resolves dependencies in a set of module updates, and orders them correctly.
+ *
+ * This function receives a list of requested module updates and determines an
+ * appropriate order to run them in such that all update dependencies are met.
+ * Any updates whose dependencies cannot be met are included in the returned
+ * array but have the key 'allowed' set to FALSE; the calling function should
+ * take responsibility for ensuring that these updates are ultimately not
+ * performed.
+ *
+ * In addition, the returned array also includes detailed information about the
+ * dependency chain for each update, as provided by the depth-first search
+ * algorithm in Drupal\Component\Graph\Graph::searchAndSort().
+ *
+ * @param $starting_updates
+ *   An array whose keys contain the names of modules with updates to be run
+ *   and whose values contain the number of the first requested update for that
+ *   module.
+ *
+ * @return
+ *   An array whose keys are the names of all update functions within the
+ *   provided modules that would need to be run in order to fulfill the
+ *   request, arranged in the order in which the update functions should be
+ *   run. (This includes the provided starting update for each module and all
+ *   subsequent updates that are available.) The values are themselves arrays
+ *   containing all the keys provided by the
+ *   Drupal\Component\Graph\Graph::searchAndSort() algorithm, which encode
+ *   detailed information about the dependency chain for this update function
+ *   (for example: 'paths', 'reverse_paths', 'weight', and 'component'), as
+ *   well as the following additional keys:
+ *   - 'allowed': A boolean which is TRUE when the update function's
+ *     dependencies are met, and FALSE otherwise. Calling functions should
+ *     inspect this value before running the update.
+ *   - 'missing_dependencies': An array containing the names of any other
+ *     update functions that are required by this one but that are unavailable
+ *     to be run. This array will be empty when 'allowed' is TRUE.
+ *   - 'module': The name of the module that this update function belongs to.
+ *   - 'number': The number of this update function within that module.
+ *
+ * @see \Drupal\Component\Graph\Graph::searchAndSort()
+ */
+function update_resolve_dependencies($starting_updates) {
+  // Obtain a dependency graph for the requested update functions.
+  $update_functions = update_get_update_function_list($starting_updates);
+  $graph = update_build_dependency_graph($update_functions);
+
+  // Perform the depth-first search and sort on the results.
+  $graph_object = new Graph($graph);
+  $graph = $graph_object->searchAndSort();
+  uasort($graph, ['Drupal\Component\Utility\SortArray', 'sortByWeightElement']);
+
+  foreach ($graph as $function => &$data) {
+    $module = $data['module'];
+    $number = $data['number'];
+    // If the update function is missing and has not yet been performed, mark
+    // it and everything that ultimately depends on it as disallowed.
+    if (update_is_missing($module, $number, $update_functions) && !update_already_performed($module, $number)) {
+      $data['allowed'] = FALSE;
+      foreach (array_keys($data['paths']) as $dependent) {
+        $graph[$dependent]['allowed'] = FALSE;
+        $graph[$dependent]['missing_dependencies'][] = $function;
+      }
+    }
+    elseif (!isset($data['allowed'])) {
+      $data['allowed'] = TRUE;
+      $data['missing_dependencies'] = [];
+    }
+    // Now that we have finished processing this function, remove it from the
+    // graph if it was not part of the original list. This ensures that we
+    // never try to run any updates that were not specifically requested.
+    if (!isset($update_functions[$module][$number])) {
+      unset($graph[$function]);
+    }
+  }
+
+  return $graph;
+}
+
+/**
+ * Returns an organized list of update functions for a set of modules.
+ *
+ * @param $starting_updates
+ *   An array whose keys contain the names of modules and whose values contain
+ *   the number of the first requested update for that module.
+ *
+ * @return
+ *   An array containing all the update functions that should be run for each
+ *   module, including the provided starting update and all subsequent updates
+ *   that are available. The keys of the array contain the module names, and
+ *   each value is an ordered array of update functions, keyed by the update
+ *   number.
+ *
+ * @see update_resolve_dependencies()
+ */
+function update_get_update_function_list($starting_updates) {
+  // Go through each module and find all updates that we need (including the
+  // first update that was requested and any updates that run after it).
+  $update_functions = [];
+  foreach ($starting_updates as $module => $version) {
+    $update_functions[$module] = [];
+    $updates = drupal_get_schema_versions($module);
+    if ($updates !== FALSE) {
+      $max_version = max($updates);
+      if ($version <= $max_version) {
+        foreach ($updates as $update) {
+          if ($update >= $version) {
+            $update_functions[$module][$update] = $module . '_update_' . $update;
+          }
+        }
+      }
+    }
+  }
+  return $update_functions;
+}
+
+/**
+ * Constructs a graph which encodes the dependencies between module updates.
+ *
+ * This function returns an associative array which contains a "directed graph"
+ * representation of the dependencies between a provided list of update
+ * functions, as well as any outside update functions that they directly depend
+ * on but that were not in the provided list. The vertices of the graph
+ * represent the update functions themselves, and each edge represents a
+ * requirement that the first update function needs to run before the second.
+ * For example, consider this graph:
+ *
+ * system_update_8001 ---> system_update_8002 ---> system_update_8003
+ *
+ * Visually, this indicates that system_update_8001() must run before
+ * system_update_8002(), which in turn must run before system_update_8003().
+ *
+ * The function takes into account standard dependencies within each module, as
+ * shown above (i.e., the fact that each module's updates must run in numerical
+ * order), but also finds any cross-module dependencies that are defined by
+ * modules which implement hook_update_dependencies(), and builds them into the
+ * graph as well.
+ *
+ * @param $update_functions
+ *   An organized array of update functions, in the format returned by
+ *   update_get_update_function_list().
+ *
+ * @return
+ *   A multidimensional array representing the dependency graph, suitable for
+ *   passing in to Drupal\Component\Graph\Graph::searchAndSort(), but with extra
+ *   information about each update function also included. Each array key
+ *   contains the name of an update function, including all update functions
+ *   from the provided list as well as any outside update functions which they
+ *   directly depend on. Each value is an associative array containing the
+ *   following keys:
+ *   - 'edges': A representation of any other update functions that immediately
+ *     depend on this one. See Drupal\Component\Graph\Graph::searchAndSort() for
+ *     more details on the format.
+ *   - 'module': The name of the module that this update function belongs to.
+ *   - 'number': The number of this update function within that module.
+ *
+ * @see \Drupal\Component\Graph\Graph::searchAndSort()
+ * @see update_resolve_dependencies()
+ */
+function update_build_dependency_graph($update_functions) {
+  // Initialize an array that will define a directed graph representing the
+  // dependencies between update functions.
+  $graph = [];
+
+  // Go through each update function and build an initial list of dependencies.
+  foreach ($update_functions as $module => $functions) {
+    $previous_function = NULL;
+    foreach ($functions as $number => $function) {
+      // Add an edge to the directed graph representing the fact that each
+      // update function in a given module must run after the update that
+      // numerically precedes it.
+      if ($previous_function) {
+        $graph[$previous_function]['edges'][$function] = TRUE;
+      }
+      $previous_function = $function;
+
+      // Define the module and update number associated with this function.
+      $graph[$function]['module'] = $module;
+      $graph[$function]['number'] = $number;
+    }
+  }
+
+  // Now add any explicit update dependencies declared by modules.
+  $update_dependencies = update_retrieve_dependencies();
+  foreach ($graph as $function => $data) {
+    if (!empty($update_dependencies[$data['module']][$data['number']])) {
+      foreach ($update_dependencies[$data['module']][$data['number']] as $module => $number) {
+        $dependency = $module . '_update_' . $number;
+        $graph[$dependency]['edges'][$function] = TRUE;
+        $graph[$dependency]['module'] = $module;
+        $graph[$dependency]['number'] = $number;
+      }
+    }
+  }
+
+  return $graph;
+}
+
+/**
+ * Determines if a module update is missing or unavailable.
+ *
+ * @param $module
+ *   The name of the module.
+ * @param $number
+ *   The number of the update within that module.
+ * @param $update_functions
+ *   An organized array of update functions, in the format returned by
+ *   update_get_update_function_list(). This should represent all module
+ *   updates that are requested to run at the time this function is called.
+ *
+ * @return
+ *   TRUE if the provided module update is not installed or is not in the
+ *   provided list of updates to run; FALSE otherwise.
+ */
+function update_is_missing($module, $number, $update_functions) {
+  return !isset($update_functions[$module][$number]) || !function_exists($update_functions[$module][$number]);
+}
+
+/**
+ * Determines if a module update has already been performed.
+ *
+ * @param $module
+ *   The name of the module.
+ * @param $number
+ *   The number of the update within that module.
+ *
+ * @return
+ *   TRUE if the database schema indicates that the update has already been
+ *   performed; FALSE otherwise.
+ */
+function update_already_performed($module, $number) {
+  return $number <= drupal_get_installed_schema_version($module);
+}
+
+/**
+ * Invokes hook_update_dependencies() in all installed modules.
+ *
+ * This function is similar to \Drupal::moduleHandler()->invokeAll(), with the
+ * main difference that it does not require that a module be enabled to invoke
+ * its hook, only that it be installed. This allows the update system to
+ * properly perform updates even on modules that are currently disabled.
+ *
+ * @return
+ *   An array of return values obtained by merging the results of the
+ *   hook_update_dependencies() implementations in all installed modules.
+ *
+ * @see \Drupal\Core\Extension\ModuleHandlerInterface::invokeAll()
+ * @see hook_update_dependencies()
+ */
+function update_retrieve_dependencies() {
+  $return = [];
+  /** @var \Drupal\Core\Extension\ModuleExtensionList */
+  $extension_list = \Drupal::service('extension.list.module');
+  // Get a list of installed modules, arranged so that we invoke their hooks in
+  // the same order that \Drupal::moduleHandler()->invokeAll() does.
+  foreach (\Drupal::keyValue('system.schema')->getAll() as $module => $schema) {
+    // Skip modules that are entirely missing from the filesystem here, since
+    // module_load_install() will call trigger_error() if invoked on a module
+    // that doesn't exist. There's no way to catch() that, so avoid it entirely.
+    // This can happen when there are orphaned entries in the system.schema k/v
+    // store for modules that have been removed from a site without first being
+    // cleanly uninstalled. We don't care here if the module has been installed
+    // or not, since we'll filter those out in update_get_update_list().
+    if ($schema == SCHEMA_UNINSTALLED || !$extension_list->exists($module)) {
+      // Nothing to upgrade.
+      continue;
+    }
+    $function = $module . '_update_dependencies';
+    // Ensure install file is loaded.
+    module_load_install($module);
+    if (function_exists($function)) {
+      $updated_dependencies = $function();
+      // Each implementation of hook_update_dependencies() returns a
+      // multidimensional, associative array containing some keys that
+      // represent module names (which are strings) and other keys that
+      // represent update function numbers (which are integers). We cannot use
+      // array_merge_recursive() to properly merge these results, since it
+      // treats strings and integers differently. Therefore, we have to
+      // explicitly loop through the expected array structure here and perform
+      // the merge manually.
+      if (isset($updated_dependencies) && is_array($updated_dependencies)) {
+        foreach ($updated_dependencies as $module_name => $module_data) {
+          foreach ($module_data as $update_version => $update_data) {
+            foreach ($update_data as $module_dependency => $update_dependency) {
+              // If there are redundant dependencies declared for the same
+              // update function (so that it is declared to depend on more than
+              // one update from a particular module), record the dependency on
+              // the highest numbered update here, since that automatically
+              // implies the previous ones. For example, if one module's
+              // implementation of hook_update_dependencies() required this
+              // ordering:
+              //
+              // system_update_8002 ---> user_update_8001
+              //
+              // but another module's implementation of the hook required this
+              // one:
+              //
+              // system_update_8003 ---> user_update_8001
+              //
+              // we record the second one, since system_update_8002() is always
+              // guaranteed to run before system_update_8003() anyway (within
+              // an individual module, updates are always run in numerical
+              // order).
+              if (!isset($return[$module_name][$update_version][$module_dependency]) || $update_dependency > $return[$module_name][$update_version][$module_dependency]) {
+                $return[$module_name][$update_version][$module_dependency] = $update_dependency;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  return $return;
+}
+
+/**
+ * Replace permissions during update.
+ *
+ * This function can replace one permission to several or even delete an old
+ * one.
+ *
+ * @param array $replace
+ *   An associative array. The keys are the old permissions the values are lists
+ *   of new permissions. If the list is an empty array, the old permission is
+ *   removed.
+ */
+function update_replace_permissions($replace) {
+  $prefix = 'user.role.';
+  $cut = strlen($prefix);
+  $role_names = \Drupal::service('config.storage')->listAll($prefix);
+  foreach ($role_names as $role_name) {
+    $rid = substr($role_name, $cut);
+    $config = \Drupal::config("user.role.$rid");
+    $permissions = $config->get('permissions') ?: [];
+    foreach ($replace as $old_permission => $new_permissions) {
+      if (($index = array_search($old_permission, $permissions)) !== FALSE) {
+        unset($permissions[$index]);
+        $permissions = array_unique(array_merge($permissions, $new_permissions));
+      }
+    }
+    $config
+      ->set('permissions', $permissions)
+      ->save();
+  }
+}

+ 61 - 0
web/core/includes/utility.inc

@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @file
+ * Miscellaneous functions.
+ */
+
+use Drupal\Core\PhpStorage\PhpStorageFactory;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\DrupalKernel;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Rebuilds all caches even when Drupal itself does not work.
+ *
+ * @param $class_loader
+ *   The class loader. Normally Composer's ClassLoader, as included by the
+ *   front controller, but may also be decorated; e.g.,
+ *   \Symfony\Component\ClassLoader\ApcClassLoader, \Symfony\Component\ClassLoader\WinCacheClassLoader, or \Symfony\Component\ClassLoader\XcacheClassLoader
+ * @param \Symfony\Component\HttpFoundation\Request $request
+ *   The current request.
+ *
+ * @see rebuild.php
+ */
+function drupal_rebuild($class_loader, Request $request) {
+  // Remove Drupal's error and exception handlers; they rely on a working
+  // service container and other subsystems and will only cause a fatal error
+  // that hides the actual error.
+  restore_error_handler();
+  restore_exception_handler();
+
+  // Force kernel to rebuild php cache.
+  PhpStorageFactory::get('twig')->deleteAll();
+
+  // Bootstrap up to where caches exist and clear them.
+  $kernel = new DrupalKernel('prod', $class_loader);
+  $kernel->setSitePath(DrupalKernel::findSitePath($request));
+  $kernel->boot();
+  $kernel->preHandle($request);
+  // Ensure our request includes the session if appropriate.
+  if (PHP_SAPI !== 'cli') {
+    $request->setSession($kernel->getContainer()->get('session'));
+  }
+
+  // Invalidate the container.
+  $kernel->invalidateContainer();
+
+  foreach (Cache::getBins() as $bin) {
+    $bin->deleteAll();
+  }
+
+  // Disable recording of cached pages.
+  \Drupal::service('page_cache_kill_switch')->trigger();
+
+  drupal_flush_all_caches();
+
+  // Restore Drupal's error and exception handlers.
+  // @see \Drupal\Core\DrupalKernel::boot()
+  set_error_handler('_drupal_error_handler');
+  set_exception_handler('_drupal_exception_handler');
+}

+ 44 - 0
web/core/install.php

@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Initiates a browser-based installation of Drupal.
+ */
+
+use Drupal\Component\Utility\OpCodeCache;
+
+// Change the directory to the Drupal root.
+chdir('..');
+// Store the Drupal root path.
+$root_path = realpath('');
+
+/**
+ * Global flag to indicate the site is in installation mode.
+ *
+ * The constant is defined using define() instead of const so that PHP
+ * versions prior to 5.3 can display proper PHP requirements instead of causing
+ * a fatal error.
+ */
+define('MAINTENANCE_MODE', 'install');
+
+// Exit early if running an incompatible PHP version to avoid fatal errors.
+// The minimum version is specified explicitly, as DRUPAL_MINIMUM_PHP is not
+// yet available. It is defined in bootstrap.inc, but it is not possible to
+// load that file yet as it would cause a fatal error on older versions of PHP.
+if (version_compare(PHP_VERSION, '7.0.8') < 0) {
+  print 'Your PHP installation is too old. Drupal requires at least PHP 7.0.8. See the <a href="https://www.drupal.org/requirements">system requirements</a> page for more information.';
+  exit;
+}
+
+// Initialize the autoloader.
+$class_loader = require_once $root_path . '/autoload.php';
+
+// If OPCache is in use, ensure opcache.save_comments is enabled.
+if (OpCodeCache::isEnabled() && !ini_get('opcache.save_comments')) {
+  print 'Systems with OPcache installed must have <a href="http://php.net/manual/opcache.configuration.php#ini.opcache.save-comments">opcache.save_comments</a> enabled.';
+  exit();
+}
+
+// Start the installer.
+require_once $root_path . '/core/includes/install.core.inc';
+install_drupal($class_loader);

+ 791 - 0
web/core/lib/Drupal.php

@@ -0,0 +1,791 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal.
+ */
+
+use Drupal\Core\DependencyInjection\ContainerNotInitializedException;
+use Drupal\Core\Messenger\LegacyMessenger;
+use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Static Service Container wrapper.
+ *
+ * Generally, code in Drupal should accept its dependencies via either
+ * constructor injection or setter method injection. However, there are cases,
+ * particularly in legacy procedural code, where that is infeasible. This
+ * class acts as a unified global accessor to arbitrary services within the
+ * system in order to ease the transition from procedural code to injected OO
+ * code.
+ *
+ * The container is built by the kernel and passed in to this class which stores
+ * it statically. The container always contains the services from
+ * \Drupal\Core\CoreServiceProvider, the service providers of enabled modules and any other
+ * service providers defined in $GLOBALS['conf']['container_service_providers'].
+ *
+ * This class exists only to support legacy code that cannot be dependency
+ * injected. If your code needs it, consider refactoring it to be object
+ * oriented, if possible. When this is not possible, for instance in the case of
+ * hook implementations, and your code is more than a few non-reusable lines, it
+ * is recommended to instantiate an object implementing the actual logic.
+ *
+ * @code
+ *   // Legacy procedural code.
+ *   function hook_do_stuff() {
+ *     $lock = lock()->acquire('stuff_lock');
+ *     // ...
+ *   }
+ *
+ *   // Correct procedural code.
+ *   function hook_do_stuff() {
+ *     $lock = \Drupal::lock()->acquire('stuff_lock');
+ *     // ...
+ *   }
+ *
+ *   // The preferred way: dependency injected code.
+ *   function hook_do_stuff() {
+ *     // Move the actual implementation to a class and instantiate it.
+ *     $instance = new StuffDoingClass(\Drupal::lock());
+ *     $instance->doStuff();
+ *
+ *     // Or, even better, rely on the service container to avoid hard coding a
+ *     // specific interface implementation, so that the actual logic can be
+ *     // swapped. This might not always make sense, but in general it is a good
+ *     // practice.
+ *     \Drupal::service('stuff.doing')->doStuff();
+ *   }
+ *
+ *   interface StuffDoingInterface {
+ *     public function doStuff();
+ *   }
+ *
+ *   class StuffDoingClass implements StuffDoingInterface {
+ *     protected $lockBackend;
+ *
+ *     public function __construct(LockBackendInterface $lock_backend) {
+ *       $this->lockBackend = $lock_backend;
+ *     }
+ *
+ *     public function doStuff() {
+ *       $lock = $this->lockBackend->acquire('stuff_lock');
+ *       // ...
+ *     }
+ *   }
+ * @endcode
+ *
+ * @see \Drupal\Core\DrupalKernel
+ */
+class Drupal {
+
+  /**
+   * The current system version.
+   */
+  const VERSION = '8.9.0';
+
+  /**
+   * Core API compatibility.
+   */
+  const CORE_COMPATIBILITY = '8.x';
+
+  /**
+   * Core minimum schema version.
+   */
+  const CORE_MINIMUM_SCHEMA_VERSION = 8000;
+
+  /**
+   * The currently active container object, or NULL if not initialized yet.
+   *
+   * @var \Symfony\Component\DependencyInjection\ContainerInterface|null
+   */
+  protected static $container;
+
+  /**
+   * Sets a new global container.
+   *
+   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+   *   A new container instance to replace the current.
+   */
+  public static function setContainer(ContainerInterface $container) {
+    static::$container = $container;
+  }
+
+  /**
+   * Unsets the global container.
+   */
+  public static function unsetContainer() {
+    static::$container = NULL;
+  }
+
+  /**
+   * Returns the currently active global container.
+   *
+   * @return \Symfony\Component\DependencyInjection\ContainerInterface
+   *
+   * @throws \Drupal\Core\DependencyInjection\ContainerNotInitializedException
+   */
+  public static function getContainer() {
+    if (static::$container === NULL) {
+      throw new ContainerNotInitializedException('\Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container.');
+    }
+    return static::$container;
+  }
+
+  /**
+   * Returns TRUE if the container has been initialized, FALSE otherwise.
+   *
+   * @return bool
+   */
+  public static function hasContainer() {
+    return static::$container !== NULL;
+  }
+
+  /**
+   * Retrieves a service from the container.
+   *
+   * Use this method if the desired service is not one of those with a dedicated
+   * accessor method below. If it is listed below, those methods are preferred
+   * as they can return useful type hints.
+   *
+   * @param string $id
+   *   The ID of the service to retrieve.
+   *
+   * @return mixed
+   *   The specified service.
+   */
+  public static function service($id) {
+    return static::getContainer()->get($id);
+  }
+
+  /**
+   * Indicates if a service is defined in the container.
+   *
+   * @param string $id
+   *   The ID of the service to check.
+   *
+   * @return bool
+   *   TRUE if the specified service exists, FALSE otherwise.
+   */
+  public static function hasService($id) {
+    // Check hasContainer() first in order to always return a Boolean.
+    return static::hasContainer() && static::getContainer()->has($id);
+  }
+
+  /**
+   * Gets the app root.
+   *
+   * @return string
+   */
+  public static function root() {
+    return static::getContainer()->get('app.root');
+  }
+
+  /**
+   * Gets the active install profile.
+   *
+   * @return string|null
+   *   The name of the active install profile.
+   */
+  public static function installProfile() {
+    return static::getContainer()->getParameter('install_profile');
+  }
+
+  /**
+   * Indicates if there is a currently active request object.
+   *
+   * @return bool
+   *   TRUE if there is a currently active request object, FALSE otherwise.
+   */
+  public static function hasRequest() {
+    // Check hasContainer() first in order to always return a Boolean.
+    return static::hasContainer() && static::getContainer()->has('request_stack') && static::getContainer()->get('request_stack')->getCurrentRequest() !== NULL;
+  }
+
+  /**
+   * Retrieves the currently active request object.
+   *
+   * Note: The use of this wrapper in particular is especially discouraged. Most
+   * code should not need to access the request directly.  Doing so means it
+   * will only function when handling an HTTP request, and will require special
+   * modification or wrapping when run from a command line tool, from certain
+   * queue processors, or from automated tests.
+   *
+   * If code must access the request, it is considerably better to register
+   * an object with the Service Container and give it a setRequest() method
+   * that is configured to run when the service is created.  That way, the
+   * correct request object can always be provided by the container and the
+   * service can still be unit tested.
+   *
+   * If this method must be used, never save the request object that is
+   * returned.  Doing so may lead to inconsistencies as the request object is
+   * volatile and may change at various times, such as during a subrequest.
+   *
+   * @return \Symfony\Component\HttpFoundation\Request
+   *   The currently active request object.
+   */
+  public static function request() {
+    return static::getContainer()->get('request_stack')->getCurrentRequest();
+  }
+
+  /**
+   * Retrieves the request stack.
+   *
+   * @return \Symfony\Component\HttpFoundation\RequestStack
+   *   The request stack
+   */
+  public static function requestStack() {
+    return static::getContainer()->get('request_stack');
+  }
+
+  /**
+   * Retrieves the currently active route match object.
+   *
+   * @return \Drupal\Core\Routing\RouteMatchInterface
+   *   The currently active route match object.
+   */
+  public static function routeMatch() {
+    return static::getContainer()->get('current_route_match');
+  }
+
+  /**
+   * Gets the current active user.
+   *
+   * This method will return the \Drupal\Core\Session\AccountProxy object of the
+   * current user. You can use the \Drupal\user\Entity\User::load() method to
+   * load the full user entity object. For example:
+   * @code
+   *   $user = \Drupal\user\Entity\User::load(\Drupal::currentUser()->id());
+   * @endcode
+   *
+   * @return \Drupal\Core\Session\AccountProxyInterface
+   */
+  public static function currentUser() {
+    return static::getContainer()->get('current_user');
+  }
+
+  /**
+   * Retrieves the entity manager service.
+   *
+   * @return \Drupal\Core\Entity\EntityManagerInterface
+   *   The entity manager service.
+   *
+   * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+   *   Use \Drupal::entityTypeManager() instead in most cases. If the needed
+   *   method is not on \Drupal\Core\Entity\EntityTypeManagerInterface, see the
+   *   deprecated \Drupal\Core\Entity\EntityManager to find the
+   *   correct interface or service.
+   */
+  public static function entityManager() {
+    @trigger_error("\Drupal::entityManager() is deprecated in Drupal 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal::entityTypeManager() instead in most cases. If the needed method is not on \Drupal\Core\Entity\EntityTypeManagerInterface, see the deprecated \Drupal\Core\Entity\EntityManager to find the correct interface or service. See https://www.drupal.org/node/2549139", E_USER_DEPRECATED);
+    return static::getContainer()->get('entity.manager');
+  }
+
+  /**
+   * Retrieves the entity type manager.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeManagerInterface
+   *   The entity type manager.
+   */
+  public static function entityTypeManager() {
+    return static::getContainer()->get('entity_type.manager');
+  }
+
+  /**
+   * Returns the current primary database.
+   *
+   * @return \Drupal\Core\Database\Connection
+   *   The current active database's master connection.
+   */
+  public static function database() {
+    return static::getContainer()->get('database');
+  }
+
+  /**
+   * Returns the requested cache bin.
+   *
+   * @param string $bin
+   *   (optional) The cache bin for which the cache object should be returned,
+   *   defaults to 'default'.
+   *
+   * @return \Drupal\Core\Cache\CacheBackendInterface
+   *   The cache object associated with the specified bin.
+   *
+   * @ingroup cache
+   */
+  public static function cache($bin = 'default') {
+    return static::getContainer()->get('cache.' . $bin);
+  }
+
+  /**
+   * Retrieves the class resolver.
+   *
+   * This is to be used in procedural code such as module files to instantiate
+   * an object of a class that implements
+   * \Drupal\Core\DependencyInjection\ContainerInjectionInterface.
+   *
+   * One common usecase is to provide a class which contains the actual code
+   * of a hook implementation, without having to create a service.
+   *
+   * @param string $class
+   *   (optional) A class name to instantiate.
+   *
+   * @return \Drupal\Core\DependencyInjection\ClassResolverInterface|object
+   *   The class resolver or if $class is provided, a class instance with a
+   *   given class definition.
+   *
+   * @throws \InvalidArgumentException
+   *   If $class does not exist.
+   */
+  public static function classResolver($class = NULL) {
+    if ($class) {
+      return static::getContainer()->get('class_resolver')->getInstanceFromDefinition($class);
+    }
+    return static::getContainer()->get('class_resolver');
+  }
+
+  /**
+   * Returns an expirable key value store collection.
+   *
+   * @param string $collection
+   *   The name of the collection holding key and value pairs.
+   *
+   * @return \Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
+   *   An expirable key value store collection.
+   */
+  public static function keyValueExpirable($collection) {
+    return static::getContainer()->get('keyvalue.expirable')->get($collection);
+  }
+
+  /**
+   * Returns the locking layer instance.
+   *
+   * @return \Drupal\Core\Lock\LockBackendInterface
+   *
+   * @ingroup lock
+   */
+  public static function lock() {
+    return static::getContainer()->get('lock');
+  }
+
+  /**
+   * Retrieves a configuration object.
+   *
+   * This is the main entry point to the configuration API. Calling
+   * @code \Drupal::config('book.admin') @endcode will return a configuration
+   * object the Book module can use to read its administrative settings.
+   *
+   * @param string $name
+   *   The name of the configuration object to retrieve, which typically
+   *   corresponds to a configuration file. For
+   *   @code \Drupal::config('book.admin') @endcode, the configuration
+   *   object returned will contain the content of the book.admin
+   *   configuration file.
+   *
+   * @return \Drupal\Core\Config\ImmutableConfig
+   *   An immutable configuration object.
+   */
+  public static function config($name) {
+    return static::getContainer()->get('config.factory')->get($name);
+  }
+
+  /**
+   * Retrieves the configuration factory.
+   *
+   * This is mostly used to change the override settings on the configuration
+   * factory. For example, changing the language, or turning all overrides on
+   * or off.
+   *
+   * @return \Drupal\Core\Config\ConfigFactoryInterface
+   *   The configuration factory service.
+   */
+  public static function configFactory() {
+    return static::getContainer()->get('config.factory');
+  }
+
+  /**
+   * Returns a queue for the given queue name.
+   *
+   * The following values can be set in your settings.php file's $settings
+   * array to define which services are used for queues:
+   * - queue_reliable_service_$name: The container service to use for the
+   *   reliable queue $name.
+   * - queue_service_$name: The container service to use for the
+   *   queue $name.
+   * - queue_default: The container service to use by default for queues
+   *   without overrides. This defaults to 'queue.database'.
+   *
+   * @param string $name
+   *   The name of the queue to work with.
+   * @param bool $reliable
+   *   (optional) TRUE if the ordering of items and guaranteeing every item
+   *   executes at least once is important, FALSE if scalability is the main
+   *   concern. Defaults to FALSE.
+   *
+   * @return \Drupal\Core\Queue\QueueInterface
+   *   The queue object for a given name.
+   */
+  public static function queue($name, $reliable = FALSE) {
+    return static::getContainer()->get('queue')->get($name, $reliable);
+  }
+
+  /**
+   * Returns a key/value storage collection.
+   *
+   * @param string $collection
+   *   Name of the key/value collection to return.
+   *
+   * @return \Drupal\Core\KeyValueStore\KeyValueStoreInterface
+   */
+  public static function keyValue($collection) {
+    return static::getContainer()->get('keyvalue')->get($collection);
+  }
+
+  /**
+   * Returns the state storage service.
+   *
+   * Use this to store machine-generated data, local to a specific environment
+   * that does not need deploying and does not need human editing; for example,
+   * the last time cron was run. Data which needs to be edited by humans and
+   * needs to be the same across development, production, etc. environments
+   * (for example, the system maintenance message) should use \Drupal::config() instead.
+   *
+   * @return \Drupal\Core\State\StateInterface
+   */
+  public static function state() {
+    return static::getContainer()->get('state');
+  }
+
+  /**
+   * Returns the default http client.
+   *
+   * @return \GuzzleHttp\Client
+   *   A guzzle http client instance.
+   */
+  public static function httpClient() {
+    return static::getContainer()->get('http_client');
+  }
+
+  /**
+   * Returns the entity query object for this entity type.
+   *
+   * @param string $entity_type
+   *   The entity type (for example, node) for which the query object should be
+   *   returned.
+   * @param string $conjunction
+   *   (optional) Either 'AND' if all conditions in the query need to apply, or
+   *   'OR' if any of them is sufficient. Defaults to 'AND'.
+   *
+   * @return \Drupal\Core\Entity\Query\QueryInterface
+   *   The query object that can query the given entity type.
+   */
+  public static function entityQuery($entity_type, $conjunction = 'AND') {
+    return static::entityTypeManager()->getStorage($entity_type)->getQuery($conjunction);
+  }
+
+  /**
+   * Returns the entity query aggregate object for this entity type.
+   *
+   * @param string $entity_type
+   *   The entity type (for example, node) for which the query object should be
+   *   returned.
+   * @param string $conjunction
+   *   (optional) Either 'AND' if all conditions in the query need to apply, or
+   *   'OR' if any of them is sufficient. Defaults to 'AND'.
+   *
+   * @return \Drupal\Core\Entity\Query\QueryAggregateInterface
+   *   The query object that can query the given entity type.
+   */
+  public static function entityQueryAggregate($entity_type, $conjunction = 'AND') {
+    return static::entityTypeManager()->getStorage($entity_type)->getAggregateQuery($conjunction);
+  }
+
+  /**
+   * Returns the flood instance.
+   *
+   * @return \Drupal\Core\Flood\FloodInterface
+   */
+  public static function flood() {
+    return static::getContainer()->get('flood');
+  }
+
+  /**
+   * Returns the module handler.
+   *
+   * @return \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  public static function moduleHandler() {
+    return static::getContainer()->get('module_handler');
+  }
+
+  /**
+   * Returns the typed data manager service.
+   *
+   * Use the typed data manager service for creating typed data objects.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataManagerInterface
+   *   The typed data manager.
+   *
+   * @see \Drupal\Core\TypedData\TypedDataManager::create()
+   */
+  public static function typedDataManager() {
+    return static::getContainer()->get('typed_data_manager');
+  }
+
+  /**
+   * Returns the token service.
+   *
+   * @return \Drupal\Core\Utility\Token
+   *   The token service.
+   */
+  public static function token() {
+    return static::getContainer()->get('token');
+  }
+
+  /**
+   * Returns the url generator service.
+   *
+   * @return \Drupal\Core\Routing\UrlGeneratorInterface
+   *   The url generator service.
+   */
+  public static function urlGenerator() {
+    return static::getContainer()->get('url_generator');
+  }
+
+  /**
+   * Generates a URL string for a specific route based on the given parameters.
+   *
+   * This method is a convenience wrapper for generating URL strings for URLs
+   * that have Drupal routes (that is, most pages generated by Drupal) using
+   * the \Drupal\Core\Url object. See \Drupal\Core\Url::fromRoute() for
+   * detailed documentation. For non-routed local URIs relative to
+   * the base path (like robots.txt) use Url::fromUri()->toString() with the
+   * base: scheme.
+   *
+   * @param string $route_name
+   *   The name of the route.
+   * @param array $route_parameters
+   *   (optional) An associative array of parameter names and values.
+   * @param array $options
+   *   (optional) An associative array of additional options.
+   * @param bool $collect_bubbleable_metadata
+   *   (optional) Defaults to FALSE. When TRUE, both the generated URL and its
+   *   associated bubbleable metadata are returned.
+   *
+   * @return string|\Drupal\Core\GeneratedUrl
+   *   A string containing a URL to the given path.
+   *   When $collect_bubbleable_metadata is TRUE, a GeneratedUrl object is
+   *   returned, containing the generated URL plus bubbleable metadata.
+   *
+   * @see \Drupal\Core\Routing\UrlGeneratorInterface::generateFromRoute()
+   * @see \Drupal\Core\Url
+   * @see \Drupal\Core\Url::fromRoute()
+   * @see \Drupal\Core\Url::fromUri()
+   *
+   * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0.
+   *   Instead create a \Drupal\Core\Url object directly, for example using
+   *   Url::fromRoute().
+   */
+  public static function url($route_name, $route_parameters = [], $options = [], $collect_bubbleable_metadata = FALSE) {
+    @trigger_error('Drupal::url() is deprecated as of Drupal 8.0.x, will be removed before Drupal 9.0.0. Instead create a \Drupal\Core\Url object directly, for example using Url::fromRoute()', E_USER_DEPRECATED);
+    return static::getContainer()->get('url_generator')->generateFromRoute($route_name, $route_parameters, $options, $collect_bubbleable_metadata);
+  }
+
+  /**
+   * Returns the link generator service.
+   *
+   * @return \Drupal\Core\Utility\LinkGeneratorInterface
+   */
+  public static function linkGenerator() {
+    return static::getContainer()->get('link_generator');
+  }
+
+  /**
+   * Renders a link with a given link text and Url object.
+   *
+   * This method is a convenience wrapper for the link generator service's
+   * generate() method.
+   *
+   * @param string $text
+   *   The link text for the anchor tag.
+   * @param \Drupal\Core\Url $url
+   *   The URL object used for the link.
+   *
+   * @return \Drupal\Core\GeneratedLink
+   *   A GeneratedLink object containing a link to the given route and
+   *   parameters and bubbleable metadata.
+   *
+   * @deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use
+   * \Drupal\Core\Link::fromTextAndUrl() instead.
+   *
+   * @see https://www.drupal.org/node/2614344
+   * @see \Drupal\Core\Utility\LinkGeneratorInterface::generate()
+   * @see \Drupal\Core\Url
+   */
+  public static function l($text, Url $url) {
+    @trigger_error('\Drupal::l() is deprecated in drupal:8.0.0 and is removed from drupal:9.0.0. Use \Drupal\Core\Link::fromTextAndUrl() instead. See https://www.drupal.org/node/2614344', E_USER_DEPRECATED);
+    return static::getContainer()->get('link_generator')->generate($text, $url);
+  }
+
+  /**
+   * Returns the string translation service.
+   *
+   * @return \Drupal\Core\StringTranslation\TranslationManager
+   *   The string translation manager.
+   */
+  public static function translation() {
+    return static::getContainer()->get('string_translation');
+  }
+
+  /**
+   * Returns the language manager service.
+   *
+   * @return \Drupal\Core\Language\LanguageManagerInterface
+   *   The language manager.
+   */
+  public static function languageManager() {
+    return static::getContainer()->get('language_manager');
+  }
+
+  /**
+   * Returns the CSRF token manager service.
+   *
+   * The generated token is based on the session ID of the current user. Normally,
+   * anonymous users do not have a session, so the generated token will be
+   * different on every page request. To generate a token for users without a
+   * session, manually start a session prior to calling this function.
+   *
+   * @return \Drupal\Core\Access\CsrfTokenGenerator
+   *   The CSRF token manager.
+   *
+   * @see \Drupal\Core\Session\SessionManager::start()
+   */
+  public static function csrfToken() {
+    return static::getContainer()->get('csrf_token');
+  }
+
+  /**
+   * Returns the transliteration service.
+   *
+   * @return \Drupal\Core\Transliteration\PhpTransliteration
+   *   The transliteration manager.
+   */
+  public static function transliteration() {
+    return static::getContainer()->get('transliteration');
+  }
+
+  /**
+   * Returns the form builder service.
+   *
+   * @return \Drupal\Core\Form\FormBuilderInterface
+   *   The form builder.
+   */
+  public static function formBuilder() {
+    return static::getContainer()->get('form_builder');
+  }
+
+  /**
+   * Gets the theme service.
+   *
+   * @return \Drupal\Core\Theme\ThemeManagerInterface
+   */
+  public static function theme() {
+    return static::getContainer()->get('theme.manager');
+  }
+
+  /**
+   * Gets the syncing state.
+   *
+   * @return bool
+   *   Returns TRUE is syncing flag set.
+   */
+  public static function isConfigSyncing() {
+    return static::getContainer()->get('config.installer')->isSyncing();
+  }
+
+  /**
+   * Returns a channel logger object.
+   *
+   * @param string $channel
+   *   The name of the channel. Can be any string, but the general practice is
+   *   to use the name of the subsystem calling this.
+   *
+   * @return \Psr\Log\LoggerInterface
+   *   The logger for this channel.
+   */
+  public static function logger($channel) {
+    return static::getContainer()->get('logger.factory')->get($channel);
+  }
+
+  /**
+   * Returns the menu tree.
+   *
+   * @return \Drupal\Core\Menu\MenuLinkTreeInterface
+   *   The menu tree.
+   */
+  public static function menuTree() {
+    return static::getContainer()->get('menu.link_tree');
+  }
+
+  /**
+   * Returns the path validator.
+   *
+   * @return \Drupal\Core\Path\PathValidatorInterface
+   */
+  public static function pathValidator() {
+    return static::getContainer()->get('path.validator');
+  }
+
+  /**
+   * Returns the access manager service.
+   *
+   * @return \Drupal\Core\Access\AccessManagerInterface
+   *   The access manager service.
+   */
+  public static function accessManager() {
+    return static::getContainer()->get('access_manager');
+  }
+
+  /**
+   * Returns the redirect destination helper.
+   *
+   * @return \Drupal\Core\Routing\RedirectDestinationInterface
+   *   The redirect destination helper.
+   */
+  public static function destination() {
+    return static::getContainer()->get('redirect.destination');
+  }
+
+  /**
+   * Returns the entity definition update manager.
+   *
+   * @return \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
+   *   The entity definition update manager.
+   */
+  public static function entityDefinitionUpdateManager() {
+    return static::getContainer()->get('entity.definition_update_manager');
+  }
+
+  /**
+   * Returns the time service.
+   *
+   * @return \Drupal\Component\Datetime\TimeInterface
+   *   The time service.
+   */
+  public static function time() {
+    return static::getContainer()->get('datetime.time');
+  }
+
+  /**
+   * Returns the messenger.
+   *
+   * @return \Drupal\Core\Messenger\MessengerInterface
+   *   The messenger.
+   */
+  public static function messenger() {
+    // @todo Replace with service once LegacyMessenger is removed in 9.0.0.
+    // @see https://www.drupal.org/node/2928994
+    return new LegacyMessenger();
+  }
+
+}

+ 66 - 0
web/core/lib/Drupal/Component/Annotation/AnnotationBase.php

@@ -0,0 +1,66 @@
+<?php
+
+namespace Drupal\Component\Annotation;
+
+/**
+ * Provides a base class for classed annotations.
+ */
+abstract class AnnotationBase implements AnnotationInterface {
+
+  /**
+   * The annotated class ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The class used for this annotated class.
+   *
+   * @var string
+   */
+  protected $class;
+
+  /**
+   * The provider of the annotated class.
+   *
+   * @var string
+   */
+  protected $provider;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getProvider() {
+    return $this->provider;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setProvider($provider) {
+    $this->provider = $provider;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getId() {
+    return $this->id;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClass() {
+    return $this->class;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setClass($class) {
+    $this->class = $class;
+  }
+
+}

+ 50 - 0
web/core/lib/Drupal/Component/Annotation/AnnotationInterface.php

@@ -0,0 +1,50 @@
+<?php
+
+namespace Drupal\Component\Annotation;
+
+/**
+ * Defines a common interface for classed annotations.
+ */
+interface AnnotationInterface {
+
+  /**
+   * Gets the value of an annotation.
+   */
+  public function get();
+
+  /**
+   * Gets the name of the provider of the annotated class.
+   *
+   * @return string
+   */
+  public function getProvider();
+
+  /**
+   * Sets the name of the provider of the annotated class.
+   *
+   * @param string $provider
+   */
+  public function setProvider($provider);
+
+  /**
+   * Gets the unique ID for this annotated class.
+   *
+   * @return string
+   */
+  public function getId();
+
+  /**
+   * Gets the class of the annotated class.
+   *
+   * @return string
+   */
+  public function getClass();
+
+  /**
+   * Sets the class of the annotated class.
+   *
+   * @param string $class
+   */
+  public function setClass($class);
+
+}

+ 1142 - 0
web/core/lib/Drupal/Component/Annotation/Doctrine/DocParser.php

@@ -0,0 +1,1142 @@
+<?php
+// @codingStandardsIgnoreFile
+
+/**
+ * @file
+ *
+ * This class is a near-copy of Doctrine\Common\Annotations\DocParser, which is
+ * part of the Doctrine project: <http://www.doctrine-project.org>. It was
+ * copied from version 1.2.7.
+ *
+ * Original copyright:
+ *
+ * Copyright (c) 2006-2013 Doctrine Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ */
+
+namespace Drupal\Component\Annotation\Doctrine;
+
+use Doctrine\Common\Annotations\Annotation\Attribute;
+use Doctrine\Common\Annotations\Annotation\Enum;
+use Doctrine\Common\Annotations\Annotation\Target;
+use Doctrine\Common\Annotations\Annotation\Attributes;
+use Doctrine\Common\Annotations\AnnotationException;
+use Doctrine\Common\Annotations\AnnotationRegistry;
+use Doctrine\Common\Annotations\DocLexer;
+
+/**
+ * A parser for docblock annotations.
+ *
+ * This Drupal version allows for ignoring annotations when namespaces are
+ * present.
+ *
+ * @internal
+ */
+final class DocParser
+{
+    /**
+     * An array of all valid tokens for a class name.
+     *
+     * @var array
+     */
+    private static $classIdentifiers = array(
+        DocLexer::T_IDENTIFIER,
+        DocLexer::T_TRUE,
+        DocLexer::T_FALSE,
+        DocLexer::T_NULL
+    );
+
+    /**
+     * The lexer.
+     *
+     * @var \Doctrine\Common\Annotations\DocLexer
+     */
+    private $lexer;
+
+    /**
+     * Current target context.
+     *
+     * @var string
+     */
+    private $target;
+
+    /**
+     * Doc parser used to collect annotation target.
+     *
+     * @var \Doctrine\Common\Annotations\DocParser
+     */
+    private static $metadataParser;
+
+    /**
+     * Flag to control if the current annotation is nested or not.
+     *
+     * @var boolean
+     */
+    private $isNestedAnnotation = false;
+
+    /**
+     * Hashmap containing all use-statements that are to be used when parsing
+     * the given doc block.
+     *
+     * @var array
+     */
+    private $imports = array();
+
+    /**
+     * This hashmap is used internally to cache results of class_exists()
+     * look-ups.
+     *
+     * @var array
+     */
+    private $classExists = array();
+
+    /**
+     * Whether annotations that have not been imported should be ignored.
+     *
+     * @var boolean
+     */
+    private $ignoreNotImportedAnnotations = false;
+
+    /**
+     * An array of default namespaces if operating in simple mode.
+     *
+     * @var array
+     */
+    private $namespaces = array();
+
+    /**
+     * A list with annotations that are not causing exceptions when not resolved to an annotation class.
+     *
+     * The names must be the raw names as used in the class, not the fully qualified
+     * class names.
+     *
+     * @var array
+     */
+    private $ignoredAnnotationNames = array();
+
+    /**
+     * @var string
+     */
+    private $context = '';
+
+    /**
+     * Hash-map for caching annotation metadata.
+     *
+     * @var array
+     */
+    private static $annotationMetadata = array(
+        'Doctrine\Common\Annotations\Annotation\Target' => array(
+            'is_annotation'    => true,
+            'has_constructor'  => true,
+            'properties'       => array(),
+            'targets_literal'  => 'ANNOTATION_CLASS',
+            'targets'          => Target::TARGET_CLASS,
+            'default_property' => 'value',
+            'attribute_types'  => array(
+                'value'  => array(
+                    'required'  => false,
+                    'type'      =>'array',
+                    'array_type'=>'string',
+                    'value'     =>'array<string>'
+                )
+             ),
+        ),
+        'Doctrine\Common\Annotations\Annotation\Attribute' => array(
+            'is_annotation'    => true,
+            'has_constructor'  => false,
+            'targets_literal'  => 'ANNOTATION_ANNOTATION',
+            'targets'          => Target::TARGET_ANNOTATION,
+            'default_property' => 'name',
+            'properties'       => array(
+                'name'      => 'name',
+                'type'      => 'type',
+                'required'  => 'required'
+            ),
+            'attribute_types'  => array(
+                'value'  => array(
+                    'required'  => true,
+                    'type'      =>'string',
+                    'value'     =>'string'
+                ),
+                'type'  => array(
+                    'required'  =>true,
+                    'type'      =>'string',
+                    'value'     =>'string'
+                ),
+                'required'  => array(
+                    'required'  =>false,
+                    'type'      =>'boolean',
+                    'value'     =>'boolean'
+                )
+             ),
+        ),
+        'Doctrine\Common\Annotations\Annotation\Attributes' => array(
+            'is_annotation'    => true,
+            'has_constructor'  => false,
+            'targets_literal'  => 'ANNOTATION_CLASS',
+            'targets'          => Target::TARGET_CLASS,
+            'default_property' => 'value',
+            'properties'       => array(
+                'value' => 'value'
+            ),
+            'attribute_types'  => array(
+                'value' => array(
+                    'type'      =>'array',
+                    'required'  =>true,
+                    'array_type'=>'Doctrine\Common\Annotations\Annotation\Attribute',
+                    'value'     =>'array<Doctrine\Common\Annotations\Annotation\Attribute>'
+                )
+             ),
+        ),
+        'Doctrine\Common\Annotations\Annotation\Enum' => array(
+            'is_annotation'    => true,
+            'has_constructor'  => true,
+            'targets_literal'  => 'ANNOTATION_PROPERTY',
+            'targets'          => Target::TARGET_PROPERTY,
+            'default_property' => 'value',
+            'properties'       => array(
+                'value' => 'value'
+            ),
+            'attribute_types'  => array(
+                'value' => array(
+                    'type'      => 'array',
+                    'required'  => true,
+                ),
+                'literal' => array(
+                    'type'      => 'array',
+                    'required'  => false,
+                ),
+             ),
+        ),
+    );
+
+    /**
+     * Hash-map for handle types declaration.
+     *
+     * @var array
+     */
+    private static $typeMap = array(
+        'float'     => 'double',
+        'bool'      => 'boolean',
+        // allow uppercase Boolean in honor of George Boole
+        'Boolean'   => 'boolean',
+        'int'       => 'integer',
+    );
+
+    /**
+     * Constructs a new DocParser.
+     */
+    public function __construct()
+    {
+        $this->lexer = new DocLexer;
+    }
+
+    /**
+     * Sets the annotation names that are ignored during the parsing process.
+     *
+     * The names are supposed to be the raw names as used in the class, not the
+     * fully qualified class names.
+     *
+     * @param array $names
+     *
+     * @return void
+     */
+    public function setIgnoredAnnotationNames(array $names)
+    {
+        $this->ignoredAnnotationNames = $names;
+    }
+
+    /**
+     * Sets ignore on not-imported annotations.
+     *
+     * @param boolean $bool
+     *
+     * @return void
+     */
+    public function setIgnoreNotImportedAnnotations($bool)
+    {
+        $this->ignoreNotImportedAnnotations = (boolean) $bool;
+    }
+
+    /**
+     * Sets the default namespaces.
+     *
+     * @param array $namespace
+     *
+     * @return void
+     *
+     * @throws \RuntimeException
+     */
+    public function addNamespace($namespace)
+    {
+        if ($this->imports) {
+            throw new \RuntimeException('You must either use addNamespace(), or setImports(), but not both.');
+        }
+
+        $this->namespaces[] = $namespace;
+    }
+
+    /**
+     * Sets the imports.
+     *
+     * @param array $imports
+     *
+     * @return void
+     *
+     * @throws \RuntimeException
+     */
+    public function setImports(array $imports)
+    {
+        if ($this->namespaces) {
+            throw new \RuntimeException('You must either use addNamespace(), or setImports(), but not both.');
+        }
+
+        $this->imports = $imports;
+    }
+
+    /**
+     * Sets current target context as bitmask.
+     *
+     * @param integer $target
+     *
+     * @return void
+     */
+    public function setTarget($target)
+    {
+        $this->target = $target;
+    }
+
+    /**
+     * Parses the given docblock string for annotations.
+     *
+     * @param string $input   The docblock string to parse.
+     * @param string $context The parsing context.
+     *
+     * @return array Array of annotations. If no annotations are found, an empty array is returned.
+     */
+    public function parse($input, $context = '')
+    {
+        $pos = $this->findInitialTokenPosition($input);
+        if ($pos === null) {
+            return array();
+        }
+
+        $this->context = $context;
+
+        $this->lexer->setInput(trim(substr($input, $pos), '* /'));
+        $this->lexer->moveNext();
+
+        return $this->Annotations();
+    }
+
+    /**
+     * Finds the first valid annotation
+     *
+     * @param string $input The docblock string to parse
+     *
+     * @return int|null
+     */
+    private function findInitialTokenPosition($input)
+    {
+        $pos = 0;
+
+        // search for first valid annotation
+        while (($pos = strpos($input, '@', $pos)) !== false) {
+            // if the @ is preceded by a space or * it is valid
+            if ($pos === 0 || $input[$pos - 1] === ' ' || $input[$pos - 1] === '*') {
+                return $pos;
+            }
+
+            $pos++;
+        }
+
+        return null;
+    }
+
+    /**
+     * Attempts to match the given token with the current lookahead token.
+     * If they match, updates the lookahead token; otherwise raises a syntax error.
+     *
+     * @param integer $token Type of token.
+     *
+     * @return boolean True if tokens match; false otherwise.
+     */
+    private function match($token)
+    {
+        if ( ! $this->lexer->isNextToken($token) ) {
+            $this->syntaxError($this->lexer->getLiteral($token));
+        }
+
+        return $this->lexer->moveNext();
+    }
+
+    /**
+     * Attempts to match the current lookahead token with any of the given tokens.
+     *
+     * If any of them matches, this method updates the lookahead token; otherwise
+     * a syntax error is raised.
+     *
+     * @param array $tokens
+     *
+     * @return boolean
+     */
+    private function matchAny(array $tokens)
+    {
+        if ( ! $this->lexer->isNextTokenAny($tokens)) {
+            $this->syntaxError(implode(' or ', array_map(array($this->lexer, 'getLiteral'), $tokens)));
+        }
+
+        return $this->lexer->moveNext();
+    }
+
+    /**
+     * Generates a new syntax error.
+     *
+     * @param string     $expected Expected string.
+     * @param array|null $token    Optional token.
+     *
+     * @return void
+     *
+     * @throws AnnotationException
+     */
+    private function syntaxError($expected, $token = null)
+    {
+        if ($token === null) {
+            $token = $this->lexer->lookahead;
+        }
+
+        $message  = sprintf('Expected %s, got ', $expected);
+        $message .= ($this->lexer->lookahead === null)
+            ? 'end of string'
+            : sprintf("'%s' at position %s", $token['value'], $token['position']);
+
+        if (strlen($this->context)) {
+            $message .= ' in ' . $this->context;
+        }
+
+        $message .= '.';
+
+        throw AnnotationException::syntaxError($message);
+    }
+
+    /**
+     * Attempts to check if a class exists or not. This never goes through the PHP autoloading mechanism
+     * but uses the {@link AnnotationRegistry} to load classes.
+     *
+     * @param string $fqcn
+     *
+     * @return boolean
+     */
+    private function classExists($fqcn)
+    {
+        if (isset($this->classExists[$fqcn])) {
+            return $this->classExists[$fqcn];
+        }
+
+        // first check if the class already exists, maybe loaded through another AnnotationReader
+        if (class_exists($fqcn, false)) {
+            return $this->classExists[$fqcn] = true;
+        }
+
+        // final check, does this class exist?
+        return $this->classExists[$fqcn] = AnnotationRegistry::loadAnnotationClass($fqcn);
+    }
+
+    /**
+     * Collects parsing metadata for a given annotation class
+     *
+     * @param string $name The annotation name
+     *
+     * @return void
+     */
+    private function collectAnnotationMetadata($name)
+    {
+        if (self::$metadataParser === null) {
+            self::$metadataParser = new self();
+
+            self::$metadataParser->setIgnoreNotImportedAnnotations(true);
+            self::$metadataParser->setIgnoredAnnotationNames($this->ignoredAnnotationNames);
+            self::$metadataParser->setImports(array(
+                'enum'          => 'Doctrine\Common\Annotations\Annotation\Enum',
+                'target'        => 'Doctrine\Common\Annotations\Annotation\Target',
+                'attribute'     => 'Doctrine\Common\Annotations\Annotation\Attribute',
+                'attributes'    => 'Doctrine\Common\Annotations\Annotation\Attributes'
+            ));
+        }
+
+        $class      = new \ReflectionClass($name);
+        $docComment = $class->getDocComment();
+
+        // Sets default values for annotation metadata
+        $metadata = array(
+            'default_property' => null,
+            'has_constructor'  => (null !== $constructor = $class->getConstructor()) && $constructor->getNumberOfParameters() > 0,
+            'properties'       => array(),
+            'property_types'   => array(),
+            'attribute_types'  => array(),
+            'targets_literal'  => null,
+            'targets'          => Target::TARGET_ALL,
+            'is_annotation'    => false !== strpos($docComment, '@Annotation'),
+        );
+
+        // verify that the class is really meant to be an annotation
+        if ($metadata['is_annotation']) {
+            self::$metadataParser->setTarget(Target::TARGET_CLASS);
+
+            foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) {
+                if ($annotation instanceof Target) {
+                    $metadata['targets']         = $annotation->targets;
+                    $metadata['targets_literal'] = $annotation->literal;
+
+                    continue;
+                }
+
+                if ($annotation instanceof Attributes) {
+                    foreach ($annotation->value as $attribute) {
+                        $this->collectAttributeTypeMetadata($metadata, $attribute);
+                    }
+                }
+            }
+
+            // if not has a constructor will inject values into public properties
+            if (false === $metadata['has_constructor']) {
+                // collect all public properties
+                foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
+                    $metadata['properties'][$property->name] = $property->name;
+
+                    if (false === ($propertyComment = $property->getDocComment())) {
+                        continue;
+                    }
+
+                    $attribute = new Attribute();
+
+                    $attribute->required = (false !== strpos($propertyComment, '@Required'));
+                    $attribute->name     = $property->name;
+                    $attribute->type     = (false !== strpos($propertyComment, '@var') && preg_match('/@var\s+([^\s]+)/',$propertyComment, $matches))
+                        ? $matches[1]
+                        : 'mixed';
+
+                    $this->collectAttributeTypeMetadata($metadata, $attribute);
+
+                    // checks if the property has @Enum
+                    if (false !== strpos($propertyComment, '@Enum')) {
+                        $context = 'property ' . $class->name . "::\$" . $property->name;
+
+                        self::$metadataParser->setTarget(Target::TARGET_PROPERTY);
+
+                        foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) {
+                            if ( ! $annotation instanceof Enum) {
+                                continue;
+                            }
+
+                            $metadata['enum'][$property->name]['value']   = $annotation->value;
+                            $metadata['enum'][$property->name]['literal'] = ( ! empty($annotation->literal))
+                                ? $annotation->literal
+                                : $annotation->value;
+                        }
+                    }
+                }
+
+                // choose the first property as default property
+                $metadata['default_property'] = reset($metadata['properties']);
+            }
+        }
+
+        self::$annotationMetadata[$name] = $metadata;
+    }
+
+    /**
+     * Collects parsing metadata for a given attribute.
+     *
+     * @param array     $metadata
+     * @param Attribute $attribute
+     *
+     * @return void
+     */
+    private function collectAttributeTypeMetadata(&$metadata, Attribute $attribute)
+    {
+        // handle internal type declaration
+        $type = isset(self::$typeMap[$attribute->type])
+            ? self::$typeMap[$attribute->type]
+            : $attribute->type;
+
+        // handle the case if the property type is mixed
+        if ('mixed' === $type) {
+            return;
+        }
+
+        // Evaluate type
+        switch (true) {
+            // Checks if the property has array<type>
+            case (false !== $pos = strpos($type, '<')):
+                $arrayType  = substr($type, $pos + 1, -1);
+                $type       = 'array';
+
+                if (isset(self::$typeMap[$arrayType])) {
+                    $arrayType = self::$typeMap[$arrayType];
+                }
+
+                $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType;
+                break;
+
+            // Checks if the property has type[]
+            case (false !== $pos = strrpos($type, '[')):
+                $arrayType  = substr($type, 0, $pos);
+                $type       = 'array';
+
+                if (isset(self::$typeMap[$arrayType])) {
+                    $arrayType = self::$typeMap[$arrayType];
+                }
+
+                $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType;
+                break;
+        }
+
+        $metadata['attribute_types'][$attribute->name]['type']     = $type;
+        $metadata['attribute_types'][$attribute->name]['value']    = $attribute->type;
+        $metadata['attribute_types'][$attribute->name]['required'] = $attribute->required;
+    }
+
+    /**
+     * Annotations ::= Annotation {[ "*" ]* [Annotation]}*
+     *
+     * @return array
+     */
+    private function Annotations()
+    {
+        $annotations = array();
+
+        while (null !== $this->lexer->lookahead) {
+            if (DocLexer::T_AT !== $this->lexer->lookahead['type']) {
+                $this->lexer->moveNext();
+                continue;
+            }
+
+            // make sure the @ is preceded by non-catchable pattern
+            if (null !== $this->lexer->token && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen($this->lexer->token['value'])) {
+                $this->lexer->moveNext();
+                continue;
+            }
+
+            // make sure the @ is followed by either a namespace separator, or
+            // an identifier token
+            if ((null === $peek = $this->lexer->glimpse())
+                || (DocLexer::T_NAMESPACE_SEPARATOR !== $peek['type'] && !in_array($peek['type'], self::$classIdentifiers, true))
+                || $peek['position'] !== $this->lexer->lookahead['position'] + 1) {
+                $this->lexer->moveNext();
+                continue;
+            }
+
+            $this->isNestedAnnotation = false;
+            if (false !== $annot = $this->Annotation()) {
+                $annotations[] = $annot;
+            }
+        }
+
+        return $annotations;
+    }
+
+    /**
+     * Annotation     ::= "@" AnnotationName MethodCall
+     * AnnotationName ::= QualifiedName | SimpleName
+     * QualifiedName  ::= NameSpacePart "\" {NameSpacePart "\"}* SimpleName
+     * NameSpacePart  ::= identifier | null | false | true
+     * SimpleName     ::= identifier | null | false | true
+     *
+     * @return mixed False if it is not a valid annotation.
+     *
+     * @throws AnnotationException
+     */
+    private function Annotation()
+    {
+        $this->match(DocLexer::T_AT);
+
+        // check if we have an annotation
+        $name = $this->Identifier();
+
+        // only process names which are not fully qualified, yet
+        // fully qualified names must start with a \
+        $originalName = $name;
+
+        if ('\\' !== $name[0]) {
+            $alias = (false === $pos = strpos($name, '\\'))? $name : substr($name, 0, $pos);
+            $found = false;
+
+            if ($this->namespaces) {
+                if (isset($this->ignoredAnnotationNames[$name])) {
+                    return false;
+                }
+                foreach ($this->namespaces as $namespace) {
+                    if ($this->classExists($namespace.'\\'.$name)) {
+                        $name = $namespace.'\\'.$name;
+                        $found = true;
+                        break;
+                    }
+                }
+            } elseif (isset($this->imports[$loweredAlias = strtolower($alias)])) {
+                $found = true;
+                $name  = (false !== $pos)
+                    ? $this->imports[$loweredAlias] . substr($name, $pos)
+                    : $this->imports[$loweredAlias];
+            } elseif ( ! isset($this->ignoredAnnotationNames[$name])
+                && isset($this->imports['__NAMESPACE__'])
+                && $this->classExists($this->imports['__NAMESPACE__'] . '\\' . $name)
+            ) {
+                $name  = $this->imports['__NAMESPACE__'].'\\'.$name;
+                $found = true;
+            } elseif (! isset($this->ignoredAnnotationNames[$name]) && $this->classExists($name)) {
+                $found = true;
+            }
+
+            if ( ! $found) {
+                if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$name])) {
+                    return false;
+                }
+
+                throw AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s was never imported. Did you maybe forget to add a "use" statement for this annotation?', $name, $this->context));
+            }
+        }
+
+        if ( ! $this->classExists($name)) {
+            throw AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s does not exist, or could not be auto-loaded.', $name, $this->context));
+        }
+
+        // at this point, $name contains the fully qualified class name of the
+        // annotation, and it is also guaranteed that this class exists, and
+        // that it is loaded
+
+
+        // collects the metadata annotation only if there is not yet
+        if ( ! isset(self::$annotationMetadata[$name])) {
+            $this->collectAnnotationMetadata($name);
+        }
+
+        // verify that the class is really meant to be an annotation and not just any ordinary class
+        if (self::$annotationMetadata[$name]['is_annotation'] === false) {
+            if (isset($this->ignoredAnnotationNames[$originalName])) {
+                return false;
+            }
+
+            throw AnnotationException::semanticalError(sprintf('The class "%s" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "%s". If it is indeed no annotation, then you need to add @IgnoreAnnotation("%s") to the _class_ doc comment of %s.', $name, $name, $originalName, $this->context));
+        }
+
+        //if target is nested annotation
+        $target = $this->isNestedAnnotation ? Target::TARGET_ANNOTATION : $this->target;
+
+        // Next will be nested
+        $this->isNestedAnnotation = true;
+
+        //if annotation does not support current target
+        if (0 === (self::$annotationMetadata[$name]['targets'] & $target) && $target) {
+            throw AnnotationException::semanticalError(
+                sprintf('Annotation @%s is not allowed to be declared on %s. You may only use this annotation on these code elements: %s.',
+                     $originalName, $this->context, self::$annotationMetadata[$name]['targets_literal'])
+            );
+        }
+
+        $values = $this->MethodCall();
+
+        if (isset(self::$annotationMetadata[$name]['enum'])) {
+            // checks all declared attributes
+            foreach (self::$annotationMetadata[$name]['enum'] as $property => $enum) {
+                // checks if the attribute is a valid enumerator
+                if (isset($values[$property]) && ! in_array($values[$property], $enum['value'])) {
+                    throw AnnotationException::enumeratorError($property, $name, $this->context, $enum['literal'], $values[$property]);
+                }
+            }
+        }
+
+        // checks all declared attributes
+        foreach (self::$annotationMetadata[$name]['attribute_types'] as $property => $type) {
+            if ($property === self::$annotationMetadata[$name]['default_property']
+                && !isset($values[$property]) && isset($values['value'])) {
+                $property = 'value';
+            }
+
+            // handle a not given attribute or null value
+            if (!isset($values[$property])) {
+                if ($type['required']) {
+                    throw AnnotationException::requiredError($property, $originalName, $this->context, 'a(n) '.$type['value']);
+                }
+
+                continue;
+            }
+
+            if ($type['type'] === 'array') {
+                // handle the case of a single value
+                if ( ! is_array($values[$property])) {
+                    $values[$property] = array($values[$property]);
+                }
+
+                // checks if the attribute has array type declaration, such as "array<string>"
+                if (isset($type['array_type'])) {
+                    foreach ($values[$property] as $item) {
+                        if (gettype($item) !== $type['array_type'] && !$item instanceof $type['array_type']) {
+                            throw AnnotationException::attributeTypeError($property, $originalName, $this->context, 'either a(n) '.$type['array_type'].', or an array of '.$type['array_type'].'s', $item);
+                        }
+                    }
+                }
+            } elseif (gettype($values[$property]) !== $type['type'] && !$values[$property] instanceof $type['type']) {
+                throw AnnotationException::attributeTypeError($property, $originalName, $this->context, 'a(n) '.$type['value'], $values[$property]);
+            }
+        }
+
+        // check if the annotation expects values via the constructor,
+        // or directly injected into public properties
+        if (self::$annotationMetadata[$name]['has_constructor'] === true) {
+            return new $name($values);
+        }
+
+        $instance = new $name();
+
+        foreach ($values as $property => $value) {
+            if (!isset(self::$annotationMetadata[$name]['properties'][$property])) {
+                if ('value' !== $property) {
+                    throw AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not have a property named "%s". Available properties: %s', $originalName, $this->context, $property, implode(', ', self::$annotationMetadata[$name]['properties'])));
+                }
+
+                // handle the case if the property has no annotations
+                if ( ! $property = self::$annotationMetadata[$name]['default_property']) {
+                    throw AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not accept any values, but got %s.', $originalName, $this->context, json_encode($values)));
+                }
+            }
+
+            $instance->{$property} = $value;
+        }
+
+        return $instance;
+    }
+
+    /**
+     * MethodCall ::= ["(" [Values] ")"]
+     *
+     * @return array
+     */
+    private function MethodCall()
+    {
+        $values = array();
+
+        if ( ! $this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) {
+            return $values;
+        }
+
+        $this->match(DocLexer::T_OPEN_PARENTHESIS);
+
+        if ( ! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) {
+            $values = $this->Values();
+        }
+
+        $this->match(DocLexer::T_CLOSE_PARENTHESIS);
+
+        return $values;
+    }
+
+    /**
+     * Values ::= Array | Value {"," Value}* [","]
+     *
+     * @return array
+     */
+    private function Values()
+    {
+        $values = array($this->Value());
+
+        while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
+            $this->match(DocLexer::T_COMMA);
+
+            if ($this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) {
+                break;
+            }
+
+            $token = $this->lexer->lookahead;
+            $value = $this->Value();
+
+            if ( ! is_object($value) && ! is_array($value)) {
+                $this->syntaxError('Value', $token);
+            }
+
+            $values[] = $value;
+        }
+
+        foreach ($values as $k => $value) {
+            if (is_object($value) && $value instanceof \stdClass) {
+                $values[$value->name] = $value->value;
+            } else if ( ! isset($values['value'])){
+                $values['value'] = $value;
+            } else {
+                if ( ! is_array($values['value'])) {
+                    $values['value'] = array($values['value']);
+                }
+
+                $values['value'][] = $value;
+            }
+
+            unset($values[$k]);
+        }
+
+        return $values;
+    }
+
+    /**
+     * Constant ::= integer | string | float | boolean
+     *
+     * @return mixed
+     *
+     * @throws AnnotationException
+     */
+    private function Constant()
+    {
+        $identifier = $this->Identifier();
+
+        if ( ! defined($identifier) && false !== strpos($identifier, '::') && '\\' !== $identifier[0]) {
+            list($className, $const) = explode('::', $identifier);
+
+            $alias = (false === $pos = strpos($className, '\\')) ? $className : substr($className, 0, $pos);
+            $found = false;
+
+            switch (true) {
+                case !empty ($this->namespaces):
+                    foreach ($this->namespaces as $ns) {
+                        if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) {
+                             $className = $ns.'\\'.$className;
+                             $found = true;
+                             break;
+                        }
+                    }
+                    break;
+
+                case isset($this->imports[$loweredAlias = strtolower($alias)]):
+                    $found     = true;
+                    $className = (false !== $pos)
+                        ? $this->imports[$loweredAlias] . substr($className, $pos)
+                        : $this->imports[$loweredAlias];
+                    break;
+
+                default:
+                    if(isset($this->imports['__NAMESPACE__'])) {
+                        $ns = $this->imports['__NAMESPACE__'];
+
+                        if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) {
+                            $className = $ns.'\\'.$className;
+                            $found = true;
+                        }
+                    }
+                    break;
+            }
+
+            if ($found) {
+                 $identifier = $className . '::' . $const;
+            }
+        }
+
+        // checks if identifier ends with ::class, \strlen('::class') === 7
+        $classPos = stripos($identifier, '::class');
+        if ($classPos === strlen($identifier) - 7) {
+            return substr($identifier, 0, $classPos);
+        }
+
+        if (!defined($identifier)) {
+            throw AnnotationException::semanticalErrorConstants($identifier, $this->context);
+        }
+
+        return constant($identifier);
+    }
+
+    /**
+     * Identifier ::= string
+     *
+     * @return string
+     */
+    private function Identifier()
+    {
+        // check if we have an annotation
+        if ( ! $this->lexer->isNextTokenAny(self::$classIdentifiers)) {
+            $this->syntaxError('namespace separator or identifier');
+        }
+
+        $this->lexer->moveNext();
+
+        $className = $this->lexer->token['value'];
+
+        while (
+            null !== $this->lexer->lookahead &&
+            $this->lexer->lookahead['position'] === ($this->lexer->token['position'] + strlen($this->lexer->token['value'])) &&
+            $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)
+        ) {
+            $this->match(DocLexer::T_NAMESPACE_SEPARATOR);
+            $this->matchAny(self::$classIdentifiers);
+
+            $className .= '\\' . $this->lexer->token['value'];
+        }
+
+        return $className;
+    }
+
+    /**
+     * Value ::= PlainValue | FieldAssignment
+     *
+     * @return mixed
+     */
+    private function Value()
+    {
+        $peek = $this->lexer->glimpse();
+
+        if (DocLexer::T_EQUALS === $peek['type']) {
+            return $this->FieldAssignment();
+        }
+
+        return $this->PlainValue();
+    }
+
+    /**
+     * PlainValue ::= integer | string | float | boolean | Array | Annotation
+     *
+     * @return mixed
+     */
+    private function PlainValue()
+    {
+        if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) {
+            return $this->Arrayx();
+        }
+
+        if ($this->lexer->isNextToken(DocLexer::T_AT)) {
+            return $this->Annotation();
+        }
+
+        if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
+            return $this->Constant();
+        }
+
+        switch ($this->lexer->lookahead['type']) {
+            case DocLexer::T_STRING:
+                $this->match(DocLexer::T_STRING);
+                return $this->lexer->token['value'];
+
+            case DocLexer::T_INTEGER:
+                $this->match(DocLexer::T_INTEGER);
+                return (int)$this->lexer->token['value'];
+
+            case DocLexer::T_FLOAT:
+                $this->match(DocLexer::T_FLOAT);
+                return (float)$this->lexer->token['value'];
+
+            case DocLexer::T_TRUE:
+                $this->match(DocLexer::T_TRUE);
+                return true;
+
+            case DocLexer::T_FALSE:
+                $this->match(DocLexer::T_FALSE);
+                return false;
+
+            case DocLexer::T_NULL:
+                $this->match(DocLexer::T_NULL);
+                return null;
+
+            default:
+                $this->syntaxError('PlainValue');
+        }
+    }
+
+    /**
+     * FieldAssignment ::= FieldName "=" PlainValue
+     * FieldName ::= identifier
+     *
+     * @return array
+     */
+    private function FieldAssignment()
+    {
+        $this->match(DocLexer::T_IDENTIFIER);
+        $fieldName = $this->lexer->token['value'];
+
+        $this->match(DocLexer::T_EQUALS);
+
+        $item = new \stdClass();
+        $item->name  = $fieldName;
+        $item->value = $this->PlainValue();
+
+        return $item;
+    }
+
+    /**
+     * Array ::= "{" ArrayEntry {"," ArrayEntry}* [","] "}"
+     *
+     * @return array
+     */
+    private function Arrayx()
+    {
+        $array = $values = array();
+
+        $this->match(DocLexer::T_OPEN_CURLY_BRACES);
+
+        // If the array is empty, stop parsing and return.
+        if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) {
+            $this->match(DocLexer::T_CLOSE_CURLY_BRACES);
+
+            return $array;
+        }
+
+        $values[] = $this->ArrayEntry();
+
+        while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
+            $this->match(DocLexer::T_COMMA);
+
+            // optional trailing comma
+            if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) {
+                break;
+            }
+
+            $values[] = $this->ArrayEntry();
+        }
+
+        $this->match(DocLexer::T_CLOSE_CURLY_BRACES);
+
+        foreach ($values as $value) {
+            list ($key, $val) = $value;
+
+            if ($key !== null) {
+                $array[$key] = $val;
+            } else {
+                $array[] = $val;
+            }
+        }
+
+        return $array;
+    }
+
+    /**
+     * ArrayEntry ::= Value | KeyValuePair
+     * KeyValuePair ::= Key ("=" | ":") PlainValue | Constant
+     * Key ::= string | integer | Constant
+     *
+     * @return array
+     */
+    private function ArrayEntry()
+    {
+        $peek = $this->lexer->glimpse();
+
+        if (DocLexer::T_EQUALS === $peek['type']
+                || DocLexer::T_COLON === $peek['type']) {
+
+            if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
+                $key = $this->Constant();
+            } else {
+                $this->matchAny(array(DocLexer::T_INTEGER, DocLexer::T_STRING));
+                $key = $this->lexer->token['value'];
+            }
+
+            $this->matchAny(array(DocLexer::T_EQUALS, DocLexer::T_COLON));
+
+            return array($key, $this->PlainValue());
+        }
+
+        return array(null, $this->Value());
+    }
+}

+ 161 - 0
web/core/lib/Drupal/Component/Annotation/Doctrine/SimpleAnnotationReader.php

@@ -0,0 +1,161 @@
+<?php
+// @codingStandardsIgnoreFile
+
+/**
+ * @file
+ *
+ * This class is a near-copy of
+ * Doctrine\Common\Annotations\SimpleAnnotationReader, which is part of the
+ * Doctrine project: <http://www.doctrine-project.org>. It was copied from
+ * version 1.2.7.
+ *
+ * Original copyright:
+ *
+ * Copyright (c) 2006-2013 Doctrine Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ */
+
+namespace Drupal\Component\Annotation\Doctrine;
+
+use Doctrine\Common\Annotations\Reader;
+
+/**
+ * Simple Annotation Reader.
+ *
+ * Drupal adds its own version of DocParser and allows for ignoring common
+ * annotations.
+ *
+ * @internal
+ */
+final class SimpleAnnotationReader implements Reader
+{
+
+    protected $ignoredAnnotations = [
+      'addtogroup' => TRUE,
+      'code' => TRUE,
+      'defgroup' => TRUE,
+      'deprecated' => TRUE,
+      'endcode' => TRUE,
+      'endlink' => TRUE,
+      'file' => TRUE,
+      'ingroup' => TRUE,
+      'group' => TRUE,
+      'link' => TRUE,
+      'mainpage' => TRUE,
+      'param' => TRUE,
+      'ref' => TRUE,
+      'return' => TRUE,
+      'section' => TRUE,
+      'see' => TRUE,
+      'subsection' => TRUE,
+      'throws' => TRUE,
+      'todo' => TRUE,
+      'var' => TRUE,
+      '{' => TRUE,
+      '}' => TRUE,
+    ];
+
+    /**
+     * @var DocParser
+     */
+    private $parser;
+
+    /**
+     * Constructor.
+     *
+     * Initializes a new SimpleAnnotationReader.
+     */
+    public function __construct()
+    {
+        $this->parser = new DocParser();
+        $this->parser->setIgnoreNotImportedAnnotations(true);
+        $this->parser->setIgnoredAnnotationNames($this->ignoredAnnotations);
+    }
+
+    /**
+     * Adds a namespace in which we will look for annotations.
+     *
+     * @param string $namespace
+     *
+     * @return void
+     */
+    public function addNamespace($namespace)
+    {
+        $this->parser->addNamespace($namespace);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getClassAnnotations(\ReflectionClass $class)
+    {
+        return $this->parser->parse($class->getDocComment(), 'class '.$class->getName());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethodAnnotations(\ReflectionMethod $method)
+    {
+        return $this->parser->parse($method->getDocComment(), 'method '.$method->getDeclaringClass()->name.'::'.$method->getName().'()');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPropertyAnnotations(\ReflectionProperty $property)
+    {
+        return $this->parser->parse($property->getDocComment(), 'property '.$property->getDeclaringClass()->name.'::$'.$property->getName());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getClassAnnotation(\ReflectionClass $class, $annotationName)
+    {
+        foreach ($this->getClassAnnotations($class) as $annot) {
+            if ($annot instanceof $annotationName) {
+                return $annot;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
+    {
+        foreach ($this->getMethodAnnotations($method) as $annot) {
+            if ($annot instanceof $annotationName) {
+                return $annot;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
+    {
+        foreach ($this->getPropertyAnnotations($property) as $annot) {
+            if ($annot instanceof $annotationName) {
+                return $annot;
+            }
+        }
+
+        return null;
+    }
+}

+ 339 - 0
web/core/lib/Drupal/Component/Annotation/LICENSE.txt

@@ -0,0 +1,339 @@
+        GNU GENERAL PUBLIC LICENSE
+           Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+          Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+        GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+          NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+         END OF TERMS AND CONDITIONS
+
+      How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.

+ 112 - 0
web/core/lib/Drupal/Component/Annotation/Plugin.php

@@ -0,0 +1,112 @@
+<?php
+
+namespace Drupal\Component\Annotation;
+
+use Drupal\Component\Utility\NestedArray;
+
+/**
+ * Defines a Plugin annotation object.
+ *
+ * Annotations in plugin classes can use this class in order to pass various
+ * metadata about the plugin through the parser to
+ * DiscoveryInterface::getDefinitions() calls. This allows the metadata
+ * of a class to be located with the class itself, rather than in module-based
+ * info hooks.
+ *
+ * @ingroup plugin_api
+ *
+ * @Annotation
+ */
+class Plugin implements AnnotationInterface {
+
+  /**
+   * The plugin definition read from the class annotation.
+   *
+   * @var array
+   */
+  protected $definition;
+
+  /**
+   * Constructs a Plugin object.
+   *
+   * Builds up the plugin definition and invokes the get() method for any
+   * classed annotations that were used.
+   */
+  public function __construct($values) {
+    $reflection = new \ReflectionClass($this);
+    // Only keep actual default values by ignoring NULL values.
+    $defaults = array_filter($reflection->getDefaultProperties(), function ($value) {
+      return $value !== NULL;
+    });
+    $parsed_values = $this->parse($values);
+    $this->definition = NestedArray::mergeDeep($defaults, $parsed_values);
+  }
+
+  /**
+   * Parses an annotation into its definition.
+   *
+   * @param array $values
+   *   The annotation array.
+   *
+   * @return array
+   *   The parsed annotation as a definition.
+   */
+  protected function parse(array $values) {
+    $definitions = [];
+    foreach ($values as $key => $value) {
+      if ($value instanceof AnnotationInterface) {
+        $definitions[$key] = $value->get();
+      }
+      elseif (is_array($value)) {
+        $definitions[$key] = $this->parse($value);
+      }
+      else {
+        $definitions[$key] = $value;
+      }
+    }
+    return $definitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get() {
+    return $this->definition;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getProvider() {
+    return isset($this->definition['provider']) ? $this->definition['provider'] : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setProvider($provider) {
+    $this->definition['provider'] = $provider;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getId() {
+    return $this->definition['id'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClass() {
+    return $this->definition['class'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setClass($class) {
+    $this->definition['class'] = $class;
+  }
+
+}

+ 191 - 0
web/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php

@@ -0,0 +1,191 @@
+<?php
+
+namespace Drupal\Component\Annotation\Plugin\Discovery;
+
+use Drupal\Component\Annotation\AnnotationInterface;
+use Drupal\Component\FileCache\FileCacheFactory;
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Component\Annotation\Doctrine\SimpleAnnotationReader;
+use Drupal\Component\Annotation\Reflection\MockFileFinder;
+use Doctrine\Common\Annotations\AnnotationRegistry;
+use Doctrine\Common\Reflection\StaticReflectionParser;
+use Drupal\Component\Plugin\Discovery\DiscoveryTrait;
+use Drupal\Component\Utility\Crypt;
+
+/**
+ * Defines a discovery mechanism to find annotated plugins in PSR-4 namespaces.
+ */
+class AnnotatedClassDiscovery implements DiscoveryInterface {
+
+  use DiscoveryTrait;
+
+  /**
+   * The namespaces within which to find plugin classes.
+   *
+   * @var string[]
+   */
+  protected $pluginNamespaces;
+
+  /**
+   * The name of the annotation that contains the plugin definition.
+   *
+   * The class corresponding to this name must implement
+   * \Drupal\Component\Annotation\AnnotationInterface.
+   *
+   * @var string
+   */
+  protected $pluginDefinitionAnnotationName;
+
+  /**
+   * The doctrine annotation reader.
+   *
+   * @var \Doctrine\Common\Annotations\Reader
+   */
+  protected $annotationReader;
+
+  /**
+   * Additional namespaces to be scanned for annotation classes.
+   *
+   * @var string[]
+   */
+  protected $annotationNamespaces = [];
+
+  /**
+   * The file cache object.
+   *
+   * @var \Drupal\Component\FileCache\FileCacheInterface
+   */
+  protected $fileCache;
+
+  /**
+   * Constructs a new instance.
+   *
+   * @param string[] $plugin_namespaces
+   *   (optional) An array of namespace that may contain plugin implementations.
+   *   Defaults to an empty array.
+   * @param string $plugin_definition_annotation_name
+   *   (optional) The name of the annotation that contains the plugin definition.
+   *   Defaults to 'Drupal\Component\Annotation\Plugin'.
+   * @param string[] $annotation_namespaces
+   *   (optional) Additional namespaces to be scanned for annotation classes.
+   */
+  public function __construct($plugin_namespaces = [], $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin', array $annotation_namespaces = []) {
+    $this->pluginNamespaces = $plugin_namespaces;
+    $this->pluginDefinitionAnnotationName = $plugin_definition_annotation_name;
+    $this->annotationNamespaces = $annotation_namespaces;
+
+    $file_cache_suffix = str_replace('\\', '_', $plugin_definition_annotation_name);
+    $file_cache_suffix .= ':' . Crypt::hashBase64(serialize($annotation_namespaces));
+    $this->fileCache = FileCacheFactory::get('annotation_discovery:' . $file_cache_suffix);
+  }
+
+  /**
+   * Gets the used doctrine annotation reader.
+   *
+   * @return \Doctrine\Common\Annotations\Reader
+   *   The annotation reader.
+   */
+  protected function getAnnotationReader() {
+    if (!isset($this->annotationReader)) {
+      $this->annotationReader = new SimpleAnnotationReader();
+
+      // Add the namespaces from the main plugin annotation, like @EntityType.
+      $namespace = substr($this->pluginDefinitionAnnotationName, 0, strrpos($this->pluginDefinitionAnnotationName, '\\'));
+      $this->annotationReader->addNamespace($namespace);
+
+      // Register additional namespaces to be scanned for annotations.
+      foreach ($this->annotationNamespaces as $namespace) {
+        $this->annotationReader->addNamespace($namespace);
+      }
+    }
+    return $this->annotationReader;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinitions() {
+    $definitions = [];
+
+    $reader = $this->getAnnotationReader();
+
+    // Clear the annotation loaders of any previous annotation classes.
+    AnnotationRegistry::reset();
+    // Register the namespaces of classes that can be used for annotations.
+    AnnotationRegistry::registerLoader('class_exists');
+
+    // Search for classes within all PSR-4 namespace locations.
+    foreach ($this->getPluginNamespaces() as $namespace => $dirs) {
+      foreach ($dirs as $dir) {
+        if (file_exists($dir)) {
+          $iterator = new \RecursiveIteratorIterator(
+            new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS)
+          );
+          foreach ($iterator as $fileinfo) {
+            if ($fileinfo->getExtension() == 'php') {
+              if ($cached = $this->fileCache->get($fileinfo->getPathName())) {
+                if (isset($cached['id'])) {
+                  // Explicitly unserialize this to create a new object instance.
+                  $definitions[$cached['id']] = unserialize($cached['content']);
+                }
+                continue;
+              }
+
+              $sub_path = $iterator->getSubIterator()->getSubPath();
+              $sub_path = $sub_path ? str_replace(DIRECTORY_SEPARATOR, '\\', $sub_path) . '\\' : '';
+              $class = $namespace . '\\' . $sub_path . $fileinfo->getBasename('.php');
+
+              // The filename is already known, so there is no need to find the
+              // file. However, StaticReflectionParser needs a finder, so use a
+              // mock version.
+              $finder = MockFileFinder::create($fileinfo->getPathName());
+              $parser = new StaticReflectionParser($class, $finder, TRUE);
+
+              /** @var $annotation \Drupal\Component\Annotation\AnnotationInterface */
+              if ($annotation = $reader->getClassAnnotation($parser->getReflectionClass(), $this->pluginDefinitionAnnotationName)) {
+                $this->prepareAnnotationDefinition($annotation, $class);
+
+                $id = $annotation->getId();
+                $content = $annotation->get();
+                $definitions[$id] = $content;
+                // Explicitly serialize this to create a new object instance.
+                $this->fileCache->set($fileinfo->getPathName(), ['id' => $id, 'content' => serialize($content)]);
+              }
+              else {
+                // Store a NULL object, so the file is not reparsed again.
+                $this->fileCache->set($fileinfo->getPathName(), [NULL]);
+              }
+            }
+          }
+        }
+      }
+    }
+
+    // Don't let annotation loaders pile up.
+    AnnotationRegistry::reset();
+
+    return $definitions;
+  }
+
+  /**
+   * Prepares the annotation definition.
+   *
+   * @param \Drupal\Component\Annotation\AnnotationInterface $annotation
+   *   The annotation derived from the plugin.
+   * @param string $class
+   *   The class used for the plugin.
+   */
+  protected function prepareAnnotationDefinition(AnnotationInterface $annotation, $class) {
+    $annotation->setClass($class);
+  }
+
+  /**
+   * Gets an array of PSR-4 namespaces to search for plugin classes.
+   *
+   * @return string[]
+   */
+  protected function getPluginNamespaces() {
+    return $this->pluginNamespaces;
+  }
+
+}

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