Update to Drupal 8.0-dev-2015-11-17. Commits through da81cd220, Tue Nov 17 15:53:49 2015 +0000, Issue #2617224 by Wim Leers: Move around/fix some documentation.

This commit is contained in:
Pantheon Automation 2015-11-17 13:42:33 -08:00 committed by Greg Anderson
parent 4afb23bbd3
commit 7784f4c23d
929 changed files with 19798 additions and 5304 deletions

4
.gitignore vendored
View file

@ -50,3 +50,7 @@ Thumbs.db
# SASS #
##########
.sass-cache
# Things in the core directory that Drupal 8 commits in the repository.
!core/**/*.gz

View file

@ -15,9 +15,6 @@
# Don't show directory listings for URLs which map to a directory.
Options -Indexes
# Follow symbolic links in this directory.
Options +FollowSymLinks
# Set the default handler.
DirectoryIndex index.php index.html index.htm

View file

@ -5,7 +5,7 @@
"license": "GPL-2.0+",
"require": {
"composer/installers": "^1.0.21",
"wikimedia/composer-merge-plugin": "^1.3.0"
"wikimedia/composer-merge-plugin": "~1.3"
},
"replace": {
"drupal/core": "~8.0"

222
composer.lock generated
View file

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "2be29019515c847055593ea41b88475d",
"content-hash": "f38613812a285c03a1a18458384fe0b1",
"hash": "dac77f10c1f7585fd1f7344c6a376338",
"content-hash": "73cbcb262208c5d802cb528279f2a95c",
"packages": [
{
"name": "composer/installers",
@ -1107,24 +1107,23 @@
},
{
"name": "symfony/class-loader",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/class-loader.git",
"reference": "d957ea6295d7016e20d7eff33a6c1deef819c0d4"
"reference": "320f8d2a9cdbcbeb24be602c124aae9d998474a4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/class-loader/zipball/d957ea6295d7016e20d7eff33a6c1deef819c0d4",
"reference": "d957ea6295d7016e20d7eff33a6c1deef819c0d4",
"url": "https://api.github.com/repos/symfony/class-loader/zipball/320f8d2a9cdbcbeb24be602c124aae9d998474a4",
"reference": "320f8d2a9cdbcbeb24be602c124aae9d998474a4",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/finder": "~2.0,>=2.0.5",
"symfony/phpunit-bridge": "~2.7"
"symfony/finder": "~2.0,>=2.0.5"
},
"type": "library",
"extra": {
@ -1153,20 +1152,20 @@
],
"description": "Symfony ClassLoader Component",
"homepage": "https://symfony.com",
"time": "2015-08-26 17:56:37"
"time": "2015-10-23 14:47:27"
},
{
"name": "symfony/console",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "06cb17c013a82f94a3d840682b49425cd00a2161"
"reference": "5efd632294c8320ea52492db22292ff853a43766"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/06cb17c013a82f94a3d840682b49425cd00a2161",
"reference": "06cb17c013a82f94a3d840682b49425cd00a2161",
"url": "https://api.github.com/repos/symfony/console/zipball/5efd632294c8320ea52492db22292ff853a43766",
"reference": "5efd632294c8320ea52492db22292ff853a43766",
"shasum": ""
},
"require": {
@ -1175,7 +1174,6 @@
"require-dev": {
"psr/log": "~1.0",
"symfony/event-dispatcher": "~2.1",
"symfony/phpunit-bridge": "~2.7",
"symfony/process": "~2.1"
},
"suggest": {
@ -1210,20 +1208,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2015-09-25 08:32:23"
"time": "2015-10-20 14:38:46"
},
{
"name": "symfony/debug",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/debug.git",
"reference": "c79c361bca8e5ada6a47603875a3c964d03b67b1"
"reference": "fb9e6887db716939f41af0ba8ef38a1582eb501b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/debug/zipball/c79c361bca8e5ada6a47603875a3c964d03b67b1",
"reference": "c79c361bca8e5ada6a47603875a3c964d03b67b1",
"url": "https://api.github.com/repos/symfony/debug/zipball/fb9e6887db716939f41af0ba8ef38a1582eb501b",
"reference": "fb9e6887db716939f41af0ba8ef38a1582eb501b",
"shasum": ""
},
"require": {
@ -1235,8 +1233,7 @@
},
"require-dev": {
"symfony/class-loader": "~2.2",
"symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2",
"symfony/phpunit-bridge": "~2.7"
"symfony/http-kernel": "~2.3.24|~2.5.9|~2.6,>=2.6.2"
},
"type": "library",
"extra": {
@ -1265,20 +1262,20 @@
],
"description": "Symfony Debug Component",
"homepage": "https://symfony.com",
"time": "2015-09-14 08:41:38"
"time": "2015-10-11 09:39:48"
},
{
"name": "symfony/dependency-injection",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
"reference": "422c3819b110f610d79c6f1dc38af23787dc790e"
"reference": "af284e795ec8a08c80d1fc47518fd23004b89847"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/422c3819b110f610d79c6f1dc38af23787dc790e",
"reference": "422c3819b110f610d79c6f1dc38af23787dc790e",
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/af284e795ec8a08c80d1fc47518fd23004b89847",
"reference": "af284e795ec8a08c80d1fc47518fd23004b89847",
"shasum": ""
},
"require": {
@ -1290,7 +1287,6 @@
"require-dev": {
"symfony/config": "~2.2",
"symfony/expression-language": "~2.6",
"symfony/phpunit-bridge": "~2.7",
"symfony/yaml": "~2.1"
},
"suggest": {
@ -1325,20 +1321,20 @@
],
"description": "Symfony DependencyInjection Component",
"homepage": "https://symfony.com",
"time": "2015-09-15 08:30:42"
"time": "2015-10-27 15:38:06"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "ae4dcc2a8d3de98bd794167a3ccda1311597c5d9"
"reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ae4dcc2a8d3de98bd794167a3ccda1311597c5d9",
"reference": "ae4dcc2a8d3de98bd794167a3ccda1311597c5d9",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87a5db5ea887763fa3a31a5471b512ff1596d9b8",
"reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8",
"shasum": ""
},
"require": {
@ -1349,7 +1345,6 @@
"symfony/config": "~2.0,>=2.0.5",
"symfony/dependency-injection": "~2.6",
"symfony/expression-language": "~2.6",
"symfony/phpunit-bridge": "~2.7",
"symfony/stopwatch": "~2.3"
},
"suggest": {
@ -1383,28 +1378,27 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
"time": "2015-09-22 13:49:29"
"time": "2015-10-11 09:39:48"
},
{
"name": "symfony/http-foundation",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "e1509119f164a0d0a940d7d924d693a7a28a5470"
"reference": "7598eea151ae3d4134df1f9957364b17809eea75"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/e1509119f164a0d0a940d7d924d693a7a28a5470",
"reference": "e1509119f164a0d0a940d7d924d693a7a28a5470",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/7598eea151ae3d4134df1f9957364b17809eea75",
"reference": "7598eea151ae3d4134df1f9957364b17809eea75",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/expression-language": "~2.4",
"symfony/phpunit-bridge": "~2.7"
"symfony/expression-language": "~2.4"
},
"type": "library",
"extra": {
@ -1436,20 +1430,20 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
"time": "2015-09-22 13:49:29"
"time": "2015-10-23 14:47:27"
},
{
"name": "symfony/http-kernel",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "353aa457424262d7d4e4289ea483145921cffcb5"
"reference": "4260f2273a446a6715063dc9ca89fd0c475c2f77"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/353aa457424262d7d4e4289ea483145921cffcb5",
"reference": "353aa457424262d7d4e4289ea483145921cffcb5",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/4260f2273a446a6715063dc9ca89fd0c475c2f77",
"reference": "4260f2273a446a6715063dc9ca89fd0c475c2f77",
"shasum": ""
},
"require": {
@ -1472,7 +1466,6 @@
"symfony/dom-crawler": "~2.0,>=2.0.5",
"symfony/expression-language": "~2.4",
"symfony/finder": "~2.0,>=2.0.5",
"symfony/phpunit-bridge": "~2.7",
"symfony/process": "~2.0,>=2.0.5",
"symfony/routing": "~2.2",
"symfony/stopwatch": "~2.3",
@ -1516,28 +1509,25 @@
],
"description": "Symfony HttpKernel Component",
"homepage": "https://symfony.com",
"time": "2015-09-25 11:16:52"
"time": "2015-10-27 19:07:21"
},
{
"name": "symfony/process",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "b27c8e317922cd3cdd3600850273cf6b82b2e8e9"
"reference": "4a959dd4e19c2c5d7512689413921e0a74386ec7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/b27c8e317922cd3cdd3600850273cf6b82b2e8e9",
"reference": "b27c8e317922cd3cdd3600850273cf6b82b2e8e9",
"url": "https://api.github.com/repos/symfony/process/zipball/4a959dd4e19c2c5d7512689413921e0a74386ec7",
"reference": "4a959dd4e19c2c5d7512689413921e0a74386ec7",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
@ -1565,7 +1555,7 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2015-09-19 19:59:23"
"time": "2015-10-23 14:47:27"
},
{
"name": "symfony/psr-http-message-bridge",
@ -1623,16 +1613,16 @@
},
{
"name": "symfony/routing",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
"reference": "6c5fae83efa20baf166fcf4582f57094e9f60f16"
"reference": "f353e1f588679c3ec987624e6c617646bd01ba38"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/routing/zipball/6c5fae83efa20baf166fcf4582f57094e9f60f16",
"reference": "6c5fae83efa20baf166fcf4582f57094e9f60f16",
"url": "https://api.github.com/repos/symfony/routing/zipball/f353e1f588679c3ec987624e6c617646bd01ba38",
"reference": "f353e1f588679c3ec987624e6c617646bd01ba38",
"shasum": ""
},
"require": {
@ -1648,7 +1638,6 @@
"symfony/config": "~2.7",
"symfony/expression-language": "~2.4",
"symfony/http-foundation": "~2.3",
"symfony/phpunit-bridge": "~2.7",
"symfony/yaml": "~2.0,>=2.0.5"
},
"suggest": {
@ -1690,20 +1679,20 @@
"uri",
"url"
],
"time": "2015-09-14 14:14:09"
"time": "2015-10-27 15:38:06"
},
{
"name": "symfony/serializer",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/serializer.git",
"reference": "baf24f86a8656eea9c80988f332e51461bfcb67f"
"reference": "14056684acad23b8815eb336bccc0b4ac76bd823"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/serializer/zipball/baf24f86a8656eea9c80988f332e51461bfcb67f",
"reference": "baf24f86a8656eea9c80988f332e51461bfcb67f",
"url": "https://api.github.com/repos/symfony/serializer/zipball/14056684acad23b8815eb336bccc0b4ac76bd823",
"reference": "14056684acad23b8815eb336bccc0b4ac76bd823",
"shasum": ""
},
"require": {
@ -1713,7 +1702,6 @@
"doctrine/annotations": "~1.0",
"doctrine/cache": "~1.0",
"symfony/config": "~2.2",
"symfony/phpunit-bridge": "~2.7",
"symfony/property-access": "~2.3",
"symfony/yaml": "~2.0,>=2.0.5"
},
@ -1751,20 +1739,20 @@
],
"description": "Symfony Serializer Component",
"homepage": "https://symfony.com",
"time": "2015-08-31 16:44:53"
"time": "2015-10-11 09:39:48"
},
{
"name": "symfony/translation",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "485877661835e188cd78345c6d4eef1290d17571"
"reference": "6ccd9289ec1c71d01a49d83480de3b5293ce30c8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/485877661835e188cd78345c6d4eef1290d17571",
"reference": "485877661835e188cd78345c6d4eef1290d17571",
"url": "https://api.github.com/repos/symfony/translation/zipball/6ccd9289ec1c71d01a49d83480de3b5293ce30c8",
"reference": "6ccd9289ec1c71d01a49d83480de3b5293ce30c8",
"shasum": ""
},
"require": {
@ -1777,7 +1765,6 @@
"psr/log": "~1.0",
"symfony/config": "~2.7",
"symfony/intl": "~2.4",
"symfony/phpunit-bridge": "~2.7",
"symfony/yaml": "~2.2"
},
"suggest": {
@ -1812,20 +1799,20 @@
],
"description": "Symfony Translation Component",
"homepage": "https://symfony.com",
"time": "2015-09-06 08:36:38"
"time": "2015-10-27 15:38:06"
},
{
"name": "symfony/validator",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/validator.git",
"reference": "b359dc71e253ce6eb69eefbd5088032241e7a66f"
"reference": "df9021e689aa3d08367881e7f8917219fabe5e64"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/validator/zipball/b359dc71e253ce6eb69eefbd5088032241e7a66f",
"reference": "b359dc71e253ce6eb69eefbd5088032241e7a66f",
"url": "https://api.github.com/repos/symfony/validator/zipball/df9021e689aa3d08367881e7f8917219fabe5e64",
"reference": "df9021e689aa3d08367881e7f8917219fabe5e64",
"shasum": ""
},
"require": {
@ -1835,12 +1822,12 @@
"require-dev": {
"doctrine/annotations": "~1.0",
"doctrine/cache": "~1.0",
"doctrine/common": "~2.3",
"egulias/email-validator": "~1.2,>=1.2.1",
"symfony/config": "~2.2",
"symfony/expression-language": "~2.4",
"symfony/http-foundation": "~2.1",
"symfony/intl": "~2.4",
"symfony/phpunit-bridge": "~2.7",
"symfony/property-access": "~2.3",
"symfony/yaml": "~2.0,>=2.0.5"
},
@ -1882,28 +1869,25 @@
],
"description": "Symfony Validator Component",
"homepage": "https://symfony.com",
"time": "2015-09-23 11:13:27"
"time": "2015-10-18 20:23:18"
},
{
"name": "symfony/yaml",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "31cb2ad0155c95b88ee55fe12bc7ff92232c1770"
"reference": "eca9019c88fbe250164affd107bc8057771f3f4d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/31cb2ad0155c95b88ee55fe12bc7ff92232c1770",
"reference": "31cb2ad0155c95b88ee55fe12bc7ff92232c1770",
"url": "https://api.github.com/repos/symfony/yaml/zipball/eca9019c88fbe250164affd107bc8057771f3f4d",
"reference": "eca9019c88fbe250164affd107bc8057771f3f4d",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
@ -1931,20 +1915,20 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2015-09-14 14:14:09"
"time": "2015-10-11 09:39:48"
},
{
"name": "twig/twig",
"version": "v1.22.2",
"version": "v1.23.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "79249fc8c9ff62e41e217e0c630e2e00bcadda6a"
"reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/79249fc8c9ff62e41e217e0c630e2e00bcadda6a",
"reference": "79249fc8c9ff62e41e217e0c630e2e00bcadda6a",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/d9b6333ae8dd2c8e3fd256e127548def0bc614c6",
"reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6",
"shasum": ""
},
"require": {
@ -1957,7 +1941,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.22-dev"
"dev-master": "1.23-dev"
}
},
"autoload": {
@ -1992,20 +1976,20 @@
"keywords": [
"templating"
],
"time": "2015-09-22 13:59:32"
"time": "2015-11-05 12:49:06"
},
{
"name": "wikimedia/composer-merge-plugin",
"version": "dev-master",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/wikimedia/composer-merge-plugin.git",
"reference": "47bb3388cfeae41a38087ac8465a7d08fa92ea2e"
"reference": "bfed1f8d4eb97e9ba80eee57ea46229d7e5364d9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/wikimedia/composer-merge-plugin/zipball/6196fdb001faf681f92db2ae10abafb5815affde",
"reference": "47bb3388cfeae41a38087ac8465a7d08fa92ea2e",
"url": "https://api.github.com/repos/wikimedia/composer-merge-plugin/zipball/bfed1f8d4eb97e9ba80eee57ea46229d7e5364d9",
"reference": "bfed1f8d4eb97e9ba80eee57ea46229d7e5364d9",
"shasum": ""
},
"require": {
@ -2015,8 +1999,7 @@
"require-dev": {
"composer/composer": "1.0.*@dev",
"jakub-onderka/php-parallel-lint": "~0.8",
"phpspec/prophecy-phpunit": "~1.0",
"phpunit/phpunit": "~4.0",
"phpunit/phpunit": "~4.8|~5.0",
"squizlabs/php_codesniffer": "~2.1.0"
},
"type": "composer-plugin",
@ -2042,7 +2025,7 @@
}
],
"description": "Composer plugin to merge multiple composer.json files",
"time": "2015-09-22 21:14:25"
"time": "2015-11-06 20:31:16"
},
{
"name": "zendframework/zend-diactoros",
@ -3599,16 +3582,16 @@
},
{
"name": "symfony/browser-kit",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/browser-kit.git",
"reference": "277a2457776d4cc25706fbdd9d1e4ab2dac884e4"
"reference": "07d664a052572ccc28eb2ab7dbbe82155b1ad367"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/277a2457776d4cc25706fbdd9d1e4ab2dac884e4",
"reference": "277a2457776d4cc25706fbdd9d1e4ab2dac884e4",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/07d664a052572ccc28eb2ab7dbbe82155b1ad367",
"reference": "07d664a052572ccc28eb2ab7dbbe82155b1ad367",
"shasum": ""
},
"require": {
@ -3617,8 +3600,7 @@
},
"require-dev": {
"symfony/css-selector": "~2.0,>=2.0.5",
"symfony/phpunit-bridge": "~2.7",
"symfony/process": "~2.0,>=2.0.5"
"symfony/process": "~2.3.34|~2.7,>=2.7.6"
},
"suggest": {
"symfony/process": ""
@ -3650,28 +3632,25 @@
],
"description": "Symfony BrowserKit Component",
"homepage": "https://symfony.com",
"time": "2015-09-06 08:36:38"
"time": "2015-10-23 14:47:27"
},
{
"name": "symfony/css-selector",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "abe19cc0429a06be0c133056d1f9859854860970"
"reference": "e1b865b26be4a56d22a8dee398375044a80c865b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/abe19cc0429a06be0c133056d1f9859854860970",
"reference": "abe19cc0429a06be0c133056d1f9859854860970",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/e1b865b26be4a56d22a8dee398375044a80c865b",
"reference": "e1b865b26be4a56d22a8dee398375044a80c865b",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7"
},
"type": "library",
"extra": {
"branch-alias": {
@ -3703,28 +3682,27 @@
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
"time": "2015-09-22 13:49:29"
"time": "2015-10-11 09:39:48"
},
{
"name": "symfony/dom-crawler",
"version": "v2.7.5",
"version": "v2.7.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
"reference": "2e185ca136399f902b948694987e62c80099c052"
"reference": "5fef7d8b80d8f9992df99d8ee283f420484c9612"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/2e185ca136399f902b948694987e62c80099c052",
"reference": "2e185ca136399f902b948694987e62c80099c052",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/5fef7d8b80d8f9992df99d8ee283f420484c9612",
"reference": "5fef7d8b80d8f9992df99d8ee283f420484c9612",
"shasum": ""
},
"require": {
"php": ">=5.3.9"
},
"require-dev": {
"symfony/css-selector": "~2.3",
"symfony/phpunit-bridge": "~2.7"
"symfony/css-selector": "~2.3"
},
"suggest": {
"symfony/css-selector": ""
@ -3756,7 +3734,7 @@
],
"description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com",
"time": "2015-09-20 21:13:58"
"time": "2015-10-11 09:39:48"
}
],
"aliases": [],

View file

@ -1,24 +1,69 @@
Drupal 8.0, xxxx-xx-xx (development version)
Drupal 8.0.x, xxxx-xx-xx (development version)
----------------------
- Added Twig as the default template engine and converted all .tpl.php templates
to .html.twig.
- Dramatically improved the front end:
* Made all built-in themes responsive.
* Added support for responsive images.
* Added Twig as the default template engine and converted all .tpl.php
templates and theme functions to .html.twig.
* Removed the PHPTemplate engine.
* Several large scale cleanups of the markup produced by Drupal.
* Added Classy as a base theme to maintain CSS classes and wrappers.
* Added Stable as the default base theme to maintain backwards compatibility
for core template and CSS changes, because templates and CSS outside
Stable can be improved in minor releases (8.1.0, 8.2.0 …).
* Redesigned several key elements of the Seven theme.
* Added support for HTML5 elements.
* Included the HTML5 Shiv library to support HTML5 elements in IE 8 and
below.
* Included Backbone.js and Underscore.js JavaScript frameworks.
* Updated to jQuery 2.1.4.
* Updated to jQuery UI 1.11.4.
* Removed jquery.bbq.
* Removed the Garland theme from core.
* Removed the Overlay module from core.
* Improved the asset library system to manage CSS and JavaScript files and
their dependencies. Allowing for smaller AJAX request payloads.
* jQuery is no longer loaded on all pages, only when another asset needs it.
* No JavaScript is loaded at all for anonymous users by default, for faster
page loads.
* Implemented SMACSS-style categorization for CSS files.
* Removed most support for Internet Explorer 8 and below.
* Added Modernizr for making styling changes based on browser support.
* All page template variables converted to blocks.
- Added tour module. Provides highly contextual tips for UI elements.
- Improved entity system.
* Added support for saving and deleting entities through the controller.
* Base entity fields (such as labels) support widgets, formatters and
translation.
* Form modes introduced, similar to display modes.
* Entities are now classed objects, implementing EntityInterface.
* Drupal now understands the concept of a "default" revision, tracked
independently from the latest revision, allowing for the creation of
drafts while the current revision stays published.
* All entity types, not just nodes, now have support for revisions.
- Replaced the core routing system with one built on the Symfony2 framework.
- Refactored routing system based on Symfony2 components.
- Reworked menu links, local actions, and local tasks based upon the new routing
system.
- Added plugin system to standardize implementation of several core APIs.
- Configuration:
* Added a centralized file-based configuration system.
* Allows module authors to provide configuration in a standard format.
* Implements functionality to get, set, add and remove configuration.
* Includes ability to override configuration values with language variants
and other runtime values.
- Added the CKEditor WYSIWYG editor. Provides a drag-and-drop configuration UI.
- Included the HTML5 Shiv library to support HTML5 elements in IE 8 and below.
* Supports configuration schema, dependencies, and validation to maintain
data-integrity between deployments and updates.
- Improved authoring experience:
* Added the CKEditor WYSIWYG editor. Clean markup guaranteed thanks to tight
integration with the filter system.
* Includes uploading, aligning and captioning of images.
* Correspondingly modernized the default text formats.
* Provides a drag-and-drop configuration UI, which automatically updates the
HTML filter settings, making configuring text formats trivial for typical
use cases.
* Added align and caption filters that can be applied to any element:
images, blockquotes, code snippets, videos…
* In-place editing of any entity: nodes, blocks…
- Included the following Symfony2 components:
* ClassLoader - PSR-0-compatible autoload routines.
* DependencyInjection - Flexible dependency injection container.
@ -28,10 +73,13 @@ Drupal 8.0, xxxx-xx-xx (development version)
* Process - Allows for executing commands in a sub-process.
* Routing - Framework for mapping incoming requests to controller
information.
* Serialization - Serialize complex nested objects into JSON/XML etc.
* Validator - Ensure that an object is in a valid state based upon some
validation rules defined for it.
* Yaml - Parser for YAML files.
- Included the Assetic asset management framework for PHP.
- Included Backbone.js and Underscore.js JavaScript frameworks.
- Support added for making HTTP requests through a proxy server.
- Added routing component from Symfony CMF.
- Added Guzzle HTTP library.
- Added Zend Feed component.
- Removed modules from core.
* The following modules have been removed from core, because contributed
modules with similar functionality are available:
@ -42,35 +90,30 @@ Drupal 8.0, xxxx-xx-xx (development version)
* Poll
* Profile
* Trigger
- Removed the Overlay module from core.
- Removed the Garland theme from core.
- Removed the Statistics module's accesslog functionality and reports from core.
- Removed XML-RPC functionality from core.
- Removed user signatures support from core.
- Removed backwards-compatibility with 'magic_quotes_gpc'/'magic_quotes_runtime'
PHP configuration settings. Both are required to be disabled.
- Universally Unique IDentifier (UUID):
* Support for generating and validating UUIDs.
- JavaScript changes:
* Updated to jQuery 2.1.0
* Updated to jQuery UI 1.10.2
* Removed jquery.bbq
- Tremendously improved language support all around.
* Great language improvements for users:
* Improved language selection with user preference detection in the
installer.
installer based on browser settings.
* The installer is presented in the user's native language.
* Moved base language support to Language module.
* Greatly simplified the interface for setting up languages.
* Improved browser language detection considerably.
* Language domain and path prefix configuraton simplified and centralized;
path prefix detection is now default.
* Language domain and path prefix configuration simplified and
centralized; path prefix detection is now default.
* Added HTML 5 language markup; language information added in markup in
several more places.
* Made it possible to assign external language codes to local languages.
* Introduced the possibility of an administration-specific language
preference for users.
* Language selection fallback language is now independently configurable
without needing to change the site default language.
* Simplified and added new features in interface translation:
* Made interface translation directly accessible from language list.
* Made interface translation directly accessible from the language list.
* Centralized interface translation import to one directory.
* Drupal can now be translated to English and English can be deleted.
* Much improved built-in translation interface.
@ -79,6 +122,7 @@ Drupal 8.0, xxxx-xx-xx (development version)
be identified and protected from translation update overwrites.
* All Gettext files are now imported in chunks, better for low resource
environments.
* Automated import and update of translations in the installer and later.
* Improved content language support:
* Made it possible to assign language to taxonomy terms, vocabularies,
menu items, and files.
@ -107,29 +151,64 @@ Drupal 8.0, xxxx-xx-xx (development version)
developers.
* Made it possible for users to have a preferred language separate from
their user entity language.
* The text formatter from t() is now available as format_string().
* The text formatter from t() is now available as FormattableMarkup.
* Added support for interface translation contexts in Drupal.t(),
Drupal.formatPlural() as well as routing, tabs, actions, and contextual
links.
Drupal.formatPlural() as well as routing, tabs, actions, shipped
menu items and contextual links.
* Removed textgroups support from interface translation in favor of
native configuration language support.
* Added configuration schema system to support generating translation
forms for any configuration.
* Reworked Gettext PO support to use pluggable read/write handlers.
* Added language select form element in the Form API.
- Added Email field type to core.
- Added Link field type to core.
- Added Phone number field type to core.
* Added a transliteration API. (Only used for machine names in core.)
- New field types added to core:
- Email
- Link
- Phone number
- Entity reference
- Date
- Comment (allows comment threads on entity types other than node).
- Added local image input filter, to enable secure image posting.
- Added Views and Views UI module to core.
- Added Entity Reference field type to core.
- Added Date field type to core.
- Added Views and Views UI module to core:
* Various core listings: /node, /admin/content/node, /admin/people etc. are
now served by views.
* REST API support built in.
* Rewrote caching integration for better performance.
- Custom blocks are now fieldable, revisionable, and translatable entities.
- An accessible modal API based on improvements made in collaboration with the
jQuery UI team and the Views team.
- Fieldable contact forms allowing site-builders to easily build custom forms
for soliciting feedback from users.
- Added a Web Services module package.
* Added a RESTful web services provider module.
* Added a serialization module using the Symfony serialization component.
* Added a Hypertext Application Language (HAL) serialization module.
* Added a HTTP Basic authentication provider module.
- Significant performance/scalability improvements:
* Cache tags, which allow content to be invalidated accurately and instantly,
including reverse proxies and CDNs.
* Cache contexts, which allow content to be cached correctly, and placeholdered
to improve cache hit rates.
* Cacheability bubbling, which allows strict tracking of assets and
cacheability throughout page rendering.
* Page caching has been factored out to its own module and is enabled by
default.
* Authenticated page caching has been added to core via the Dynamic Page Cache
module and is enabled by default.
* APCu, memory, and PHP file caching backends added to core, alongside support
for a chained, consistent cache backend to support correctly using fast
local cache implementations with multiple web servers.
- When using MySQL, the MyISAM engine is no longer supported.
- Testing improvements
* Added PHPUnit for proper unit testing, see
https://phpunit.de/manual/4.8/en/index.html so you can run tests via
your IDE.
* Added BrowserTestBase as an alternative to simpletest for browser
testing (JavaScript support to be included in the future)
* Added KernelTestBase to provide a fast API testing of integration of
different components
* Core branch nightly tests include PHP 5.5, 5.6, 7, sqlite and PostgreSQL.
Drupal 7.0, 2011-01-05
----------------------

View file

@ -283,6 +283,7 @@ Breakpoint module
CKEditor module
- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers
- Marek 'mlewand' Lewandowski https://www.drupal.org/u/mlewand
Color module
- ?

View file

@ -17,7 +17,7 @@
"symfony/validator": "2.7.*",
"symfony/process": "2.7.*",
"symfony/yaml": "2.7.*",
"twig/twig": "^1.22.2",
"twig/twig": "^1.23.1",
"doctrine/common": "2.5.*",
"doctrine/annotations": "1.2.*",
"guzzlehttp/guzzle": "~6.1",

View file

@ -260,7 +260,6 @@ drupal.tabledrag:
misc/tabledrag.js: { weight: -1 }
dependencies:
- core/jquery
- core/modernizr
- core/drupal
- core/drupalSettings
- core/jquery.once

View file

@ -949,14 +949,14 @@ services:
tags:
- { name: route_enhancer }
- { 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_content_controller_subscriber:
class: Drupal\Core\EventSubscriber\ContentControllerSubscriber
tags:
- { name: event_subscriber }
route_special_attributes_subscriber:
class: Drupal\Core\EventSubscriber\SpecialAttributesRouteSubscriber
tags:

View file

@ -119,8 +119,11 @@ function error_displayable($error = NULL) {
* %line, 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 $fatal
* TRUE if the error is fatal.
* @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 = drupal_installation_attempted();
@ -169,6 +172,11 @@ function _drupal_log_error($error, $fatal = FALSE) {
}
}
// Log fatal errors, so developers can find and debug them.
if ($fatal) {
error_log(sprintf('%s: %s in %s on line %d', $error['%type'], $error['@message'], $error['%file'], $error['%line']));
}
if (PHP_SAPI === 'cli') {
if ($fatal) {
// When called from CLI, simply output a plain text message.

View file

@ -15,6 +15,7 @@ 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;
@ -403,6 +404,8 @@ function install_begin_request($class_loader, &$install_state) {
$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'])) {

View file

@ -1778,7 +1778,7 @@ function drupal_common_theme() {
),
// From menu.inc.
'menu' => array(
'variables' => array('items' => array(), 'attributes' => array()),
'variables' => array('menu_name' => NULL, 'items' => array(), 'attributes' => array()),
),
'menu_local_task' => array(
'render element' => 'element',

View file

@ -82,19 +82,21 @@ function _drupal_maintenance_theme() {
$theme = $custom_theme;
// Find all our ancestor themes and put them in an array.
$base_theme = 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_theme[] = $themes[$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.
// Ensure that the base theme is added and installed.
$theme_handler->addTheme($themes[$ancestor]);
}
}
// @todo This is just a workaround. Find a better way how to handle themes
// on maintenance pages, see https://www.drupal.org/node/2322619.
\Drupal::theme()->setActiveTheme($theme_init->getActiveTheme($themes[$custom_theme], array_reverse($base_theme)));
\Drupal::theme()->setActiveTheme($theme_init->getActiveTheme($themes[$custom_theme], $base_themes));
// Prime the theme registry.
Drupal::service('theme.registry');
}

View file

@ -81,7 +81,7 @@ class Drupal {
/**
* The current system version.
*/
const VERSION = '8.0.0-rc3';
const VERSION = '8.0.0-dev-2015-11-17';
/**
* Core API compatibility.
@ -252,11 +252,27 @@ class Drupal {
*
* @return \Drupal\Core\Entity\EntityManagerInterface
* The entity manager service.
*
* @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.
*/
public static function entityManager() {
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.
*

View file

@ -79,8 +79,7 @@ class FileStorage implements PhpStorageInterface {
public static function htaccessLines($private = TRUE) {
$lines = <<<EOF
# Turn off all options we don't need.
Options None
Options +FollowSymLinks
Options -Indexes -ExecCGI -Includes -MultiViews
# Set the catch-all handler to prevent scripts from being executed.
SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006

View file

@ -116,8 +116,9 @@ class AssetResolver implements AssetResolverInterface {
public function getCssAssets(AttachedAssetsInterface $assets, $optimize) {
$theme_info = $this->themeManager->getActiveTheme();
// Add the theme name to the cache key since themes may implement
// hook_css_alter().
$cid = 'css:' . $theme_info->getName() . ':' . Crypt::hashBase64(serialize($assets)) . (int) $optimize;
// hook_library_info_alter().
$libraries_to_load = $this->getLibrariesToLoad($assets);
$cid = 'css:' . $theme_info->getName() . ':' . Crypt::hashBase64(serialize($libraries_to_load)) . (int) $optimize;
if ($cached = $this->cache->get($cid)) {
return $cached->data;
}
@ -132,7 +133,7 @@ class AssetResolver implements AssetResolverInterface {
'browsers' => [],
];
foreach ($this->getLibrariesToLoad($assets) as $library) {
foreach ($libraries_to_load as $library) {
list($extension, $name) = explode('/', $library, 2);
$definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
if (isset($definition['css'])) {
@ -187,9 +188,7 @@ class AssetResolver implements AssetResolverInterface {
* Returns the JavaScript settings assets for this response's libraries.
*
* Gathers all drupalSettings from all libraries in the attached assets
* collection and merges them, then it merges individual attached settings,
* and finally invokes hook_js_settings_alter() to allow alterations of
* JavaScript settings by modules and themes.
* collection and merges them.
*
* @param \Drupal\Core\Asset\AttachedAssetsInterface $assets
* The assets attached to the current response.
@ -207,9 +206,6 @@ class AssetResolver implements AssetResolverInterface {
}
}
// Attached settings win over settings in libraries.
$settings = NestedArray::mergeDeepArray([$settings, $assets->getSettings()], TRUE);
return $settings;
}
@ -219,9 +215,10 @@ class AssetResolver implements AssetResolverInterface {
public function getJsAssets(AttachedAssetsInterface $assets, $optimize) {
$theme_info = $this->themeManager->getActiveTheme();
// Add the theme name to the cache key since themes may implement
// hook_js_alter(). Additionally add the current language to support
// translation of JavaScript files.
$cid = 'js:' . $theme_info->getName() . ':' . $this->languageManager->getCurrentLanguage()->getId() . ':' . Crypt::hashBase64(serialize($assets)) . (int) $optimize;
// hook_library_info_alter(). Additionally add the current language to
// support translation of JavaScript files via hook_js_alter().
$libraries_to_load = $this->getLibrariesToLoad($assets);
$cid = 'js:' . $theme_info->getName() . ':' . $this->languageManager->getCurrentLanguage()->getId() . ':' . Crypt::hashBase64(serialize($libraries_to_load)) . (int) (count($assets->getSettings()) > 0) . (int) $optimize;
if ($cached = $this->cache->get($cid)) {
list($js_assets_header, $js_assets_footer, $settings, $settings_in_header) = $cached->data;
@ -239,8 +236,6 @@ class AssetResolver implements AssetResolverInterface {
'browsers' => [],
];
$libraries_to_load = $this->getLibrariesToLoad($assets);
// Collect all libraries that contain JS assets and are in the header.
$header_js_libraries = [];
foreach ($libraries_to_load as $library) {
@ -329,8 +324,10 @@ class AssetResolver implements AssetResolverInterface {
$this->cache->set($cid, [$js_assets_header, $js_assets_footer, $settings, $settings_in_header], CacheBackendInterface::CACHE_PERMANENT, ['library_info']);
}
if ($settings !== FALSE) {
// Attached settings override both library definitions and
// hook_js_settings_build().
$settings = NestedArray::mergeDeepArray([$settings, $assets->getSettings()], TRUE);
// Allow modules and themes to alter the JavaScript settings.
$this->moduleHandler->alter('js_settings', $settings, $assets);
$this->themeManager->alter('js_settings', $settings, $assets);

View file

@ -7,6 +7,7 @@
namespace Drupal\Core\Cache;
use Drupal\Component\Utility\Crypt;
use Drupal\Core\DestructableInterface;
use Drupal\Core\Lock\LockBackendInterface;
@ -232,7 +233,7 @@ abstract class CacheCollector implements CacheCollectorInterface, DestructableIn
// Lock cache writes to help avoid stampedes.
$cid = $this->getCid();
$lock_name = $cid . ':' . __CLASS__;
$lock_name = $this->normalizeLockName($cid . ':' . __CLASS__);
if (!$lock || $this->lock->acquire($lock_name)) {
// Set and delete operations invalidate the cache item. Try to also load
// an eventually invalidated cache entry, only update an invalidated cache
@ -264,6 +265,30 @@ abstract class CacheCollector implements CacheCollectorInterface, DestructableIn
$this->keysToRemove = array();
}
/**
* Normalizes a cache ID in order to comply with database limitations.
*
* @param string $cid
* The passed in cache ID.
*
* @return string
* An ASCII-encoded cache ID that is at most 255 characters long.
*/
protected function normalizeLockName($cid) {
// Nothing to do if the ID is a US ASCII string of 255 characters or less.
$cid_is_ascii = mb_check_encoding($cid, 'ASCII');
if (strlen($cid) <= 255 && $cid_is_ascii) {
return $cid;
}
// Return a string that uses as much as possible of the original cache ID
// with the hash appended.
$hash = Crypt::hashBase64($cid);
if (!$cid_is_ascii) {
return $hash;
}
return substr($cid, 0, 255 - strlen($hash)) . $hash;
}
/**
* {@inheritdoc}
*/

View file

@ -10,6 +10,7 @@ namespace Drupal\Core\Composer;
use Drupal\Component\PhpStorage\FileStorage;
use Composer\Script\Event;
use Composer\Installer\PackageEvent;
use Composer\Semver\Constraint\Constraint;
/**
* Provides static functions for composer script events.
@ -71,23 +72,38 @@ class Composer {
];
/**
* Add vendor classes to composers static classmap.
* Add vendor classes to Composer's static classmap.
*/
public static function preAutoloadDump(Event $event) {
$composer = $event->getComposer();
$package = $composer->getPackage();
$autoload = $package->getAutoload();
$autoload['classmap'] = array_merge($autoload['classmap'], array(
'vendor/symfony/http-foundation/Request.php',
'vendor/symfony/http-foundation/ParameterBag.php',
'vendor/symfony/http-foundation/FileBag.php',
'vendor/symfony/http-foundation/ServerBag.php',
'vendor/symfony/http-foundation/HeaderBag.php',
'vendor/symfony/http-kernel/HttpKernel.php',
'vendor/symfony/http-kernel/HttpKernelInterface.php',
'vendor/symfony/http-kernel/TerminableInterface.php',
));
$package->setAutoload($autoload);
// We need the root package so we can add our classmaps to its loader.
$package = $event->getComposer()->getPackage();
// We need the local repository so that we can query and see if it's likely
// that our files are present there.
$repository = $event->getComposer()->getRepositoryManager()->getLocalRepository();
// This is, essentially, a null constraint. We only care whether the package
// is present in vendor/ yet, but findPackage() requires it.
$constraint = new Constraint('>', '');
// Check for our packages, and then optimize them if they're present.
if ($repository->findPackage('symfony/http-foundation', $constraint)) {
$autoload = $package->getAutoload();
$autoload['classmap'] = array_merge($autoload['classmap'], array(
'vendor/symfony/http-foundation/Request.php',
'vendor/symfony/http-foundation/ParameterBag.php',
'vendor/symfony/http-foundation/FileBag.php',
'vendor/symfony/http-foundation/ServerBag.php',
'vendor/symfony/http-foundation/HeaderBag.php',
));
$package->setAutoload($autoload);
}
if ($repository->findPackage('symfony/http-kernel', $constraint)) {
$autoload = $package->getAutoload();
$autoload['classmap'] = array_merge($autoload['classmap'], array(
'vendor/symfony/http-kernel/HttpKernel.php',
'vendor/symfony/http-kernel/HttpKernelInterface.php',
'vendor/symfony/http-kernel/TerminableInterface.php',
));
$package->setAutoload($autoload);
}
}
/**

View file

@ -112,13 +112,13 @@ class ConfigInstaller implements ConfigInstallerInterface {
$prefix = $name . '.';
}
// Gets a profile storage to search for overrides if necessary.
$profile_storage = $this->getProfileStorage($name);
// Gets profile storages to search for overrides if necessary.
$profile_storages = $this->getProfileStorages($name);
// Gather information about all the supported collections.
$collection_info = $this->configManager->getConfigCollectionInfo();
foreach ($collection_info->getCollectionNames() as $collection) {
$config_to_create = $this->getConfigToCreate($storage, $collection, $prefix, $profile_storage);
$config_to_create = $this->getConfigToCreate($storage, $collection, $prefix, $profile_storages);
// If we're installing a profile ensure configuration that is overriding
// is excluded.
if ($name == $this->drupalGetProfile()) {
@ -223,19 +223,22 @@ class ConfigInstaller implements ConfigInstallerInterface {
* The configuration collection to use.
* @param string $prefix
* (optional) Limit to configuration starting with the provided string.
* @param \Drupal\Core\Config\StorageInterface[] $profile_storages
* An array of storage interfaces containing profile configuration to check
* for overrides.
*
* @return array
* An array of configuration data read from the source storage keyed by the
* configuration object name.
*/
protected function getConfigToCreate(StorageInterface $storage, $collection, $prefix = '', StorageInterface $profile_storage = NULL) {
protected function getConfigToCreate(StorageInterface $storage, $collection, $prefix = '', array $profile_storages = []) {
if ($storage->getCollectionName() != $collection) {
$storage = $storage->createCollection($collection);
}
$data = $storage->readMultiple($storage->listAll($prefix));
// Check to see if the corresponding override storage has any overrides.
if ($profile_storage) {
foreach ($profile_storages as $profile_storage) {
if ($profile_storage->getCollectionName() != $collection) {
$profile_storage = $profile_storage->createCollection($collection);
}
@ -435,11 +438,11 @@ class ConfigInstaller implements ConfigInstallerInterface {
$enabled_extensions = $this->getEnabledExtensions();
// Add the extension that will be enabled to the list of enabled extensions.
$enabled_extensions[] = $name;
// Gets a profile storage to search for overrides if necessary.
$profile_storage = $this->getProfileStorage($name);
// Gets profile storages to search for overrides if necessary.
$profile_storages = $this->getProfileStorages($name);
// Check the dependencies of configuration provided by the module.
$invalid_default_config = $this->findDefaultConfigWithUnmetDependencies($storage, $enabled_extensions, $profile_storage);
$invalid_default_config = $this->findDefaultConfigWithUnmetDependencies($storage, $enabled_extensions, $profile_storages);
if (!empty($invalid_default_config)) {
throw UnmetDependenciesException::create($name, $invalid_default_config);
}
@ -460,14 +463,19 @@ class ConfigInstaller implements ConfigInstallerInterface {
/**
* Finds default configuration with unmet dependencies.
*
* @param \Drupal\Core\Config\StorageInterface $storage
* The storage containing the default configuration.
* @param array $enabled_extensions
* A list of all the currently enabled modules and themes.
* @param \Drupal\Core\Config\StorageInterface[] $profile_storages
* An array of storage interfaces containing profile configuration to check
* for overrides.
*
* @return array
* List of configuration that has unmet dependencies
*/
protected function findDefaultConfigWithUnmetDependencies(StorageInterface $storage, array $enabled_extensions, StorageInterface $profile_storage = NULL) {
$config_to_create = $this->getConfigToCreate($storage, StorageInterface::DEFAULT_COLLECTION, '', $profile_storage);
protected function findDefaultConfigWithUnmetDependencies(StorageInterface $storage, array $enabled_extensions, array $profile_storages = []) {
$config_to_create = $this->getConfigToCreate($storage, StorageInterface::DEFAULT_COLLECTION, '', $profile_storages);
$all_config = array_merge($this->configFactory->listAll(), array_keys($config_to_create));
return array_filter(array_keys($config_to_create), function($config_name) use ($enabled_extensions, $all_config, $config_to_create) {
return !$this->validateDependencies($config_name, $config_to_create[$config_name], $enabled_extensions, $all_config);
@ -550,27 +558,31 @@ class ConfigInstaller implements ConfigInstallerInterface {
/**
* Gets the profile storage to use to check for profile overrides.
*
* The install profile can override module configuration during a module
* install. Both the install and optional directories are checked for matching
* configuration. This allows profiles to override default configuration for
* modules they do not depend on.
*
* @param string $installing_name
* (optional) The name of the extension currently being installed.
*
* @return \Drupal\Core\Config\StorageInterface|null
* A storage to access configuration from the installation profile. If a
* Drupal installation is not in progress or we're installing the profile
* itself, then it will return NULL as the profile storage should not be
* used.
* @return \Drupal\Core\Config\StorageInterface[]|null
* Storages to access configuration from the installation profile. If we're
* installing the profile itself, then it will return an empty array as the
* profile storage should not be used.
*/
protected function getProfileStorage($installing_name = '') {
protected function getProfileStorages($installing_name = '') {
$profile = $this->drupalGetProfile();
if ($this->drupalInstallationAttempted() && $profile != $installing_name) {
// Profiles should not contain optional configuration so always use the
// install directory.
$profile_install_path = $this->getDefaultConfigDirectory('module', $profile);
$profile_storage = new FileStorage($profile_install_path, StorageInterface::DEFAULT_COLLECTION);
$profile_storages = [];
if ($profile && $profile != $installing_name) {
$profile_path = $this->drupalGetPath('module', $profile);
foreach ([InstallStorage::CONFIG_INSTALL_DIRECTORY, InstallStorage::CONFIG_OPTIONAL_DIRECTORY] as $directory) {
if (is_dir($profile_path . '/' . $directory)) {
$profile_storages[] = new FileStorage($profile_path . '/' . $directory, StorageInterface::DEFAULT_COLLECTION);
}
}
}
else {
$profile_storage = NULL;
}
return $profile_storage;
return $profile_storages;
}
/**

View file

@ -387,6 +387,7 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
* {@inheritdoc}
*/
public function url($rel = 'edit-form', $options = array()) {
// Do not remove this override: the default value of $rel is different.
return parent::url($rel, $options);
}
@ -394,9 +395,19 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
* {@inheritdoc}
*/
public function link($text = NULL, $rel = 'edit-form', array $options = []) {
// Do not remove this override: the default value of $rel is different.
return parent::link($text, $rel, $options);
}
/**
* {@inheritdoc}
*/
public function toUrl($rel = 'edit-form', array $options = []) {
// Unless language was already provided, avoid setting an explicit language.
$options += ['language' => NULL];
return parent::toUrl($rel, $options);
}
/**
* {@inheritdoc}
*/

View file

@ -49,6 +49,13 @@ abstract class ControllerBase implements ContainerInjectionInterface {
*/
protected $entityManager;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity form builder.
*
@ -117,6 +124,10 @@ abstract class ControllerBase implements ContainerInjectionInterface {
*
* @return \Drupal\Core\Entity\EntityManagerInterface
* The entity manager service.
*
* @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
* Most of the time static::entityTypeManager() is supposed to be used
* instead.
*/
protected function entityManager() {
if (!$this->entityManager) {
@ -125,6 +136,19 @@ abstract class ControllerBase implements ContainerInjectionInterface {
return $this->entityManager;
}
/**
* Retrieves the entity type manager.
*
* @return \Drupal\Core\Entity\EntityTypeManagerInterface
* The entity type manager.
*/
protected function entityTypeManager() {
if (!isset($this->entityTypeManager)) {
$this->entityTypeManager = $this->container()->get('entity_type.manager');
}
return $this->entityTypeManager;
}
/**
* Retrieves the entity form builder.
*

View file

@ -254,14 +254,24 @@ class DateHelper {
* An array of weekdays.
*
* @return array
* An array of weekdays reordered to match the first day of the week.
* An array of weekdays reordered to match the first day of the week. The
* keys will remain unchanged. For example, if the first day of the week is
* set to be Monday, the array keys will be [1, 2, 3, 4, 5, 6, 0].
*/
public static function weekDaysOrdered($weekdays) {
$first_day = \Drupal::config('system.date')->get('first_day');
if ($first_day > 0) {
for ($i = 1; $i <= $first_day; $i++) {
$last = array_shift($weekdays);
array_push($weekdays, $last);
// Reset the array to the first element.
reset($weekdays);
// Retrieve the first week day value.
$last = current($weekdays);
// Store the corresponding key.
$key = key($weekdays);
// Remove this week day from the beginning of the array.
unset($weekdays[$key]);
// Add this week day to the end of the array.
$weekdays[$key] = $last;
}
}
return $weekdays;

View file

@ -72,33 +72,6 @@ class ContainerBuilder extends SymfonyContainerBuilder {
parent::setParameter($name, $value);
}
/**
* Synchronizes a service change.
*
* This method is a copy of the ContainerBuilder of symfony.
*
* This method updates all services that depend on the given
* service by calling all methods referencing it.
*
* @param string $id A service id
*/
private function synchronize($id) {
foreach ($this->getDefinitions() as $definitionId => $definition) {
// only check initialized services
if (!$this->initialized($definitionId)) {
continue;
}
foreach ($definition->getMethodCalls() as $call) {
foreach ($call[1] as $argument) {
if ($argument instanceof Reference && $id == (string) $argument) {
$this->callMethod($this->get($definitionId), $call);
}
}
}
}
}
/**
* A 1to1 copy of parent::callMethod.
*/

View file

@ -821,13 +821,6 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface {
// If there is no container and no cached container definition, build a new
// one from scratch.
if (!isset($container) && !isset($container_definition)) {
if (version_compare(phpversion(), '7.0.0-dev') >= 0) {
// The service graph implementation is prone to corruption during GC.
// Collect cycles now then disable the GC for the time of the compiler
// run.
// @see https://bugs.php.net/bug.php?id=70805
gc_collect_cycles();
}
$container = $this->compileContainer();
// Only dump the container if dumping is allowed. This is useful for

View file

@ -10,10 +10,10 @@ namespace Drupal\Core\Entity\Element;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\Tags;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\Textfield;
use Drupal\Core\Site\Settings;
use Drupal\user\EntityOwnerInterface;
/**
* Provides an entity autocomplete form element.
@ -147,7 +147,7 @@ class EntityAutocomplete extends Textfield {
'handler_settings' => $element['#selection_settings'],
);
$handler = \Drupal::service('plugin.manager.entity_reference_selection')->getInstance($options);
$autocreate = (bool) $element['#autocreate'];
$autocreate = (bool) $element['#autocreate'] && $handler instanceof SelectionWithAutocreateInterface;
$input_values = $element['#tags'] ? Tags::explode($element['#value']) : array($element['#value']);
foreach ($input_values as $input) {
@ -167,13 +167,14 @@ class EntityAutocomplete extends Textfield {
// Auto-create item. See an example of how this is handled in
// \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave().
$value[] = array(
'entity' => static::createNewEntity($element['#target_type'], $element['#autocreate']['bundle'], $input, $element['#autocreate']['uid'])
'entity' => $handler->createNewEntity($element['#target_type'], $element['#autocreate']['bundle'], $input, $element['#autocreate']['uid']),
);
}
}
// Check that the referenced entities are valid, if needed.
if ($element['#validate_reference'] && !$autocreate && !empty($value)) {
if ($element['#validate_reference'] && !empty($value)) {
// Validate existing entities.
$ids = array_reduce($value, function ($return, $item) {
if (isset($item['target_id'])) {
$return[] = $item['target_id'];
@ -189,6 +190,30 @@ class EntityAutocomplete extends Textfield {
}
}
}
// Validate newly created entities.
$new_entities = array_reduce($value, function ($return, $item) {
if (isset($item['entity'])) {
$return[] = $item['entity'];
}
return $return;
});
if ($new_entities) {
if ($autocreate) {
$valid_new_entities = $handler->validateReferenceableNewEntities($new_entities);
$invalid_new_entities = array_diff_key($new_entities, $valid_new_entities);
}
else {
// If the selection handler does not support referencing newly
// created entities, all of them should be invalidated.
$invalid_new_entities = $new_entities;
}
foreach ($invalid_new_entities as $entity) {
$form_state->setError($element, t('This entity (%type: %label) cannot be referenced.', array('%type' => $element['#target_type'], '%label' => $entity->label())));
}
}
}
// Use only the last value if the form element does not support multiple
@ -310,37 +335,4 @@ class EntityAutocomplete extends Textfield {
return $match;
}
/**
* Creates a new entity from a label entered in the autocomplete input.
*
* @param string $entity_type_id
* The entity type ID.
* @param string $bundle
* The bundle name.
* @param string $label
* The entity label.
* @param int $uid
* The entity owner ID.
*
* @return \Drupal\Core\Entity\EntityInterface
*/
protected static function createNewEntity($entity_type_id, $bundle, $label, $uid) {
$entity_manager = \Drupal::entityManager();
$entity_type = $entity_manager->getDefinition($entity_type_id);
$bundle_key = $entity_type->getKey('bundle');
$label_key = $entity_type->getKey('label');
$entity = $entity_manager->getStorage($entity_type_id)->create(array(
$bundle_key => $bundle,
$label_key => $label,
));
if ($entity instanceof EntityOwnerInterface) {
$entity->setOwnerId($uid);
}
return $entity;
}
}

View file

@ -72,11 +72,26 @@ abstract class Entity implements EntityInterface {
* Gets the entity manager.
*
* @return \Drupal\Core\Entity\EntityManagerInterface
*
* @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.
*/
protected function entityManager() {
return \Drupal::entityManager();
}
/**
* Gets the entity type manager.
*
* @return \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected function entityTypeManager() {
return \Drupal::entityTypeManager();
}
/**
* Gets the language manager.
*
@ -158,6 +173,13 @@ abstract class Entity implements EntityInterface {
* {@inheritdoc}
*/
public function urlInfo($rel = 'canonical', array $options = []) {
return $this->toUrl($rel, $options);
}
/**
* {@inheritdoc}
*/
public function toUrl($rel = 'canonical', array $options = []) {
if ($this->id() === NULL) {
throw new EntityMalformedException(sprintf('The "%s" entity cannot have a URI as it does not have an ID', $this->getEntityTypeId()));
}
@ -237,26 +259,33 @@ abstract class Entity implements EntityInterface {
* {@inheritdoc}
*/
public function link($text = NULL, $rel = 'canonical', array $options = []) {
if (is_null($text)) {
return $this->toLink($text, $rel, $options)->toString();
}
/**
* {@inheritdoc}
*/
public function toLink($text = NULL, $rel = 'canonical', array $options = []) {
if (!isset($text)) {
$text = $this->label();
}
$url = $this->urlInfo($rel);
$url = $this->toUrl($rel);
$options += $url->getOptions();
$url->setOptions($options);
return (new Link($text, $url))->toString();
return new Link($text, $url);
}
/**
* {@inheritdoc}
*/
public function url($rel = 'canonical', $options = array()) {
// While self::urlInfo() will throw an exception if the entity is new,
// While self::toUrl() will throw an exception if the entity has no id,
// the expected result for a URL is always a string.
if ($this->isNew() || !$this->hasLinkTemplate($rel)) {
if ($this->id() === NULL || !$this->hasLinkTemplate($rel)) {
return '';
}
$uri = $this->urlInfo($rel);
$uri = $this->toUrl($rel);
$options += $uri->getOptions();
$uri->setOptions($options);
return $uri->toString();

View file

@ -101,7 +101,29 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
public function label();
/**
* Gets the URI elements of the entity.
* Gets the URL object for the entity.
*
* @param string $rel
* The link relationship type, for example: canonical or edit-form.
* @param array $options
* See \Drupal\Core\Routing\UrlGeneratorInterface::generateFromRoute() for
* the available options.
*
* @return \Drupal\Core\Url
* The URL object.
*
* @deprecated in Drupal 8.0.0, intended to be removed in Drupal 9.0.0
* Use toUrl() instead.
*
* @see \Drupal\Core\Entity\EntityInterface::toUrl
*/
public function urlInfo($rel = 'canonical', array $options = array());
/**
* Gets the URL object for the entity.
*
* The entity must have an id already. Content entities usually get their IDs
* by saving them.
*
* URI templates might be set in the links array in an annotation, for
* example:
@ -128,8 +150,12 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
* the available options.
*
* @return \Drupal\Core\Url
* The URL object.
*
* @throws \Drupal\Core\Entity\EntityMalformedException
* @throws \Drupal\Core\Entity\Exception\UndefinedLinkTemplateException
*/
public function urlInfo($rel = 'canonical', array $options = array());
public function toUrl($rel = 'canonical', array $options = array());
/**
* Gets the public URL for this entity.
@ -142,9 +168,36 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
*
* @return string
* The URL for this entity.
*
* @deprecated in Drupal 8.0.0, intended to be removed in Drupal 9.0.0
* Please use toUrl() instead.
*
* @see \Drupal\Core\Entity\EntityInterface::toUrl
*/
public function url($rel = 'canonical', $options = array());
/**
* Deprecated way of generating a link to the entity. See toLink().
*
* @param string|null $text
* (optional) The link text for the anchor tag as a translated string.
* If NULL, it will use the entity's label. Defaults to NULL.
* @param string $rel
* (optional) The link relationship type. Defaults to 'canonical'.
* @param array $options
* See \Drupal\Core\Routing\UrlGeneratorInterface::generateFromRoute() for
* the available options.
*
* @return string
* An HTML string containing a link to the entity.
*
* @deprecated in Drupal 8.0.0, intended to be removed in Drupal 9.0.0
* Please use toLink() instead.
*
* @see \Drupal\Core\Entity\EntityInterface::toLink
*/
public function link($text = NULL, $rel = 'canonical', array $options = []);
/**
* Generates the HTML for a link to this entity.
*
@ -157,10 +210,13 @@ interface EntityInterface extends AccessibleInterface, CacheableDependencyInterf
* See \Drupal\Core\Routing\UrlGeneratorInterface::generateFromRoute() for
* the available options.
*
* @return string
* An HTML string containing a link to the entity.
* @return \Drupal\Core\Link
* A Link to the entity.
*
* @throws \Drupal\Core\Entity\EntityMalformedException
* @throws \Drupal\Core\Entity\Exception\UndefinedLinkTemplateException
*/
public function link($text = NULL, $rel = 'canonical', array $options = []);
public function toLink($text = NULL, $rel = 'canonical', array $options = []);
/**
* Indicates if a link template exists for a given key.

View file

@ -30,7 +30,7 @@ interface SelectionInterface extends PluginFormInterface {
public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0);
/**
* Counts entities that are referenceable by a given field.
* Counts entities that are referenceable.
*
* @return int
* The number of referenceable entities.
@ -38,7 +38,7 @@ interface SelectionInterface extends PluginFormInterface {
public function countReferenceableEntities($match = NULL, $match_operator = 'CONTAINS');
/**
* Validates that entities can be referenced by this field.
* Validates which existing entities can be referenced.
*
* @return array
* An array of valid entity IDs.

View file

@ -0,0 +1,52 @@
<?php
/**
* @file
* Contains \Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface.
*/
namespace Drupal\Core\Entity\EntityReferenceSelection;
/**
* Interface for Selection plugins that support newly created entities.
*
* @see \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager
* @see \Drupal\Core\Entity\Annotation\EntityReferenceSelection
* @see plugin_api
*/
interface SelectionWithAutocreateInterface {
/**
* Creates a new entity object that can be used as a valid reference.
*
* @param string $entity_type_id
* The entity type ID.
* @param string $bundle
* The bundle name.
* @param string $label
* The entity label.
* @param int $uid
* The entity owner ID, if the entity type supports it.
*
* @return \Drupal\Core\Entity\EntityInterface
* An unsaved entity object.
*/
public function createNewEntity($entity_type_id, $bundle, $label, $uid);
/**
* Validates which newly created entities can be referenced.
*
* This method should replicate the logic implemented by
* \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface::validateReferenceableEntities(),
* but applied to newly created entities that have not been saved yet.
*
* @param \Drupal\Core\Entity\EntityInterface[] $entities
* An array of entities to check.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* The incoming $entities parameter, filtered for valid entities. Array keys
* are preserved.
*/
public function validateReferenceableNewEntities(array $entities);
}

View file

@ -97,18 +97,18 @@ class EntityTypeBundleInfo implements EntityTypeBundleInfoInterface {
}
else {
$this->bundleInfo = $this->moduleHandler->invokeAll('entity_bundle_info');
// First look for entity types that act as bundles for others, load them
// and add them as bundles.
foreach ($this->entityTypeManager->getDefinitions() as $type => $entity_type) {
if ($entity_type->getBundleOf()) {
foreach ($this->entityTypeManager->getStorage($type)->loadMultiple() as $entity) {
$this->bundleInfo[$entity_type->getBundleOf()][$entity->id()]['label'] = $entity->label();
// First look for entity types that act as bundles for others, load them
// and add them as bundles.
if ($bundle_entity_type = $entity_type->getBundleEntityType()) {
foreach ($this->entityTypeManager->getStorage($bundle_entity_type)->loadMultiple() as $entity) {
$this->bundleInfo[$type][$entity->id()]['label'] = $entity->label();
}
}
}
foreach ($this->entityTypeManager->getDefinitions() as $type => $entity_type) {
// If no bundles are provided, use the entity type name and label.
if (!isset($this->bundleInfo[$type])) {
// If entity type bundles are not supported and
// hook_entity_bundle_info() has not already set up bundle
// information, use the entity type name and label.
elseif (!isset($this->bundleInfo[$type])) {
$this->bundleInfo[$type][$type]['label'] = $entity_type->getLabel();
}
}

View file

@ -11,6 +11,7 @@ use Drupal\Component\Utility\Html;
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\Form\FormStateInterface;
@ -18,6 +19,7 @@ use Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\EntityOwnerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@ -40,7 +42,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* deriver = "Drupal\Core\Entity\Plugin\Derivative\DefaultSelectionDeriver"
* )
*/
class DefaultSelection extends PluginBase implements SelectionInterface, ContainerFactoryPluginInterface {
class DefaultSelection extends PluginBase implements SelectionInterface, SelectionWithAutocreateInterface, ContainerFactoryPluginInterface {
/**
* The entity manager.
@ -288,6 +290,38 @@ class DefaultSelection extends PluginBase implements SelectionInterface, Contain
return $result;
}
/**
* {@inheritdoc}
*/
public function createNewEntity($entity_type_id, $bundle, $label, $uid) {
$entity_type = $this->entityManager->getDefinition($entity_type_id);
$bundle_key = $entity_type->getKey('bundle');
$label_key = $entity_type->getKey('label');
$entity = $this->entityManager->getStorage($entity_type_id)->create(array(
$bundle_key => $bundle,
$label_key => $label,
));
if ($entity instanceof EntityOwnerInterface) {
$entity->setOwnerId($uid);
}
return $entity;
}
/**
* {@inheritdoc}
*/
public function validateReferenceableNewEntities(array $entities) {
return array_filter($entities, function ($entity) {
if (isset($this->configuration['handler_settings']['target_bundles'])) {
return in_array($entity->bundle(), $this->configuration['handler_settings']['target_bundles']);
}
return TRUE;
});
}
/**
* Builds an EntityQuery to get referenceable entities.
*

View file

@ -26,10 +26,24 @@ class ValidReferenceConstraint extends Constraint {
*
* @var string
*/
public $message = 'The referenced entity (%type: %id) does not exist.';
public $message = 'This entity (%type: %id) cannot be referenced.';
/**
* Validation message when the target_id is empty.
* Violation message when the entity does not exist.
*
* @var string
*/
public $nonExistingMessage = 'The referenced entity (%type: %id) does not exist.';
/**
* Violation message when a new entity ("autocreate") is invalid.
*
* @var string
*/
public $invalidAutocreateMessage = 'This entity (%type: %label) cannot be referenced.';
/**
* Violation message when the target_id is empty.
*
* @var string
*/

View file

@ -7,39 +7,142 @@
namespace Drupal\Core\Entity\Plugin\Validation\Constraint;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
use Drupal\Core\Entity\EntityReferenceSelection\SelectionWithAutocreateInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Checks if referenced entities are valid.
*/
class ValidReferenceConstraintValidator extends ConstraintValidator {
class ValidReferenceConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
/**
* The selection plugin manager.
*
* @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface
*/
protected $selectionManager;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs a ValidReferenceConstraintValidator object.
*
* @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface $selection_manager
* The selection plugin manager.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(SelectionPluginManagerInterface $selection_manager, EntityTypeManagerInterface $entity_type_manager) {
$this->selectionManager = $selection_manager;
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('plugin.manager.entity_reference_selection'),
$container->get('entity_type.manager')
);
}
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint) {
/** @var \Drupal\Core\Field\FieldItemInterface $value */
/** @var \Drupal\Core\Field\FieldItemListInterface $value */
/** @var ValidReferenceConstraint $constraint */
if (!isset($value)) {
return;
}
// We don't use a regular NotNull constraint for the target_id property as
// a NULL value is valid if the entity property contains an unsaved entity.
// @see \Drupal\Core\TypedData\DataReferenceTargetDefinition::getConstraints
if (!$value->isEmpty() && $value->target_id === NULL && !$value->entity->isNew()) {
$this->context->addViolation($constraint->nullMessage);
// Collect new entities and IDs of existing entities across the field items.
$new_entities = [];
$target_ids = [];
foreach ($value as $delta => $item) {
$target_id = $item->target_id;
// We don't use a regular NotNull constraint for the target_id property as
// NULL is allowed if the entity property contains an unsaved entity.
// @see \Drupal\Core\TypedData\DataReferenceTargetDefinition::getConstraints()
if (!$item->isEmpty() && $target_id === NULL) {
if (!$item->entity->isNew()) {
$this->context->buildViolation($constraint->nullMessage)
->atPath((string) $delta)
->addViolation();
return;
}
$new_entities[$delta] = $item->entity;
}
// '0' or NULL are considered valid empty references.
if (!empty($target_id)) {
$target_ids[$delta] = $target_id;
}
}
// Early opt-out if nothing to validate.
if (!$new_entities && !$target_ids) {
return;
}
$id = $value->get('target_id')->getValue();
// '0' or NULL are considered valid empty references.
if (empty($id)) {
return;
/** @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionInterface $handler * */
$handler = $this->selectionManager->getSelectionHandler($value->getFieldDefinition());
$target_type_id = $value->getFieldDefinition()->getSetting('target_type');
// Add violations on deltas with a new entity that is not valid.
if ($new_entities) {
if ($handler instanceof SelectionWithAutocreateInterface) {
$valid_new_entities = $handler->validateReferenceableNewEntities($new_entities);
$invalid_new_entities = array_diff_key($new_entities, $valid_new_entities);
}
else {
// If the selection handler does not support referencing newly created
// entities, all of them should be invalidated.
$invalid_new_entities = $new_entities;
}
foreach ($invalid_new_entities as $delta => $entity) {
$this->context->buildViolation($constraint->invalidAutocreateMessage)
->setParameter('%type', $target_type_id)
->setParameter('%label', $entity->label())
->atPath((string) $delta . '.entity')
->setInvalidValue($entity)
->addViolation();
}
}
$referenced_entity = $value->get('entity')->getValue();
if (!$referenced_entity) {
$type = $value->getFieldDefinition()->getSetting('target_type');
$this->context->addViolation($constraint->message, array('%type' => $type, '%id' => $id));
// Add violations on deltas with a target_id that is not valid.
if ($target_ids) {
$valid_target_ids = $handler->validateReferenceableEntities($target_ids);
if ($invalid_target_ids = array_diff($target_ids, $valid_target_ids)) {
// For accuracy of the error message, differentiate non-referenceable
// and non-existent entities.
$target_type = $this->entityTypeManager->getDefinition($target_type_id);
$existing_ids = $this->entityTypeManager->getStorage($target_type_id)->getQuery()
->condition($target_type->getKey('id'), $invalid_target_ids, 'IN')
->execute();
foreach ($invalid_target_ids as $delta => $target_id) {
$message = in_array($target_id, $existing_ids) ? $constraint->message : $constraint->nonExistingMessage;
$this->context->buildViolation($message)
->setParameter('%type', $target_type_id)
->setParameter('%id', $target_id)
->atPath((string) $delta . '.target_id')
->setInvalidValue($target_id)
->addViolation();
}
}
}
}
}

View file

@ -7,7 +7,11 @@
namespace Drupal\Core\Entity\Routing;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
@ -24,7 +28,33 @@ use Symfony\Component\Routing\RouteCollection;
*
* @internal
*/
class DefaultHtmlRouteProvider implements EntityRouteProviderInterface {
class DefaultHtmlRouteProvider implements EntityRouteProviderInterface, EntityHandlerInterface {
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* Constructs a new DefaultHtmlRouteProvider.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
*/
public function __construct(EntityManagerInterface $entity_manager) {
$this->entityManager = $entity_manager;
}
/**
* {@inheritdoc}
*/
public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
return new static(
$container->get('entity.manager')
);
}
/**
* {@inheritdoc}
@ -71,6 +101,12 @@ class DefaultHtmlRouteProvider implements EntityRouteProviderInterface {
->setOption('parameters', [
$entity_type_id => ['type' => 'entity:' . $entity_type_id],
]);
// Entity types with serial IDs can specify this in their route
// requirements, improving the matching process.
if ($this->getEntityTypeIdKeyType($entity_type) === 'integer') {
$route->setRequirement($entity_type_id, '\d+');
}
return $route;
}
}
@ -102,6 +138,12 @@ class DefaultHtmlRouteProvider implements EntityRouteProviderInterface {
->setOption('parameters', [
$entity_type_id => ['type' => 'entity:' . $entity_type_id],
]);
// Entity types with serial IDs can specify this in their route
// requirements, improving the matching process.
if ($this->getEntityTypeIdKeyType($entity_type) === 'integer') {
$route->setRequirement($entity_type_id, '\d+');
}
return $route;
}
}
@ -128,8 +170,33 @@ class DefaultHtmlRouteProvider implements EntityRouteProviderInterface {
->setOption('parameters', [
$entity_type_id => ['type' => 'entity:' . $entity_type_id],
]);
// Entity types with serial IDs can specify this in their route
// requirements, improving the matching process.
if ($this->getEntityTypeIdKeyType($entity_type) === 'integer') {
$route->setRequirement($entity_type_id, '\d+');
}
return $route;
}
}
/**
* Gets the type of the ID key for a given entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* An entity type.
*
* @return string|null
* The type of the ID key for a given entity type, or NULL if the entity
* type does not support fields.
*/
protected function getEntityTypeIdKeyType(EntityTypeInterface $entity_type) {
if (!$entity_type->isSubclassOf(FieldableEntityInterface::class)) {
return NULL;
}
$field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type->id());
return $field_storage_definitions[$entity_type->getKey('id')]->getType();
}
}

View file

@ -1916,16 +1916,16 @@ function hook_entity_extra_field_info() {
// Visibility of the ordering of the language selector is the same as on the
// node/add form.
if ($module_language_enabled) {
$configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', $bundle->type);
$configuration = ContentLanguageSettings::loadByEntityTypeBundle('node', $bundle->id());
if ($configuration->isLanguageAlterable()) {
$extra['node'][$bundle->type]['form']['language'] = array(
$extra['node'][$bundle->id()]['form']['language'] = array(
'label' => t('Language'),
'description' => $description,
'weight' => 0,
);
}
}
$extra['node'][$bundle->type]['display']['language'] = array(
$extra['node'][$bundle->id()]['display']['language'] = array(
'label' => t('Language'),
'description' => $description,
'weight' => 0,
@ -1948,8 +1948,8 @@ function hook_entity_extra_field_info() {
function hook_entity_extra_field_info_alter(&$info) {
// Force node title to always be at the top of the list by default.
foreach (NodeType::loadMultiple() as $bundle) {
if (isset($info['node'][$bundle->type]['form']['title'])) {
$info['node'][$bundle->type]['form']['title']['weight'] = -20;
if (isset($info['node'][$bundle->id()]['form']['title'])) {
$info['node'][$bundle->id()]['form']['title']['weight'] = -20;
}
}
}

View file

@ -1,48 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\Core\EventSubscriber\ContentControllerSubscriber.
*/
namespace Drupal\Core\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Sets the request format onto the request object.
*
* @todo Remove this event subscriber after
* https://www.drupal.org/node/2092647 has landed.
*/
class ContentControllerSubscriber implements EventSubscriberInterface {
/**
* Sets the _controller on a request when a _form is defined.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
* The event to process.
*/
public function onRequestDeriveFormWrapper(GetResponseEvent $event) {
$request = $event->getRequest();
if ($request->attributes->has('_form')) {
$request->attributes->set('_controller', 'controller.form:getContentResult');
}
}
/**
* Registers the methods in this class that should be listeners.
*
* @return array
* An array of event listener definitions.
*/
static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = array('onRequestDeriveFormWrapper', 25);
return $events;
}
}

View file

@ -147,7 +147,12 @@ class DefaultExceptionHtmlSubscriber extends HttpExceptionSubscriberBase {
}
$response = $this->httpKernel->handle($sub_request, HttpKernelInterface::SUB_REQUEST);
$response->setStatusCode($status_code);
// Only 2xx responses should have their status code overridden; any
// other status code should be passed on: redirects (3xx), error (5xx)…
// @see https://www.drupal.org/node/2603788#comment-10504916
if ($response->isSuccessful()) {
$response->setStatusCode($status_code);
}
// Persist any special HTTP headers that were set on the exception.
if ($exception instanceof HttpExceptionInterface) {

View file

@ -48,10 +48,6 @@ class HtmlResponsePlaceholderStrategySubscriber implements EventSubscriberInterf
* The event to process.
*/
public function onRespond(FilterResponseEvent $event) {
if (!$event->isMasterRequest()) {
return;
}
$response = $event->getResponse();
if (!$response instanceof HtmlResponse) {
return;

View file

@ -42,10 +42,6 @@ class HtmlResponseSubscriber implements EventSubscriberInterface {
* The event to process.
*/
public function onRespond(FilterResponseEvent $event) {
if (!$event->isMasterRequest()) {
return;
}
$response = $event->getResponse();
if (!$response instanceof HtmlResponse) {
return;

View file

@ -483,4 +483,18 @@ class ThemeHandler implements ThemeHandlerInterface {
throw new \InvalidArgumentException(sprintf('The theme %s does not exist.', $name));
}
/**
* {@inheritdoc}
*/
public function hasUi($name) {
$themes = $this->listInfo();
if (isset($themes[$name])) {
if (!empty($themes[$name]->info['hidden'])) {
$theme_config = $this->configFactory->get('system.theme');
return $name == $theme_config->get('default') || $name == $theme_config->get('admin');
}
return TRUE;
}
return FALSE;
}
}

View file

@ -208,4 +208,18 @@ interface ThemeHandlerInterface {
*/
public function getTheme($name);
/**
* Determines if a theme should be shown in the user interface.
*
* To be shown in the UI the theme has to be installed. If the theme is hidden
* it will not be shown unless it is the default or admin theme.
*
* @param string $name
* The name of the theme to check.
*
* @return bool
* TRUE if the theme should be shown in the UI, FALSE if not.
*/
public function hasUi($name);
}

View file

@ -15,6 +15,16 @@ use Drupal\Core\Form\FormStateInterface;
*/
class EntityReferenceFieldItemList extends FieldItemList implements EntityReferenceFieldItemListInterface {
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraints = parent::getConstraints();
$constraint_manager = $this->getTypedDataManager()->getValidationConstraintManager();
$constraints[] = $constraint_manager->create('ValidReference', []);
return $constraints;
}
/**
* {@inheritdoc}
*/

View file

@ -40,9 +40,6 @@ use Drupal\Core\Validation\Plugin\Validation\Constraint\AllowedValuesConstraint;
* default_widget = "entity_reference_autocomplete",
* default_formatter = "entity_reference_label",
* list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList",
* default_widget = "entity_reference_autocomplete",
* default_formatter = "entity_reference_label",
* constraints = {"ValidReference" = {}}
* )
*/
class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterface, PreconfiguredFieldUiOptionsInterface {
@ -165,20 +162,6 @@ class EntityReferenceItem extends FieldItemBase implements OptionsProviderInterf
unset($constraints[$key]);
}
}
list($current_handler) = explode(':', $this->getSetting('handler'), 2);
if ($current_handler === 'default') {
$handler_settings = $this->getSetting('handler_settings');
if (isset($handler_settings['target_bundles'])) {
$constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
$constraints[] = $constraint_manager->create('ComplexData', [
'entity' => [
'Bundle' => [
'bundle' => $handler_settings['target_bundles'],
],
],
]);
}
}
return $constraints;
}

View file

@ -7,6 +7,8 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
@ -77,4 +79,16 @@ class UriItem extends StringItem {
return parent::isEmpty();
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$values = parent::generateSampleValue($field_definition);
$suffix_length = $field_definition->getSetting('max_length') - 7;
foreach ($values as $key => $value) {
$values[$key] = 'http://' . Unicode::substr($value, 0, $suffix_length);
}
return $values;
}
}

View file

@ -304,7 +304,9 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI
}
// Pre-fetch all routes involved in the tree. This reduces the number
// of SQL queries that would otherwise be triggered by the access manager.
$routes = $route_names ? $this->routeProvider->getRoutesByNames($route_names) : array();
if ($route_names) {
$this->routeProvider->getRoutesByNames($route_names);
}
foreach ($tree as $level => $instances) {
/** @var $instances \Drupal\Core\Menu\LocalTaskInterface[] */

View file

@ -351,8 +351,11 @@ class MenuLinkManager implements MenuLinkManagerInterface {
* {@inheritdoc}
*/
public function addDefinition($id, array $definition) {
if ($this->treeStorage->load($id) || $id === '') {
throw new PluginException("The ID $id already exists as a plugin definition or is not valid");
if ($this->treeStorage->load($id)) {
throw new PluginException("The menu link ID $id already exists as a plugin definition");
}
elseif ($id === '') {
throw new PluginException("The menu link ID cannot be empty");
}
// Add defaults, so there is no requirement to specify everything.
$this->processDefinition($definition, $id);

View file

@ -177,6 +177,7 @@ class MenuLinkTree implements MenuLinkTreeInterface {
// Add the theme wrapper for outer markup.
// Allow menu-specific theme overrides.
$build['#theme'] = 'menu__' . strtr($menu_name, '-', '_');
$build['#menu_name'] = $menu_name;
$build['#items'] = $items;
// Set cache tag.
$build['#cache']['tags'][] = 'config:system.menu.' . $menu_name;

View file

@ -11,9 +11,14 @@ use Drupal\Core\Cache\Cache;
use Drupal\Core\Database\Connection;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Database\Query\Condition;
/**
* Provides a class for CRUD operations on path aliases.
*
* All queries perform case-insensitive matching on the 'source' and 'alias'
* fields, so the aliases '/test-alias' and '/test-Alias' are considered to be
* the same, and will both refer to the same internal system path.
*/
class AliasStorage implements AliasStorageInterface {
/**
@ -98,7 +103,13 @@ class AliasStorage implements AliasStorageInterface {
public function load($conditions) {
$select = $this->connection->select('url_alias');
foreach ($conditions as $field => $value) {
$select->condition($field, $value);
if ($field == 'source' || $field == 'alias') {
// Use LIKE for case-insensitive matching.
$select->condition($field, $this->connection->escapeLike($value), 'LIKE');
}
else {
$select->condition($field, $value);
}
}
return $select
->fields('url_alias')
@ -115,7 +126,13 @@ class AliasStorage implements AliasStorageInterface {
$path = $this->load($conditions);
$query = $this->connection->delete('url_alias');
foreach ($conditions as $field => $value) {
$query->condition($field, $value);
if ($field == 'source' || $field == 'alias') {
// Use LIKE for case-insensitive matching.
$query->condition($field, $this->connection->escapeLike($value), 'LIKE');
}
else {
$query->condition($field, $value);
}
}
$deleted = $query->execute();
// @todo Switch to using an event for this instead of a hook.
@ -128,90 +145,101 @@ class AliasStorage implements AliasStorageInterface {
* {@inheritdoc}
*/
public function preloadPathAlias($preloaded, $langcode) {
$args = array(
':system[]' => $preloaded,
':langcode' => $langcode,
':langcode_undetermined' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
);
$langcode_list = [$langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED];
$select = $this->connection->select('url_alias')
->fields('url_alias', ['source', 'alias']);
if (!empty($preloaded)) {
$conditions = new Condition('OR');
foreach ($preloaded as $preloaded_item) {
$conditions->condition('source', $this->connection->escapeLike($preloaded_item), 'LIKE');
}
$select->condition($conditions);
}
// Always get the language-specific alias before the language-neutral one.
// For example 'de' is less than 'und' so the order needs to be ASC, while
// 'xx-lolspeak' is more than 'und' so the order needs to be DESC. We also
// order by pid ASC so that fetchAllKeyed() returns the most recently
// created alias for each source. Subsequent queries using fetchField() must
// use pid DESC to have the same effect. For performance reasons, the query
// builder is not used here.
// use pid DESC to have the same effect.
if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
// Prevent PDO from complaining about a token the query doesn't use.
unset($args[':langcode']);
$result = $this->connection->query('SELECT source, alias FROM {url_alias} WHERE source IN ( :system[] ) AND langcode = :langcode_undetermined ORDER BY pid ASC', $args);
array_pop($langcode_list);
}
elseif ($langcode < LanguageInterface::LANGCODE_NOT_SPECIFIED) {
$result = $this->connection->query('SELECT source, alias FROM {url_alias} WHERE source IN ( :system[] ) AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, pid ASC', $args);
$select->orderBy('langcode', 'ASC');
}
else {
$result = $this->connection->query('SELECT source, alias FROM {url_alias} WHERE source IN ( :system[] ) AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, pid ASC', $args);
$select->orderBy('langcode', 'DESC');
}
return $result->fetchAllKeyed();
$select->orderBy('pid', 'ASC');
$select->condition('langcode', $langcode_list, 'IN');
return $select->execute()->fetchAllKeyed();
}
/**
* {@inheritdoc}
*/
public function lookupPathAlias($path, $langcode) {
$args = array(
':source' => $path,
':langcode' => $langcode,
':langcode_undetermined' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
);
// See the queries above.
$source = $this->connection->escapeLike($path);
$langcode_list = [$langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED];
// See the queries above. Use LIKE for case-insensitive matching.
$select = $this->connection->select('url_alias')
->fields('url_alias', ['alias'])
->condition('source', $source, 'LIKE');
if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
unset($args[':langcode']);
$alias = $this->connection->query("SELECT alias FROM {url_alias} WHERE source = :source AND langcode = :langcode_undetermined ORDER BY pid DESC", $args)->fetchField();
array_pop($langcode_list);
}
elseif ($langcode > LanguageInterface::LANGCODE_NOT_SPECIFIED) {
$alias = $this->connection->query("SELECT alias FROM {url_alias} WHERE source = :source AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, pid DESC", $args)->fetchField();
$select->orderBy('langcode', 'DESC');
}
else {
$alias = $this->connection->query("SELECT alias FROM {url_alias} WHERE source = :source AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, pid DESC", $args)->fetchField();
$select->orderBy('langcode', 'ASC');
}
return $alias;
$select->orderBy('pid', 'DESC');
$select->condition('langcode', $langcode_list, 'IN');
return $select->execute()->fetchField();
}
/**
* {@inheritdoc}
*/
public function lookupPathSource($path, $langcode) {
$args = array(
':alias' => $path,
':langcode' => $langcode,
':langcode_undetermined' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
);
// See the queries above.
$alias = $this->connection->escapeLike($path);
$langcode_list = [$langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED];
// See the queries above. Use LIKE for case-insensitive matching.
$select = $this->connection->select('url_alias')
->fields('url_alias', ['source'])
->condition('alias', $alias, 'LIKE');
if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
unset($args[':langcode']);
$result = $this->connection->query("SELECT source FROM {url_alias} WHERE alias = :alias AND langcode = :langcode_undetermined ORDER BY pid DESC", $args);
array_pop($langcode_list);
}
elseif ($langcode > LanguageInterface::LANGCODE_NOT_SPECIFIED) {
$result = $this->connection->query("SELECT source FROM {url_alias} WHERE alias = :alias AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode DESC, pid DESC", $args);
$select->orderBy('langcode', 'DESC');
}
else {
$result = $this->connection->query("SELECT source FROM {url_alias} WHERE alias = :alias AND langcode IN (:langcode, :langcode_undetermined) ORDER BY langcode ASC, pid DESC", $args);
$select->orderBy('langcode', 'ASC');
}
return $result->fetchField();
$select->orderBy('pid', 'DESC');
$select->condition('langcode', $langcode_list, 'IN');
return $select->execute()->fetchField();
}
/**
* {@inheritdoc}
*/
public function aliasExists($alias, $langcode, $source = NULL) {
// Use LIKE and NOT LIKE for case-insensitive matching.
$query = $this->connection->select('url_alias')
->condition('alias', $alias)
->condition('alias', $this->connection->escapeLike($alias), 'LIKE')
->condition('langcode', $langcode);
if (!empty($source)) {
$query->condition('source', $source, '<>');
$query->condition('source', $this->connection->escapeLike($source), 'NOT LIKE');
}
$query->addExpression('1');
$query->range(0, 1);

View file

@ -44,6 +44,9 @@ interface AliasStorageInterface {
/**
* Fetches a specific URL alias from the database.
*
* The default implementation performs case-insensitive matching on the
* 'source' and 'alias' strings.
*
* @param array $conditions
* An array of query conditions.
*
@ -60,6 +63,9 @@ interface AliasStorageInterface {
/**
* Deletes a URL alias.
*
* The default implementation performs case-insensitive matching on the
* 'source' and 'alias' strings.
*
* @param array $conditions
* An array of criteria.
*/
@ -82,6 +88,9 @@ interface AliasStorageInterface {
/**
* Returns an alias of Drupal system URL.
*
* The default implementation performs case-insensitive matching on the
* 'source' and 'alias' strings.
*
* @param string $path
* The path to investigate for corresponding path aliases.
* @param string $langcode
@ -96,6 +105,9 @@ interface AliasStorageInterface {
/**
* Returns Drupal system URL of an alias.
*
* The default implementation performs case-insensitive matching on the
* 'source' and 'alias' strings.
*
* @param string $path
* The path to investigate for corresponding system URLs.
* @param string $langcode
@ -110,6 +122,9 @@ interface AliasStorageInterface {
/**
* Checks if alias already exists.
*
* The default implementation performs case-insensitive matching on the
* 'source' and 'alias' strings.
*
* @param string $alias
* Alias to check against.
* @param string $langcode
@ -135,8 +150,9 @@ interface AliasStorageInterface {
*
* @param array $header
* Table header.
* @param string[]|null $keys
* (optional) Search keys.
* @param string|null $keys
* (optional) Search keyword that may include one or more '*' as wildcard
* values.
*
* @return array
* Array of items to be displayed on the current page.

View file

@ -21,8 +21,27 @@ interface OutboundPathProcessorInterface {
* @param string $path
* The path to process, with a leading slash.
* @param array $options
* An array of options such as would be passed to the generator's
* generateFromRoute() method.
* (optional) An associative array of additional options, with the following
* elements:
* - 'query': An array of query key/value-pairs (without any URL-encoding)
* to append to the URL.
* - 'fragment': A fragment identifier (named anchor) to append to the URL.
* Do not include the leading '#' character.
* - 'absolute': Defaults to FALSE. Whether to force the output to be an
* absolute link (beginning with http:). Useful for links that will be
* displayed outside the site, such as in an RSS feed.
* - 'language': An optional language object used to look up the alias
* for the URL. If $options['language'] is omitted, it defaults to the
* current language for the language type LanguageInterface::TYPE_URL.
* - 'https': Whether this URL should point to a secure location. If not
* defined, the current scheme is used, so the user stays on HTTP or HTTPS
* respectively. TRUE enforces HTTPS and FALSE enforces HTTP.
* - 'base_url': Only used internally by a path processor, for example, to
* modify the base URL when a language dependent URL requires so.
* - 'prefix': Only used internally, to modify the path when a language
* dependent URL requires so.
* - 'route': The route object for the given path. It will be set by
* \Drupal\Core\Routing\UrlGenerator::generateFromRoute().
* @param \Symfony\Component\HttpFoundation\Request $request
* The HttpRequest object representing the current request.
* @param \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata

View file

@ -269,6 +269,11 @@ abstract class RenderElement extends PluginBase implements ElementInterface {
return $element;
}
// Add a data attribute to disable automatic refocus after ajax call.
if (!empty($element['#ajax']['disable-refocus'])) {
$element['#attributes']['data-disable-refocus'] = "true";
}
// Add a reasonable default event handler if none was specified.
if (isset($element['#ajax']) && !isset($element['#ajax']['event'])) {
switch ($element['#type']) {

View file

@ -0,0 +1,33 @@
<?php
/**
* @file
* Contains \Drupal\Core\Routing\Enhancer\FormRouteEnhancer.
*/
namespace Drupal\Core\Routing\Enhancer;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Enhancer to add a wrapping controller for _form routes.
*/
class FormRouteEnhancer implements RouteEnhancerInterface {
/**
* {@inheritdoc}
*/
public function applies(Route $route) {
return $route->hasDefault('_form') && !$route->hasDefault('_controller');
}
/**
* {@inheritdoc}
*/
public function enhance(array $defaults, Request $request) {
$defaults['_controller'] = 'controller.form:getContentResult';
return $defaults;
}
}

View file

@ -44,8 +44,12 @@ class ParamConversionEnhancer implements RouteEnhancerInterface, EventSubscriber
* {@inheritdoc}
*/
public function enhance(array $defaults, Request $request) {
$defaults['_raw_variables'] = $this->copyRawVariables($defaults);
return $this->paramConverterManager->convert($defaults);
// Just run the parameter conversion once per request.
if (!isset($defaults['_raw_variables'])) {
$defaults['_raw_variables'] = $this->copyRawVariables($defaults);
$defaults = $this->paramConverterManager->convert($defaults);
}
return $defaults;
}
/**

View file

@ -246,7 +246,7 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
* @return array
* An array of outlines that could match the specified path parts.
*/
public function getCandidateOutlines(array $parts) {
protected function getCandidateOutlines(array $parts) {
$number_parts = count($parts);
$ancestors = array();
$length = $number_parts - 1;
@ -355,7 +355,7 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
/**
* Comparison function for usort on routes.
*/
public function routeProviderRouteCompare(array $a, array $b) {
protected function routeProviderRouteCompare(array $a, array $b) {
if ($a['fit'] == $b['fit']) {
return strcmp($a['name'], $b['name']);
}

View file

@ -309,6 +309,9 @@ class UrlGenerator implements UrlGeneratorInterface {
$name = $this->getRouteDebugMessage($name);
$this->processRoute($name, $route, $parameters, $generated_url);
$path = $this->getInternalPathFromRoute($name, $route, $parameters, $query_params);
// Outbound path processors might need the route object for the path, e.g.
// to get the path pattern.
$options['route'] = $route;
$path = $this->processPath($path, $options, $generated_url);
if (!empty($options['prefix'])) {

View file

@ -77,6 +77,10 @@ interface UrlGeneratorInterface extends VersatileGeneratorInterface {
* @throws \Symfony\Component\Routing\Exception\InvalidParameterException
* Thrown when a parameter value for a placeholder is not correct because it
* does not match the requirement.
*
* @internal
* Should not be used in user code.
* Use \Drupal\Core\Url instead.
*/
public function generateFromRoute($name, $parameters = array(), $options = array(), $collect_bubbleable_metadata = FALSE);

View file

@ -47,28 +47,41 @@ class ReverseProxyMiddleware implements HttpKernelInterface {
*/
public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
// Initialize proxy settings.
if ($this->settings->get('reverse_proxy', FALSE)) {
$ip_header = $this->settings->get('reverse_proxy_header', 'X_FORWARDED_FOR');
static::setSettingsOnRequest($request, $this->settings);
return $this->httpKernel->handle($request, $type, $catch);
}
/**
* Sets reverse proxy settings on Request object.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* A Request instance.
* @param \Drupal\Core\Site\Settings $settings
* The site settings.
*/
public static function setSettingsOnRequest(Request $request, Settings $settings) {
// Initialize proxy settings.
if ($settings->get('reverse_proxy', FALSE)) {
$ip_header = $settings->get('reverse_proxy_header', 'X_FORWARDED_FOR');
$request::setTrustedHeaderName($request::HEADER_CLIENT_IP, $ip_header);
$proto_header = $this->settings->get('reverse_proxy_proto_header', 'X_FORWARDED_PROTO');
$proto_header = $settings->get('reverse_proxy_proto_header', 'X_FORWARDED_PROTO');
$request::setTrustedHeaderName($request::HEADER_CLIENT_PROTO, $proto_header);
$host_header = $this->settings->get('reverse_proxy_host_header', 'X_FORWARDED_HOST');
$host_header = $settings->get('reverse_proxy_host_header', 'X_FORWARDED_HOST');
$request::setTrustedHeaderName($request::HEADER_CLIENT_HOST, $host_header);
$port_header = $this->settings->get('reverse_proxy_port_header', 'X_FORWARDED_PORT');
$port_header = $settings->get('reverse_proxy_port_header', 'X_FORWARDED_PORT');
$request::setTrustedHeaderName($request::HEADER_CLIENT_PORT, $port_header);
$forwarded_header = $this->settings->get('reverse_proxy_forwarded_header', 'FORWARDED');
$forwarded_header = $settings->get('reverse_proxy_forwarded_header', 'FORWARDED');
$request::setTrustedHeaderName($request::HEADER_FORWARDED, $forwarded_header);
$proxies = $this->settings->get('reverse_proxy_addresses', array());
$proxies = $settings->get('reverse_proxy_addresses', array());
if (count($proxies) > 0) {
$request::setTrustedProxies($proxies);
}
}
return $this->httpKernel->handle($request, $type, $catch);
}
}

View file

@ -134,6 +134,11 @@ class TranslationManager implements TranslationInterface, TranslatorInterface {
* The translated string.
*/
protected function doTranslate($string, array $options = array()) {
// If a NULL langcode has been provided, unset it.
if (!isset($options['langcode']) && array_key_exists('langcode', $options)) {
unset($options['langcode']);
}
// Merge in options defaults.
$options = $options + [
'langcode' => $this->defaultLangcode,

View file

@ -117,10 +117,11 @@ class Attribute implements \ArrayAccess, \IteratorAggregate, MarkupInterface {
* An AttributeValueBase representation of the attribute's value.
*/
protected function createAttributeValue($name, $value) {
// If the value is already an AttributeValueBase object, return it
// straight away.
// If the value is already an AttributeValueBase object,
// return a new instance of the same class, but with the new name.
if ($value instanceof AttributeValueBase) {
return $value;
$class = get_class($value);
return new $class($name, $value->value());
}
// An array value or 'class' attribute name are forced to always be an
// AttributeArray value for consistency.

View file

@ -0,0 +1,47 @@
<?php
/**
* @file
* Contains \Drupal\Core\Theme\MissingThemeDependencyException.
*/
namespace Drupal\Core\Theme;
/**
* Exception to be thrown when base theme for installed theme is not installed.
*
* @see \Drupal\Core\Theme\ThemeInitialization::getActiveThemeByName().
*/
class MissingThemeDependencyException extends \Exception {
/**
* The missing theme dependency.
*
* @var string
*/
protected $theme;
/**
* Constructs the exception.
*
* @param string $message
* The exception message.
* @param string $theme
* The missing theme dependency.
*/
public function __construct($message, $theme) {
parent::__construct($message);
$this->theme = $theme;
}
/**
* Gets the machine name of the missing theme.
*
* @return string
* The machine name of the theme that is missing.
*/
public function getMissingThemeName() {
return $this->theme;
}
}

View file

@ -109,6 +109,16 @@ class ThemeInitialization implements ThemeInitializationInterface {
$ancestor = $theme_name;
while ($ancestor && isset($themes[$ancestor]->base_theme)) {
$ancestor = $themes[$ancestor]->base_theme;
if (!$this->themeHandler->themeExists($ancestor)) {
if ($ancestor == 'stable') {
// Themes that depend on Stable will be fixed by system_update_8014().
// There is no harm in not adding it as an ancestor since at worst
// some people might experience slight visual regressions on
// update.php.
continue;
}
throw new MissingThemeDependencyException(sprintf('Base theme %s has not been installed.', $ancestor), $ancestor);
}
$base_themes[] = $themes[$ancestor];
}

View file

@ -34,6 +34,9 @@ interface ThemeInitializationInterface {
*
* @return \Drupal\Core\Theme\ActiveTheme
* An active theme object instance for the given theme.
*
* @throws \Drupal\Core\Theme\MissingThemeDependencyException
* Thrown when base theme for installed theme is not installed.
*/
public function getActiveThemeByName($theme_name);
@ -54,8 +57,8 @@ interface ThemeInitializationInterface {
* @param \Drupal\Core\Extension\Extension $theme
* The theme extension object.
* @param \Drupal\Core\Extension\Extension[] $base_themes
* An array of extension objects of base theme and its bases. It is ordered
* by 'oldest first', meaning the top level of the chain will be first.
* An array of extension objects of base theme and its bases. It is ordered
* by 'next parent first', meaning the top level of the chain will be first.
*
* @return \Drupal\Core\Theme\ActiveTheme
* The active theme instance for the passed in $theme.

View file

@ -72,6 +72,9 @@ interface LinkGeneratorInterface {
* @throws \Symfony\Component\Routing\Exception\InvalidParameterException
* Thrown when a parameter value for a placeholder is not correct because it
* does not match the requirement.
*
* @internal
* Should not be used in user code. Use \Drupal\Core\Link instead.
*/
public function generate($text, Url $url);
@ -84,6 +87,10 @@ interface LinkGeneratorInterface {
* @return \Drupal\Core\GeneratedLink
* A GeneratedLink object containing a link to the given route and
* parameters and bubbleable metadata.
*
* @internal
* Should not be used in user code.
* Use \Drupal\Core\Link instead.
*/
public function generateFromLink(Link $link);

View file

@ -735,9 +735,36 @@
}
$(this.element).prop('disabled', false);
// Save element's ancestors tree so if the element is removed from the dom
// we can try to refocus one of its parents. Using addBack reverse the
// result array, meaning that index 0 is the highest parent in the hierarchy
// in this situation it is usually a <form> element.
var elementParents = $(this.element).parents('[data-drupal-selector]').addBack().toArray();
// Track if any command is altering the focus so we can avoid changing the
// focus set by the Ajax command.
var focusChanged = false;
for (var i in response) {
if (response.hasOwnProperty(i) && response[i].command && this.commands[response[i].command]) {
this.commands[response[i].command](this, response[i], status);
if (response[i].command === 'invoke' && response[i].method === 'focus') {
focusChanged = true;
}
}
}
// If the focus hasn't be changed by the ajax commands, try to refocus the
// triggering element or one of its parents if that element does not exist
// anymore.
if (!focusChanged && this.element && !$(this.element).data('disable-refocus')) {
var target = false;
for (var n = elementParents.length - 1; !target && n > 0; n--) {
target = document.querySelector('[data-drupal-selector="' + elementParents[n].getAttribute('data-drupal-selector') + '"]');
}
if (target) {
$(target).trigger('focus');
}
}

View file

@ -7,7 +7,8 @@
'use strict';
var settingsElement = document.querySelector('script[type="application/json"][data-drupal-selector="drupal-settings-json"]');
// Use direct child elements to harden against XSS exploits when CSP is on.
var settingsElement = document.querySelector('head > script[type="application/json"][data-drupal-selector="drupal-settings-json"], body > script[type="application/json"][data-drupal-selector="drupal-settings-json"]');
/**
* Variable generated by Drupal with all the configuration created from PHP.

View file

@ -120,6 +120,7 @@
type: this.method,
url: uri,
data: '',
dataType: 'json',
success: function (progress) {
// Display errors.
if (progress.status === 0) {

View file

@ -236,14 +236,10 @@
// Add event bindings to the document. The self variable is passed along
// as event handlers do not have direct access to the tableDrag object.
if (Modernizr.touchevents) {
$(document).on('touchmove', function (event) { return self.dragRow(event.originalEvent.touches[0], self); });
$(document).on('touchend', function (event) { return self.dropRow(event.originalEvent.touches[0], self); });
}
else {
$(document).on('mousemove', function (event) { return self.dragRow(event, self); });
$(document).on('mouseup', function (event) { return self.dropRow(event, self); });
}
$(document).on('touchmove', function (event) { return self.dragRow(event.originalEvent.touches[0], self); });
$(document).on('touchend', function (event) { return self.dropRow(event.originalEvent.touches[0], self); });
$(document).on('mousemove pointermove', function (event) { return self.dragRow(event, self); });
$(document).on('mouseup pointerup', function (event) { return self.dropRow(event, self); });
// React to localStorage event showing or hiding weight columns.
$(window).on('storage', $.proxy(function (e) {
@ -460,19 +456,13 @@
$item.find('td').eq(0).prepend(handle);
}
if (Modernizr.touchevents) {
handle.on('touchstart', function (event) {
event.preventDefault();
handle.on('mousedown touchstart pointerdown', function (event) {
event.preventDefault();
if (event.originalEvent.type === 'touchstart') {
event = event.originalEvent.touches[0];
self.dragStart(event, self, item);
});
}
else {
handle.on('mousedown', function (event) {
event.preventDefault();
self.dragStart(event, self, item);
});
}
}
self.dragStart(event, self, item);
});
// Prevent the anchor tag from jumping us to the top of the page.
handle.on('click', function (e) {

View file

@ -0,0 +1,63 @@
<?php
/**
* @file
* Contains \Drupal\aggregator\Tests\Migrate\MigrateAggregatorStubTest.
*/
namespace Drupal\aggregator\Tests\Migrate;
use Drupal\migrate\MigrateException;
use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
use Drupal\migrate_drupal\Tests\StubTestTrait;
/**
* Test stub creation for aggregator feeds and items.
*
* @group aggregator
*/
class MigrateAggregatorStubTest extends MigrateDrupalTestBase {
use StubTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['aggregator'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('aggregator_feed');
$this->installEntitySchema('aggregator_item');
}
/**
* Tests creation of aggregator feed stubs.
*/
public function testFeedStub() {
$this->performStubTest('aggregator_feed');
}
/**
* Tests creation of aggregator feed items.
*/
public function testItemStub() {
try {
// We expect an exception, because there's no feed to reference.
$this->performStubTest('aggregator_item');
$this->fail('Expected exception has not been thrown.');
}
catch (MigrateException $e) {
$this->assertIdentical($e->getMessage(),
'Stubbing failed, unable to generate value for field fid');
}
// The stub should pass when there's a feed to point to.
$this->createStub('aggregator_feed');
$this->performStubTest('aggregator_item');
}
}

View file

@ -89,7 +89,10 @@ function block_page_top(array &$page_top) {
*/
function block_themes_installed($theme_list) {
foreach ($theme_list as $theme) {
block_theme_initialize($theme);
// Don't initialize themes that are not displayed in the UI.
if (\Drupal::service('theme_handler')->hasUi($theme)) {
block_theme_initialize($theme);
}
}
}

View file

@ -11,6 +11,7 @@ use Drupal\Component\Utility\Html;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Controller routines for admin block routes.
@ -53,6 +54,10 @@ class BlockController extends ControllerBase {
* A #type 'page' render array containing the block region demo.
*/
public function demo($theme) {
if (!$this->themeHandler->hasUi($theme)) {
throw new NotFoundHttpException();
}
$page = [
'#title' => Html::escape($this->themeHandler->getName($theme)),
'#type' => 'page',

View file

@ -8,13 +8,42 @@
namespace Drupal\block\Controller;
use Drupal\Core\Entity\Controller\EntityListController;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Defines a controller to list blocks.
*/
class BlockListController extends EntityListController {
/**
* The theme handler.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface
*/
protected $themeHandler;
/**
* Constructs the BlockListController.
*
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
* The theme handler.
*/
public function __construct(ThemeHandlerInterface $theme_handler) {
$this->themeHandler = $theme_handler;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('theme_handler')
);
}
/**
* Shows the block administration page.
*
@ -28,6 +57,10 @@ class BlockListController extends EntityListController {
*/
public function listing($theme = NULL, Request $request = NULL) {
$theme = $theme ?: $this->config('system.theme')->get('default');
if (!$this->themeHandler->hasUi($theme)) {
throw new NotFoundHttpException();
}
return $this->entityManager()->getListBuilder('block')->render($theme, $request);
}

View file

@ -50,7 +50,7 @@ class ThemeLocalTask extends DeriverBase implements ContainerDeriverInterface {
$default_theme = $this->themeHandler->getDefault();
foreach ($this->themeHandler->listInfo() as $theme_name => $theme) {
if ($theme->status) {
if ($this->themeHandler->hasUi($theme_name)) {
$this->derivatives[$theme_name] = $base_plugin_definition;
$this->derivatives[$theme_name]['title'] = $theme->info['name'];
$this->derivatives[$theme_name]['route_parameters'] = array('theme' => $theme_name);

View file

@ -56,7 +56,9 @@ class BlockHiddenRegionTest extends WebTestBase {
// Install "block_test_theme" and set it as the default theme.
$theme = 'block_test_theme';
\Drupal::service('theme_handler')->install(array($theme));
// We need to install a non-hidden theme so that there is more than one
// local task.
\Drupal::service('theme_handler')->install(array($theme, 'stark'));
$this->config('system.theme')
->set('default', $theme)
->save();

View file

@ -197,9 +197,9 @@ class BlockTest extends BlockTestBase {
*/
public function testBlockThemeSelector() {
// Install all themes.
\Drupal::service('theme_handler')->install(array('bartik', 'seven'));
\Drupal::service('theme_handler')->install(['bartik', 'seven', 'stark']);
$theme_settings = $this->config('system.theme');
foreach (array('bartik', 'classy', 'seven') as $theme) {
foreach (['bartik', 'seven', 'stark'] as $theme) {
$this->drupalGet('admin/structure/block/list/' . $theme);
$this->assertTitle(t('Block layout') . ' | Drupal');
// Select the 'Powered by Drupal' block to be placed.

View file

@ -90,6 +90,10 @@ class BlockUiTest extends WebTestBase {
\Drupal::service('theme_handler')->install(array('test_theme'));
$this->drupalGet('admin/structure/block/demo/test_theme');
$this->assertEscaped('<strong>Test theme</strong>');
\Drupal::service('theme_handler')->install(['stable']);
$this->drupalGet('admin/structure/block/demo/stable');
$this->assertResponse(404, 'Hidden themes that are not the default theme are not supported by the block demo screen');
}
/**
@ -136,6 +140,28 @@ class BlockUiTest extends WebTestBase {
$this->drupalGet('admin/structure/block');
$element = $this->xpath('//tr[contains(@class, :class)]', [':class' => 'region-title-header']);
$this->assertTrue(!empty($element));
// Ensure hidden themes do not appear in the UI. Enable another non base
// theme and place the local tasks block.
$this->assertTrue(\Drupal::service('theme_handler')->themeExists('classy'), 'The classy base theme is enabled');
$this->drupalPlaceBlock('local_tasks_block', ['region' => 'header']);
\Drupal::service('theme_installer')->install(['stable', 'stark']);
$this->drupalGet('admin/structure/block');
$theme_handler = \Drupal::service('theme_handler');
$this->assertLink($theme_handler->getName('classy'));
$this->assertLink($theme_handler->getName('stark'));
$this->assertNoLink($theme_handler->getName('stable'));
$this->drupalGet('admin/structure/block/list/stable');
$this->assertResponse(404, 'Placing blocks through UI is not possible for a hidden base theme.');
\Drupal::configFactory()->getEditable('system.theme')->set('admin', 'stable')->save();
\Drupal::service('router.builder')->rebuildIfNeeded();
$this->drupalPlaceBlock('local_tasks_block', ['region' => 'header', 'theme' => 'stable']);
$this->drupalGet('admin/structure/block');
$this->assertLink($theme_handler->getName('stable'));
$this->drupalGet('admin/structure/block/list/stable');
$this->assertResponse(200, 'Placing blocks through UI is possible for a hidden base theme that is the admin theme.');
}
/**

View file

@ -65,6 +65,14 @@ class NewDefaultThemeBlocksTest extends WebTestBase {
unset($new_blocks[str_replace($default_theme . '_', $new_theme . '_', $default_block_name)]);
}
$this->assertTrue(empty($new_blocks), 'The new theme has exactly the same blocks as the previous default theme.');
// Install a hidden base theme and ensure blocks are not copied.
$base_theme = 'test_basetheme';
\Drupal::service('theme_handler')->install([$base_theme]);
$new_blocks = $this->container->get('entity.query')->get('block')
->condition('theme', $base_theme)
->execute();
$this->assertTrue(empty($new_blocks), 'Installing a hidden base theme does not copy blocks from the default theme.');
}
}

View file

@ -27,7 +27,11 @@ class BlockLocalTasksTest extends LocalTaskIntegrationTestBase {
$themes = array();
$themes['test_a'] = (object) array(
'status' => 0,
'status' => 1,
'info' => array(
'name' => 'test_a',
'hidden' => TRUE,
),
);
$themes['test_b'] = (object) array(
'status' => 1,
@ -45,6 +49,13 @@ class BlockLocalTasksTest extends LocalTaskIntegrationTestBase {
$theme_handler->expects($this->any())
->method('listInfo')
->will($this->returnValue($themes));
$theme_handler->expects($this->any())
->method('hasUi')
->willReturnMap([
['test_a', FALSE],
['test_b', TRUE],
['test_c', TRUE],
]);
$container = new ContainerBuilder();
$container->set('config.factory', $config_factory);

View file

@ -44,6 +44,7 @@ entity.block_content.canonical:
_admin_route: TRUE
requirements:
_entity_access: 'block_content.update'
block_content: \d+
entity.block_content.edit_form:
path: '/block/{block_content}'
@ -53,6 +54,7 @@ entity.block_content.edit_form:
_admin_route: TRUE
requirements:
_entity_access: 'block_content.update'
block_content: \d+
entity.block_content.delete_form:
path: '/block/{block_content}/delete'
@ -63,6 +65,7 @@ entity.block_content.delete_form:
_admin_route: TRUE
requirements:
_entity_access: 'block_content.delete'
block_content: \d+
block_content.type_add:
path: '/admin/structure/block/block-content/types/add'

View file

@ -185,17 +185,15 @@ class BlockContentTypeTest extends BlockContentTestBase {
->getStorage('block_content');
// Install all themes.
\Drupal::service('theme_handler')->install(array('bartik', 'seven'));
$themes = array('bartik', 'seven', 'classy');
\Drupal::service('theme_handler')->install(['bartik', 'seven', 'stark']);
$theme_settings = $this->config('system.theme');
foreach ($themes as $default_theme) {
foreach (['bartik', 'seven', 'stark'] as $default_theme) {
// Change the default theme.
$theme_settings->set('default', $default_theme)->save();
\Drupal::service('router.builder')->rebuild();
// For each installed theme, go to its block page and test the redirects.
$themes = array('bartik', 'classy', 'seven');
foreach ($themes as $theme) {
foreach (['bartik', 'seven', 'stark'] as $theme) {
// Test that adding a block from the 'place blocks' form sends you to the
// block configure form.
$path = $theme == $default_theme ? 'admin/structure/block' : "admin/structure/block/list/$theme";

View file

@ -0,0 +1,63 @@
<?php
/**
* @file
* Contains \Drupal\block_content\Tests\Migrate\MigrateBlockContentStubTest.
*/
namespace Drupal\block_content\Tests\Migrate;
use Drupal\block_content\Entity\BlockContentType;
use Drupal\migrate\MigrateException;
use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
use Drupal\migrate_drupal\Tests\StubTestTrait;
/**
* Test stub creation for block_content entities.
*
* @group block_content
*/
class MigrateBlockContentStubTest extends MigrateDrupalTestBase {
use StubTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['block_content'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('block_content');
}
/**
* Tests creation of block content stubs with no block_content_type available.
*/
public function testStubFailure() {
$message = 'Expected MigrateException thrown when no bundles exist.';
try {
$this->createStub('block_content');
$this->fail($message);
}
catch (MigrateException $e) {
$this->pass($message);
$this->assertEqual('Stubbing failed, no bundles available for entity type: block_content', $e->getMessage());
}
}
/**
* Tests creation of block content stubs when there is a block_content_type.
*/
public function testStubSuccess() {
BlockContentType::create([
'id' => 'test_block_content_type',
'label' => 'Test block content type',
])->save();
$this->performStubTest('block_content');
}
}

View file

@ -29,6 +29,7 @@ book.export:
requirements:
_permission: 'access printer-friendly version'
_entity_access: 'node.view'
node: \d+
entity.node.book_outline_form:
path: '/node/{node}/outline'
@ -38,6 +39,7 @@ entity.node.book_outline_form:
requirements:
_permission: 'administer book outlines'
_entity_access: 'node.view'
node: \d+
options:
_node_operation_route: TRUE
@ -62,3 +64,4 @@ entity.node.book_remove_form:
_permission: 'administer book outlines'
_entity_access: 'node.view'
_access_book_removable: 'TRUE'
node: \d+

View file

@ -9,6 +9,27 @@
'use strict';
function parseAttributes(element) {
var parsedAttributes = {};
var domElement = element.$;
var attribute = null;
var attributeName;
for (var attrIndex = 0; attrIndex < domElement.attributes.length; attrIndex++) {
attribute = domElement.attributes.item(attrIndex);
attributeName = attribute.nodeName.toLowerCase();
// Don't consider data-cke-saved- attributes; they're just there to work
// around browser quirks.
if (attributeName.substring(0, 15) === 'data-cke-saved-') {
continue;
}
// Store the value for this attribute, unless there's a data-cke-saved-
// alternative for it, which will contain the quirk-free, original value.
parsedAttributes[attributeName] = element.data('cke-saved-' + attributeName) || attribute.nodeValue;
}
return parsedAttributes;
}
CKEDITOR.plugins.add('drupallink', {
init: function (editor) {
// Add the commands for link and unlink.
@ -16,8 +37,7 @@
allowedContent: {
a: {
attributes: {
'!href': true,
'target': true
'!href': true
},
classes: {}
}
@ -34,35 +54,16 @@
var drupalImageUtils = CKEDITOR.plugins.drupalimage;
var focusedImageWidget = drupalImageUtils && drupalImageUtils.getFocusedWidget(editor);
var linkElement = getSelectedLink(editor);
var linkDOMElement = null;
// Set existing values based on selected element.
var existingValues = {};
if (linkElement && linkElement.$) {
linkDOMElement = linkElement.$;
// Populate an array with the link's current attributes.
var attribute = null;
var attributeName;
for (var attrIndex = 0; attrIndex < linkDOMElement.attributes.length; attrIndex++) {
attribute = linkDOMElement.attributes.item(attrIndex);
attributeName = attribute.nodeName.toLowerCase();
// Don't consider data-cke-saved- attributes; they're just there
// to work around browser quirks.
if (attributeName.substring(0, 15) === 'data-cke-saved-') {
continue;
}
// Store the value for this attribute, unless there's a
// data-cke-saved- alternative for it, which will contain the
// quirk-free, original value.
existingValues[attributeName] = linkElement.data('cke-saved-' + attributeName) || attribute.nodeValue;
}
existingValues = parseAttributes(linkElement);
}
// Or, if an image widget is focused, we're editing a link wrapping
// an image widget.
else if (focusedImageWidget && focusedImageWidget.data.link) {
var url = focusedImageWidget.data.link.url;
existingValues.href = url.protocol + url.url;
existingValues = CKEDITOR.tools.clone(focusedImageWidget.data.link);
}
// Prepare a save callback to be used upon saving the dialog.
@ -70,14 +71,7 @@
// If an image widget is focused, we're not editing an independent
// link, but we're wrapping an image widget in a link.
if (focusedImageWidget) {
var urlMatch = returnValues.attributes.href.match(urlRegex);
focusedImageWidget.setData('link', {
type: 'url',
url: {
protocol: urlMatch[1],
url: urlMatch[2]
}
});
focusedImageWidget.setData('link', CKEDITOR.tools.extend(returnValues.attributes, focusedImageWidget.data.link));
editor.fire('saveSnapshot');
return;
}
@ -97,11 +91,6 @@
range.selectNodeContents(text);
}
// Ignore a disabled target attribute.
if (returnValues.attributes.target === 0) {
delete returnValues.attributes.target;
}
// Create the new link by applying a style to the new text.
var style = new CKEDITOR.style({element: 'a', attributes: returnValues.attributes});
style.type = CKEDITOR.STYLE_INLINE;
@ -150,8 +139,7 @@
allowedContent: {
a: {
attributes: {
'!href': true,
'target': true
'!href': true
}
}
},
@ -280,8 +268,6 @@
return null;
}
var urlRegex = /^((?:http|https):\/\/)?(.*)$/;
/**
* The image2 plugin is currently tightly coupled to the link plugin: it
* calls CKEDITOR.plugins.link.parseLinkAttributes().
@ -296,28 +282,20 @@
*/
CKEDITOR.plugins.link = CKEDITOR.plugins.link || {
parseLinkAttributes: function (editor, element) {
var href = (element && (element.data('cke-saved-href') || element.getAttribute('href'))) || '';
var urlMatch = href.match(urlRegex);
return {
type: 'url',
url: {
protocol: urlMatch[1],
url: urlMatch[2]
}
};
return parseAttributes(element);
},
getLinkAttributes: function (editor, data) {
var set = {};
var protocol = (data.url && typeof data.url.protocol !== 'undefined') ? data.url.protocol : 'http://';
var url = (data.url && CKEDITOR.tools.trim(data.url.url)) || '';
set['data-cke-saved-href'] = (url.indexOf('/') === 0) ? url : protocol + url;
// Browser need the "href" fro copy/paste link to work. (#6641)
if (set['data-cke-saved-href']) {
set.href = set['data-cke-saved-href'];
for (var attributeName in data) {
if (data.hasOwnProperty(attributeName)) {
set[attributeName] = data[attributeName];
}
}
// CKEditor tracks the *actual* saved href in a data-cke-saved-* attribute
// to work around browser quirks. We need to update it.
set['data-cke-saved-href'] = set.href;
// Remove all attributes which are not currently set.
var removed = {};
for (var s in set) {

View file

@ -23,6 +23,7 @@ entity.comment.edit_form:
_entity_form: 'comment.default'
requirements:
_entity_access: 'comment.update'
comment: \d+
comment.approve:
path: '/comment/{comment}/approve'
@ -33,6 +34,7 @@ comment.approve:
requirements:
_entity_access: 'comment.approve'
_csrf_token: 'TRUE'
comment: \d+
entity.comment.canonical:
path: '/comment/{comment}'
@ -41,6 +43,7 @@ entity.comment.canonical:
_controller: '\Drupal\comment\Controller\CommentController::commentPermalink'
requirements:
_entity_access: 'comment.view'
comment: \d+
entity.comment.delete_form:
path: '/comment/{comment}/delete'
@ -49,6 +52,7 @@ entity.comment.delete_form:
_entity_form: 'comment.delete'
requirements:
_entity_access: 'comment.delete'
comment: \d+
comment.reply:
path: '/comment/reply/{entity_type}/{entity}/{field_name}/{pid}'
@ -77,6 +81,7 @@ comment.node_redirect:
requirements:
_entity_access: 'node.view'
_module_dependencies: 'node'
node: \d+
entity.comment_type.collection:
path: '/admin/structure/comment'

View file

@ -39,6 +39,34 @@ class CommentSelection extends DefaultSelection {
return $query;
}
/**
* {@inheritdoc}
*/
public function createNewEntity($entity_type_id, $bundle, $label, $uid) {
$comment = parent::createNewEntity($entity_type_id, $bundle, $label, $uid);
// In order to create a referenceable comment, it needs to published.
/** @var \Drupal\comment\CommentInterface $comment */
$comment->setPublished(TRUE);
return $comment;
}
/**
* {@inheritdoc}
*/
public function validateReferenceableNewEntities(array $entities) {
$entities = parent::validateReferenceableNewEntities($entities);
// Mirror the conditions checked in buildEntityQuery().
if (!$this->currentUser->hasPermission('administer comments')) {
$entities = array_filter($entities, function ($comment) {
/** @var \Drupal\comment\CommentInterface $comment */
return $comment->isPublished();
});
}
return $entities;
}
/**
* {@inheritdoc}
*/

View file

@ -10,6 +10,7 @@ namespace Drupal\comment\Plugin\migrate\destination;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\State\StateInterface;
use Drupal\migrate\Entity\MigrationInterface;
use Drupal\migrate\MigrateException;
@ -62,13 +63,15 @@ class EntityComment extends EntityContentBase {
* The list of bundles this entity type has.
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager service.
* @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
* The field type plugin manager service.
* @param \Drupal\Core\State\StateInterface $state
* The state storage object.
* @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
* The query object that can query the given entity type.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, StateInterface $state, QueryFactory $entity_query) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager);
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager, StateInterface $state, QueryFactory $entity_query) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager);
$this->state = $state;
$this->entityQuery = $entity_query;
}
@ -86,6 +89,7 @@ class EntityComment extends EntityContentBase {
$container->get('entity.manager')->getStorage($entity_type),
array_keys($container->get('entity.manager')->getBundleInfo($entity_type)),
$container->get('entity.manager'),
$container->get('plugin.manager.field.field_type'),
$container->get('state'),
$container->get('entity.query')
);
@ -110,32 +114,9 @@ class EntityComment extends EntityContentBase {
*/
protected function processStubRow(Row $row) {
parent::processStubRow($row);
$stub_commented_entity_type = $row->getDestinationProperty('entity_type');
// While parent::getEntity() fills the bundle property for stub entities
// if it's still empty, here we must also make sure entity_id/entity_type
// are filled (so $comment->getCommentedEntity() always returns a value).
if (empty($this->stubCommentedEntityIds[$stub_commented_entity_type])) {
// Fill stub entity id. Any id will do, as long as it exists.
$entity_type = $this->entityManager->getDefinition($stub_commented_entity_type);
$id_key = $entity_type->getKey('id');
$result = $this->entityQuery
->get($stub_commented_entity_type)
->range(0, 1)
->execute();
if ($result) {
$this->stubCommentedEntityIds[$stub_commented_entity_type] = array_pop($result);
$row->setSourceProperty($id_key, $this->stubCommentedEntityIds[$stub_commented_entity_type]);
}
else {
throw new MigrateException(t('Could not find parent entity to use for comment %id', ['%id' => implode(':', $row->getSourceIdValues())]), MigrationInterface::MESSAGE_ERROR);
}
}
$row->setDestinationProperty('entity_id', $this->stubCommentedEntityIds[$stub_commented_entity_type]);
$row->setDestinationProperty('entity_type', $stub_commented_entity_type);
$row->setDestinationProperty('created', REQUEST_TIME);
$row->setDestinationProperty('changed', REQUEST_TIME);
// Neither uid nor name is required in itself, but it is required to set one
// of them.
$row->setDestinationProperty('name', 'anonymous_stub');
}
}

View file

@ -39,7 +39,7 @@ class CommentValidationTest extends EntityUnitTestBase {
*/
public function testValidation() {
// Add a user.
$user = User::create(array('name' => 'test'));
$user = User::create(array('name' => 'test', 'status' => TRUE));
$user->save();
// Add comment type.

View file

@ -0,0 +1,78 @@
<?php
/**
* @file
* Contains \Drupal\comment\Tests\Migrate\MigrateCommentStubTest.
*/
namespace Drupal\comment\Tests\Migrate;
use Drupal\comment\Entity\CommentType;
use Drupal\migrate\MigrateException;
use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
use Drupal\migrate_drupal\Tests\StubTestTrait;
use Drupal\node\Entity\NodeType;
/**
* Test stub creation for comment entities.
*
* @group comment
*/
class MigrateCommentStubTest extends MigrateDrupalTestBase {
use StubTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['comment', 'node'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('comment');
$this->installEntitySchema('node');
// Make sure uid 0 is created (default uid for comments is 0).
$storage = \Drupal::entityManager()->getStorage('user');
// Insert a row for the anonymous user.
$storage
->create(array(
'uid' => 0,
'status' => 0,
'name' => '',
))
->save();
// Need at least one node type and comment type present.
NodeType::create([
'type' => 'testnodetype',
'name' => 'Test node type',
])->save();
CommentType::create([
'id' => 'testcommenttype',
'label' => 'Test comment type',
'target_entity_type_id' => 'node',
])->save();
}
/**
* Tests creation of comment stubs.
*/
public function testStub() {
try {
// We expect an exception, because there's no node to reference.
$this->performStubTest('comment');
$this->fail('Expected exception has not been thrown.');
}
catch (MigrateException $e) {
$this->assertIdentical($e->getMessage(),
'Stubbing failed, unable to generate value for field entity_id');
}
// The stub should pass when there's a node to point to.
$this->createStub('node');
$this->performStubTest('comment');
}
}

View file

@ -22,13 +22,7 @@ class MigrateCommentTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'comment',
// Directly testing that a stub comment's entity_id is populated upon
// importing is not straightforward, but RDF module serves as an implicit
// test - its hook_comment_storage_load() references a stubbed comment.
'rdf',
];
public static $modules = ['comment'];
/**
* {@inheritdoc}

View file

@ -67,6 +67,7 @@ class ConfigImportInstallProfileTest extends WebTestBase {
$core['module']['testing_config_import'] = 0;
unset($core['module']['syslog']);
unset($core['theme']['stark']);
$core['theme']['stable'] = 0;
$core['theme']['classy'] = 0;
$sync->write('core.extension', $core);
$sync->deleteAll('syslog.');

View file

@ -92,12 +92,12 @@ class ConfigInstallProfileOverrideTest extends WebTestBase {
}
// Install the config_test module and ensure that the override from the
// install profile is not used. Optional configuration can not override
// install profile is used. Optional configuration can override
// configuration in a modules config/install directory.
$this->container->get('module_installer')->install(['config_test']);
$this->rebuildContainer();
$config_test_storage = \Drupal::entityManager()->getStorage('config_test');
$this->assertEqual($config_test_storage->load('dotted.default')->label(), 'Default', 'The config_test entity is not overridden by the profile optional configuration.');
$this->assertEqual($config_test_storage->load('dotted.default')->label(), 'Default install profile override', 'The config_test entity is overridden by the profile optional configuration.');
// Test that override of optional configuration does work.
$this->assertEqual($config_test_storage->load('override')->label(), 'Override', 'The optional config_test entity is overridden by the profile optional configuration.');
// Test that override of optional configuration which introduces an unmet

View file

@ -95,6 +95,7 @@ class ConfigInstallProfileUnmetDependenciesTest extends InstallerTestBase {
else {
$this->fail('Expected Drupal\Core\Config\UnmetDependenciesException exception thrown');
}
$this->assertErrorLogged('Configuration objects (system.action.user_block_user_action) provided by user have unmet dependencies in');
}
}

View file

@ -54,3 +54,4 @@ entity.user.contact_form:
_controller: '\Drupal\contact\Controller\ContactController::contactPersonalPage'
requirements:
_access_contact_personal_tab: 'TRUE'
user: \d+

View file

@ -29,3 +29,19 @@ function content_translation_enable() {
$message = t('<a href=":settings_url">Enable translation</a> for <em>content types</em>, <em>taxonomy vocabularies</em>, <em>accounts</em>, or any other element you wish to translate.', $t_args);
drupal_set_message($message, 'warning');
}
/**
* @addtogroup updates-8.0.0-rc
* @{
*/
/**
* Rebuild the routes as the content translation routes have now new names.
*/
function content_translation_update_8001() {
\Drupal::service('router.builder')->rebuild();
}
/**
* @} End of "addtogroup updates-8.0.0-rc".
*/

View file

@ -53,7 +53,7 @@ function content_translation_help($route_name, RouteMatchInterface $route_match)
*/
function content_translation_module_implements_alter(&$implementations, $hook) {
switch ($hook) {
// Move some of our hook implementations to the end of the list.
// Move our hook_entity_type_alter() implementation to the end of the list.
case 'entity_type_alter':
$group = $implementations['content_translation'];
unset($implementations['content_translation']);
@ -140,7 +140,11 @@ function content_translation_entity_type_alter(array &$entity_types) {
if ($entity_type->hasLinkTemplate('canonical')) {
// Provide default route names for the translation paths.
if (!$entity_type->hasLinkTemplate('drupal:content-translation-overview')) {
$entity_type->setLinkTemplate('drupal:content-translation-overview', $entity_type->getLinkTemplate('canonical') . '/translations');
$translations_path = $entity_type->getLinkTemplate('canonical') . '/translations';
$entity_type->setLinkTemplate('drupal:content-translation-overview', $translations_path);
$entity_type->setLinkTemplate('drupal:content-translation-add', $translations_path . '/add/{source}/{target}');
$entity_type->setLinkTemplate('drupal:content-translation-edit', $translations_path . '/edit/{language}');
$entity_type->setLinkTemplate('drupal:content-translation-delete', $translations_path . '/delete/{language}');
}
// @todo Remove this as soon as menu access checks rely on the
// controller. See https://www.drupal.org/node/2155787.

View file

@ -652,7 +652,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
$source = $form_state->getValue(array('source_langcode', 'source'));
$entity_type_id = $entity->getEntityTypeId();
$form_state->setRedirect('content_translation.translation_add_' . $entity_type_id, array(
$form_state->setRedirect("entity.$entity_type_id.content_translation_add", array(
$entity_type_id => $entity->id(),
'source' => $source,
'target' => $form_object->getFormLangcode($form_state),
@ -689,7 +689,7 @@ class ContentTranslationHandler implements ContentTranslationHandlerInterface, E
$form_state->setRedirectUrl($entity->urlInfo('delete-form'));
}
else {
$form_state->setRedirect('content_translation.translation_delete_' . $entity_type_id, [
$form_state->setRedirect("entity.$entity_type_id.content_translation_delete", [
$entity_type_id => $entity->id(),
'language' => $form_object->getFormLangcode($form_state),
]);

View file

@ -127,7 +127,7 @@ class ContentTranslationController extends ControllerBase {
$langcode = $language->getId();
$add_url = new Url(
'content_translation.translation_add_' . $entity_type_id,
"entity.$entity_type_id.content_translation_add",
array(
'source' => $original,
'target' => $language->getId(),
@ -138,7 +138,7 @@ class ContentTranslationController extends ControllerBase {
)
);
$edit_url = new Url(
'content_translation.translation_edit_' . $entity_type_id,
"entity.$entity_type_id.content_translation_edit",
array(
'language' => $language->getId(),
$entity_type_id => $entity->id(),
@ -148,7 +148,7 @@ class ContentTranslationController extends ControllerBase {
)
);
$delete_url = new Url(
'content_translation.translation_delete_' . $entity_type_id,
"entity.$entity_type_id.content_translation_delete",
array(
'language' => $language->getId(),
$entity_type_id => $entity->id(),

View file

@ -112,7 +112,7 @@ class ContentTranslationRouteSubscriber extends RouteSubscriberBase {
'_admin_route' => $is_admin,
)
);
$collection->add("content_translation.translation_add_$entity_type_id", $route);
$collection->add("entity.$entity_type_id.content_translation_add", $route);
$route = new Route(
$path . '/edit/{language}',
@ -137,7 +137,7 @@ class ContentTranslationRouteSubscriber extends RouteSubscriberBase {
'_admin_route' => $is_admin,
)
);
$collection->add("content_translation.translation_edit_$entity_type_id", $route);
$collection->add("entity.$entity_type_id.content_translation_edit", $route);
$route = new Route(
$path . '/delete/{language}',
@ -162,7 +162,7 @@ class ContentTranslationRouteSubscriber extends RouteSubscriberBase {
'_admin_route' => $is_admin,
)
);
$collection->add("content_translation.translation_delete_$entity_type_id", $route);
$collection->add("entity.$entity_type_id.content_translation_delete", $route);
}
}

View file

@ -19,7 +19,7 @@ class ContentTranslationEnableTest extends WebTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['entity_test', 'menu_link_content'];
public static $modules = ['entity_test', 'menu_link_content', 'node'];
/**
* Tests that entity schemas are up-to-date after enabling translation.
@ -39,6 +39,9 @@ class ContentTranslationEnableTest extends WebTestBase {
$requirement_value = $this->cssSelect("tr.system-status-report__entry th:contains('Entity/field definitions') + td");
$this->assertEqual(t('Up to date'), trim((string) $requirement_value[0]));
$this->drupalGet('admin/config/regional/content-language');
// The node entity type should not be an option because it has no bundles.
$this->assertNoRaw('entity_types[node]');
// Enable content translation on entity types that have will have a
// content_translation_uid.
$edit = [
@ -47,12 +50,23 @@ class ContentTranslationEnableTest extends WebTestBase {
'entity_types[entity_test_mul]' => TRUE,
'settings[entity_test_mul][entity_test_mul][translatable]' => TRUE,
];
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
$this->drupalPostForm(NULL, $edit, t('Save configuration'));
// No pending updates should be available.
$this->drupalGet('admin/reports/status');
$requirement_value = $this->cssSelect("tr.system-status-report__entry th:contains('Entity/field definitions') + td");
$this->assertEqual(t('Up to date'), trim((string) $requirement_value[0]));
// Create a node type and check the content translation settings are now
// available for nodes.
$edit = array(
'name' => 'foo',
'title_label' => 'title for foo',
'type' => 'foo',
);
$this->drupalPostForm('admin/structure/types/add', $edit, t('Save content type'));
$this->drupalGet('admin/config/regional/content-language');
$this->assertRaw('entity_types[node]');
}
}

View file

@ -107,7 +107,8 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
$language = ConfigurableLanguage::load($langcode);
$values[$langcode] = $this->getNewEntityValues($langcode);
$add_url = Url::fromRoute('content_translation.translation_add_' . $entity->getEntityTypeId(), [
$entity_type_id = $entity->getEntityTypeId();
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => $default_langcode,
'target' => $langcode
@ -167,7 +168,8 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
$language = ConfigurableLanguage::load($langcode);
$source_langcode = 'it';
$edit = array('source_langcode[source]' => $source_langcode);
$add_url = Url::fromRoute('content_translation.translation_add_' . $entity->getEntityTypeId(), [
$entity_type_id = $entity->getEntityTypeId();
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => $default_langcode,
'target' => $langcode
@ -180,7 +182,8 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
// Add another translation and mark the other ones as outdated.
$values[$langcode] = $this->getNewEntityValues($langcode);
$edit = $this->getEditValues($values, $langcode) + array('content_translation[retranslate]' => TRUE);
$add_url = Url::fromRoute('content_translation.translation_add_' . $entity->getEntityTypeId(), [
$entity_type_id = $entity->getEntityTypeId();
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => $source_langcode,
'target' => $langcode
@ -207,13 +210,15 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
*/
protected function doTestTranslationOverview() {
$entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
$this->drupalGet($entity->urlInfo('drupal:content-translation-overview'));
$translate_url = $entity->urlInfo('drupal:content-translation-overview');
$this->drupalGet($translate_url);
$translate_url->setAbsolute(FALSE);
foreach ($this->langcodes as $langcode) {
if ($entity->hasTranslation($langcode)) {
$language = new Language(array('id' => $langcode));
$view_path = $entity->url('canonical', array('language' => $language));
$elements = $this->xpath('//table//a[@href=:href]', array(':href' => $view_path));
$view_url = $entity->url('canonical', ['language' => $language]);
$elements = $this->xpath('//table//a[@href=:href]', [':href' => $view_url]);
$this->assertEqual((string) $elements[0], $entity->getTranslation($langcode)->label(), format_string('Label correctly shown for %language translation.', array('%language' => $langcode)));
$edit_path = $entity->url('edit-form', array('language' => $language));
$elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a[@href=:href]', array(':href' => $edit_path));
@ -343,7 +348,7 @@ abstract class ContentTranslationUITestBase extends ContentTranslationTestBase {
// Check that the translator cannot delete the original translation.
$args = [$this->entityTypeId => $entity->id(), 'language' => 'en'];
$this->drupalGet(Url::fromRoute('content_translation.translation_delete_' . $this->entityTypeId, $args));
$this->drupalGet(Url::fromRoute("entity.$this->entityTypeId.content_translation_delete", $args));
$this->assertResponse(403);
}

View file

@ -73,7 +73,7 @@ class ContentTranslationWorkflowsTest extends ContentTranslationTestBase {
// Create a translation.
$this->drupalLogin($this->translator);
$add_translation_url = Url::fromRoute('content_translation.translation_add_' . $this->entityTypeId, [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $this->langcodes[2]]);
$add_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_add", [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $this->langcodes[2]]);
$this->drupalPostForm($add_translation_url, array(), t('Save'));
$this->rebuildContainer();
}
@ -175,7 +175,7 @@ class ContentTranslationWorkflowsTest extends ContentTranslationTestBase {
$this->assertResponse($expected_status['overview'], SafeMarkup::format('The @user_label has the expected translation overview access.', $args));
// Check whether the user is allowed to create a translation.
$add_translation_url = Url::fromRoute('content_translation.translation_add_' . $this->entityTypeId, [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $langcode], $options);
$add_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_add", [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $langcode], $options);
if ($expected_status['add_translation'] == 200) {
$this->clickLink('Add');
$this->assertUrl($add_translation_url->toString(), [], 'The translation overview points to the translation form when creating translations.');
@ -193,7 +193,7 @@ class ContentTranslationWorkflowsTest extends ContentTranslationTestBase {
// Check whether the user is allowed to edit a translation.
$langcode = $this->langcodes[2];
$options['language'] = $languages[$langcode];
$edit_translation_url = Url::fromRoute('content_translation.translation_edit_' . $this->entityTypeId, [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
$edit_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_edit", [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
if ($expected_status['edit_translation'] == 200) {
$this->drupalGet($translations_url);
$editor = $expected_status['edit'] == 200;
@ -221,7 +221,7 @@ class ContentTranslationWorkflowsTest extends ContentTranslationTestBase {
// Check whether the user is allowed to delete a translation.
$langcode = $this->langcodes[2];
$options['language'] = $languages[$langcode];
$delete_translation_url = Url::fromRoute('content_translation.translation_delete_' . $this->entityTypeId, [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
$delete_translation_url = Url::fromRoute("entity.$this->entityTypeId.content_translation_delete", [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
if ($expected_status['delete_translation'] == 200) {
$this->drupalGet($translations_url);
$editor = $expected_status['delete'] == 200;

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