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:
parent
4afb23bbd3
commit
7784f4c23d
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -50,3 +50,7 @@ Thumbs.db
|
|||
# SASS #
|
||||
##########
|
||||
.sass-cache
|
||||
|
||||
# Things in the core directory that Drupal 8 commits in the repository.
|
||||
!core/**/*.gz
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
222
composer.lock
generated
|
@ -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": [],
|
||||
|
|
|
@ -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
|
||||
----------------------
|
||||
|
|
|
@ -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
|
||||
- ?
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -260,7 +260,6 @@ drupal.tabledrag:
|
|||
misc/tabledrag.js: { weight: -1 }
|
||||
dependencies:
|
||||
- core/jquery
|
||||
- core/modernizr
|
||||
- core/drupal
|
||||
- core/drupalSettings
|
||||
- core/jquery.once
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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'])) {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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}
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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[] */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']) {
|
||||
|
|
33
core/lib/Drupal/Core/Routing/Enhancer/FormRouteEnhancer.php
Normal file
33
core/lib/Drupal/Core/Routing/Enhancer/FormRouteEnhancer.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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']);
|
||||
}
|
||||
|
|
|
@ -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'])) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
type: this.method,
|
||||
url: uri,
|
||||
data: '',
|
||||
dataType: 'json',
|
||||
success: function (progress) {
|
||||
// Display errors.
|
||||
if (progress.status === 0) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
|
@ -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+
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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}
|
||||
*/
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
|
@ -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}
|
||||
|
|
|
@ -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.');
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,3 +54,4 @@ entity.user.contact_form:
|
|||
_controller: '\Drupal\contact\Controller\ContactController::contactPersonalPage'
|
||||
requirements:
|
||||
_access_contact_personal_tab: 'TRUE'
|
||||
user: \d+
|
||||
|
|
|
@ -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".
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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),
|
||||
]);
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
Reference in a new issue