diff --git a/.editorconfig b/.editorconfig index ccc6a28..242859d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,3 +12,6 @@ indent_size = 2 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true + +[composer.json] +indent_size = 4 diff --git a/.htaccess b/.htaccess index 974999a..f4024c6 100644 --- a/.htaccess +++ b/.htaccess @@ -3,7 +3,7 @@ # # Protect files and directories from prying eyes. - + Require all denied @@ -93,7 +93,7 @@ AddEncoding gzip svgz # If you do not have mod_rewrite installed, you should remove these # directories from your webroot or otherwise protect them from being # downloaded. - RewriteRule "(^|/)\." - [F] + RewriteRule "(^|/)\.(?!well-known)" - [F] # If your site can be accessed both with and without the 'www.' prefix, you # can use one of the following settings to redirect users to your preferred diff --git a/composer.json b/composer.json index 7f170ce..727e031 100644 --- a/composer.json +++ b/composer.json @@ -1,44 +1,44 @@ { - "name": "drupal/drupal", - "description": "Drupal is an open source content management platform powering millions of websites and applications.", - "type": "project", - "license": "GPL-2.0+", - "require": { - "composer/installers": "^1.0.21", - "wikimedia/composer-merge-plugin": "~1.3" - }, - "replace": { - "drupal/core": "~8.0" - }, - "minimum-stability": "dev", - "prefer-stable": true, - "config": { - "preferred-install": "dist", - "autoloader-suffix": "Drupal8" - }, - "extra": { - "_readme": [ - "By default Drupal loads the autoloader from ./vendor/autoload.php.", - "To change the autoloader you can edit ./autoload.php." - ], - "merge-plugin": { - "include": [ - "core/composer.json" - ], - "recurse": false, - "replace": false, - "merge-extra": false + "name": "drupal/drupal", + "description": "Drupal is an open source content management platform powering millions of websites and applications.", + "type": "project", + "license": "GPL-2.0+", + "require": { + "composer/installers": "^1.0.21", + "wikimedia/composer-merge-plugin": "~1.3" + }, + "replace": { + "drupal/core": "~8.2" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "preferred-install": "dist", + "autoloader-suffix": "Drupal8" + }, + "extra": { + "_readme": [ + "By default Drupal loads the autoloader from ./vendor/autoload.php.", + "To change the autoloader you can edit ./autoload.php." + ], + "merge-plugin": { + "include": [ + "core/composer.json" + ], + "recurse": false, + "replace": false, + "merge-extra": false + } + }, + "autoload": { + "psr-4": { + "Drupal\\Core\\Composer\\": "core/lib/Drupal/Core/Composer" + } + }, + "scripts": { + "pre-autoload-dump": "Drupal\\Core\\Composer\\Composer::preAutoloadDump", + "post-autoload-dump": "Drupal\\Core\\Composer\\Composer::ensureHtaccess", + "post-package-install": "Drupal\\Core\\Composer\\Composer::vendorTestCodeCleanup", + "post-package-update": "Drupal\\Core\\Composer\\Composer::vendorTestCodeCleanup" } - }, - "autoload": { - "psr-4": { - "Drupal\\Core\\Composer\\": "core/lib/Drupal/Core/Composer" - } - }, - "scripts": { - "pre-autoload-dump": "Drupal\\Core\\Composer\\Composer::preAutoloadDump", - "post-autoload-dump": "Drupal\\Core\\Composer\\Composer::ensureHtaccess", - "post-package-install": "Drupal\\Core\\Composer\\Composer::vendorTestCodeCleanup", - "post-package-update": "Drupal\\Core\\Composer\\Composer::vendorTestCodeCleanup" - } } diff --git a/composer.lock b/composer.lock index e20eaab..10bec80 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "dac77f10c1f7585fd1f7344c6a376338", + "hash": "8ac71fcd7128b95405b61f3ce16fc769", "content-hash": "73cbcb262208c5d802cb528279f2a95c", "packages": [ { @@ -848,6 +848,48 @@ "time": "2015-08-15 19:32:36" }, { + "name": "ircmaxell/password-compat", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/ircmaxell/password_compat.git", + "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c", + "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "autoload": { + "files": [ + "lib/password.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anthony Ferrara", + "email": "ircmaxell@php.net", + "homepage": "http://blog.ircmaxell.com" + } + ], + "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", + "homepage": "https://github.com/ircmaxell/password_compat", + "keywords": [ + "hashing", + "password" + ], + "time": "2014-11-20 16:49:30" + }, + { "name": "masterminds/html5", "version": "2.1.2", "source": { @@ -913,6 +955,54 @@ "time": "2015-06-07 08:43:18" }, { + "name": "paragonie/random_compat", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "a208865a5aeffc2dbbef2a5b3409887272d93f32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/a208865a5aeffc2dbbef2a5b3409887272d93f32", + "reference": "a208865a5aeffc2dbbef2a5b3409887272d93f32", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2015-12-01 02:52:15" + }, + { "name": "psr/http-message", "version": "1.0", "source": { @@ -1107,34 +1197,37 @@ }, { "name": "symfony/class-loader", - "version": "v2.7.6", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", - "reference": "320f8d2a9cdbcbeb24be602c124aae9d998474a4" + "reference": "98e9089a428ed0e39423b67352c57ef5910a3269" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/320f8d2a9cdbcbeb24be602c124aae9d998474a4", - "reference": "320f8d2a9cdbcbeb24be602c124aae9d998474a4", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/98e9089a428ed0e39423b67352c57ef5910a3269", + "reference": "98e9089a428ed0e39423b67352c57ef5910a3269", "shasum": "" }, "require": { "php": ">=5.3.9" }, "require-dev": { - "symfony/finder": "~2.0,>=2.0.5" + "symfony/finder": "~2.0,>=2.0.5|~3.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\ClassLoader\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1152,29 +1245,30 @@ ], "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", - "time": "2015-10-23 14:47:27" + "time": "2016-01-03 15:33:41" }, { "name": "symfony/console", - "version": "v2.7.6", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5efd632294c8320ea52492db22292ff853a43766" + "reference": "d0239fb42f98dd02e7d342f793c5d2cdee0c478d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5efd632294c8320ea52492db22292ff853a43766", - "reference": "5efd632294c8320ea52492db22292ff853a43766", + "url": "https://api.github.com/repos/symfony/console/zipball/d0239fb42f98dd02e7d342f793c5d2cdee0c478d", + "reference": "d0239fb42f98dd02e7d342f793c5d2cdee0c478d", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.1", - "symfony/process": "~2.1" + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/process": "~2.1|~3.0.0" }, "suggest": { "psr/log": "For using the console logger", @@ -1184,13 +1278,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1208,7 +1305,7 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2015-10-20 14:38:46" + "time": "2016-01-14 08:33:16" }, { "name": "symfony/debug", @@ -1266,16 +1363,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v2.7.6", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "af284e795ec8a08c80d1fc47518fd23004b89847" + "reference": "ba94a914e244e0d05f0aaef460d5558d5541d2b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/af284e795ec8a08c80d1fc47518fd23004b89847", - "reference": "af284e795ec8a08c80d1fc47518fd23004b89847", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/ba94a914e244e0d05f0aaef460d5558d5541d2b1", + "reference": "ba94a914e244e0d05f0aaef460d5558d5541d2b1", "shasum": "" }, "require": { @@ -1285,9 +1382,9 @@ "symfony/expression-language": "<2.6" }, "require-dev": { - "symfony/config": "~2.2", - "symfony/expression-language": "~2.6", - "symfony/yaml": "~2.1" + "symfony/config": "~2.2|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/yaml": "~2.1|~3.0.0" }, "suggest": { "symfony/config": "", @@ -1297,13 +1394,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\DependencyInjection\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1321,20 +1421,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2015-10-27 15:38:06" + "time": "2016-01-12 17:46:01" }, { "name": "symfony/event-dispatcher", - "version": "v2.7.6", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8" + "reference": "ee278f7c851533e58ca307f66305ccb9188aceda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87a5db5ea887763fa3a31a5471b512ff1596d9b8", - "reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/ee278f7c851533e58ca307f66305ccb9188aceda", + "reference": "ee278f7c851533e58ca307f66305ccb9188aceda", "shasum": "" }, "require": { @@ -1342,10 +1442,10 @@ }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.6", - "symfony/expression-language": "~2.6", - "symfony/stopwatch": "~2.3" + "symfony/config": "~2.0,>=2.0.5|~3.0.0", + "symfony/dependency-injection": "~2.6|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/stopwatch": "~2.3|~3.0.0" }, "suggest": { "symfony/dependency-injection": "", @@ -1354,13 +1454,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1378,40 +1481,42 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2015-10-11 09:39:48" + "time": "2016-01-13 10:28:07" }, { "name": "symfony/http-foundation", - "version": "v2.7.6", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "7598eea151ae3d4134df1f9957364b17809eea75" + "reference": "9194b33c71da8ef4d05d22964376f2f9c95a1bfd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7598eea151ae3d4134df1f9957364b17809eea75", - "reference": "7598eea151ae3d4134df1f9957364b17809eea75", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9194b33c71da8ef4d05d22964376f2f9c95a1bfd", + "reference": "9194b33c71da8ef4d05d22964376f2f9c95a1bfd", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-php54": "~1.0", + "symfony/polyfill-php55": "~1.0" }, "require-dev": { - "symfony/expression-language": "~2.4" + "symfony/expression-language": "~2.4|~3.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1430,48 +1535,48 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "https://symfony.com", - "time": "2015-10-23 14:47:27" + "time": "2016-01-13 10:28:07" }, { "name": "symfony/http-kernel", - "version": "v2.7.6", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "4260f2273a446a6715063dc9ca89fd0c475c2f77" + "reference": "dbe146efdc040dc87cc730a926c7858bb3c3b3bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/4260f2273a446a6715063dc9ca89fd0c475c2f77", - "reference": "4260f2273a446a6715063dc9ca89fd0c475c2f77", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/dbe146efdc040dc87cc730a926c7858bb3c3b3bc", + "reference": "dbe146efdc040dc87cc730a926c7858bb3c3b3bc", "shasum": "" }, "require": { "php": ">=5.3.9", "psr/log": "~1.0", "symfony/debug": "~2.6,>=2.6.2", - "symfony/event-dispatcher": "~2.6,>=2.6.7", - "symfony/http-foundation": "~2.5,>=2.5.4" + "symfony/event-dispatcher": "~2.6,>=2.6.7|~3.0.0", + "symfony/http-foundation": "~2.5,>=2.5.4|~3.0.0" }, "conflict": { "symfony/config": "<2.7" }, "require-dev": { - "symfony/browser-kit": "~2.3", - "symfony/class-loader": "~2.1", - "symfony/config": "~2.7", - "symfony/console": "~2.3", - "symfony/css-selector": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.2", - "symfony/dom-crawler": "~2.0,>=2.0.5", - "symfony/expression-language": "~2.4", - "symfony/finder": "~2.0,>=2.0.5", - "symfony/process": "~2.0,>=2.0.5", - "symfony/routing": "~2.2", - "symfony/stopwatch": "~2.3", - "symfony/templating": "~2.2", - "symfony/translation": "~2.0,>=2.0.5", - "symfony/var-dumper": "~2.6" + "symfony/browser-kit": "~2.3|~3.0.0", + "symfony/class-loader": "~2.1|~3.0.0", + "symfony/config": "~2.8", + "symfony/console": "~2.3|~3.0.0", + "symfony/css-selector": "~2.0,>=2.0.5|~3.0.0", + "symfony/dependency-injection": "~2.8|~3.0.0", + "symfony/dom-crawler": "~2.0,>=2.0.5|~3.0.0", + "symfony/expression-language": "~2.4|~3.0.0", + "symfony/finder": "~2.0,>=2.0.5|~3.0.0", + "symfony/process": "~2.0,>=2.0.5|~3.0.0", + "symfony/routing": "~2.8|~3.0.0", + "symfony/stopwatch": "~2.3|~3.0.0", + "symfony/templating": "~2.2|~3.0.0", + "symfony/translation": "~2.0,>=2.0.5|~3.0.0", + "symfony/var-dumper": "~2.6|~3.0.0" }, "suggest": { "symfony/browser-kit": "", @@ -1485,13 +1590,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\HttpKernel\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1509,20 +1617,252 @@ ], "description": "Symfony HttpKernel Component", "homepage": "https://symfony.com", - "time": "2015-10-27 19:07:21" + "time": "2016-01-14 12:00:59" + }, + { + "name": "symfony/polyfill-iconv", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-iconv.git", + "reference": "be8627f0936f86fed0bec16fe79ebb4337299764" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/be8627f0936f86fed0bec16fe79ebb4337299764", + "reference": "be8627f0936f86fed0bec16fe79ebb4337299764", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-iconv": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Iconv\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Iconv extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "iconv", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-20 09:13:37" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "1289d16209491b584839022f29257ad859b8532d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/1289d16209491b584839022f29257ad859b8532d", + "reference": "1289d16209491b584839022f29257ad859b8532d", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-20 09:13:37" + }, + { + "name": "symfony/polyfill-php54", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php54.git", + "reference": "74663d5a2ff3c530c1bc0571500e0feec9094054" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/74663d5a2ff3c530c1bc0571500e0feec9094054", + "reference": "74663d5a2ff3c530c1bc0571500e0feec9094054", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php54\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-20 09:13:37" + }, + { + "name": "symfony/polyfill-php55", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php55.git", + "reference": "b4f3f07d91702f8f926339fc4fcf81671d8c27e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/b4f3f07d91702f8f926339fc4fcf81671d8c27e6", + "reference": "b4f3f07d91702f8f926339fc4fcf81671d8c27e6", + "shasum": "" + }, + "require": { + "ircmaxell/password-compat": "~1.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php55\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-20 09:13:37" }, { "name": "symfony/process", - "version": "v2.7.6", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "4a959dd4e19c2c5d7512689413921e0a74386ec7" + "reference": "6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/4a959dd4e19c2c5d7512689413921e0a74386ec7", - "reference": "4a959dd4e19c2c5d7512689413921e0a74386ec7", + "url": "https://api.github.com/repos/symfony/process/zipball/6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac", + "reference": "6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac", "shasum": "" }, "require": { @@ -1531,13 +1871,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1555,7 +1898,7 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2015-10-23 14:47:27" + "time": "2016-01-06 09:59:23" }, { "name": "symfony/psr-http-message-bridge", @@ -1613,16 +1956,16 @@ }, { "name": "symfony/routing", - "version": "v2.7.6", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "f353e1f588679c3ec987624e6c617646bd01ba38" + "reference": "5451a8a1874fd4e6a4dd347ea611d86cd8441735" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/f353e1f588679c3ec987624e6c617646bd01ba38", - "reference": "f353e1f588679c3ec987624e6c617646bd01ba38", + "url": "https://api.github.com/repos/symfony/routing/zipball/5451a8a1874fd4e6a4dd347ea611d86cd8441735", + "reference": "5451a8a1874fd4e6a4dd347ea611d86cd8441735", "shasum": "" }, "require": { @@ -1635,27 +1978,31 @@ "doctrine/annotations": "~1.0", "doctrine/common": "~2.2", "psr/log": "~1.0", - "symfony/config": "~2.7", - "symfony/expression-language": "~2.4", - "symfony/http-foundation": "~2.3", - "symfony/yaml": "~2.0,>=2.0.5" + "symfony/config": "~2.7|~3.0.0", + "symfony/expression-language": "~2.4|~3.0.0", + "symfony/http-foundation": "~2.3|~3.0.0", + "symfony/yaml": "~2.0,>=2.0.5|~3.0.0" }, "suggest": { "doctrine/annotations": "For using the annotation loader", "symfony/config": "For using the all-in-one router or any loader", + "symfony/dependency-injection": "For loading routes from a service", "symfony/expression-language": "For using expression matching", "symfony/yaml": "For using the YAML loader" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Routing\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1679,31 +2026,32 @@ "uri", "url" ], - "time": "2015-10-27 15:38:06" + "time": "2016-01-11 16:43:36" }, { "name": "symfony/serializer", - "version": "v2.7.6", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "14056684acad23b8815eb336bccc0b4ac76bd823" + "reference": "60d6ea54abf865ab8efa9811e6c146e71f8bdbff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/14056684acad23b8815eb336bccc0b4ac76bd823", - "reference": "14056684acad23b8815eb336bccc0b4ac76bd823", + "url": "https://api.github.com/repos/symfony/serializer/zipball/60d6ea54abf865ab8efa9811e6c146e71f8bdbff", + "reference": "60d6ea54abf865ab8efa9811e6c146e71f8bdbff", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-php55": "~1.0" }, "require-dev": { "doctrine/annotations": "~1.0", "doctrine/cache": "~1.0", - "symfony/config": "~2.2", - "symfony/property-access": "~2.3", - "symfony/yaml": "~2.0,>=2.0.5" + "symfony/config": "~2.2|~3.0.0", + "symfony/property-access": "~2.3|~3.0.0", + "symfony/yaml": "~2.0,>=2.0.5|~3.0.0" }, "suggest": { "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", @@ -1715,13 +2063,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Serializer\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1739,33 +2090,34 @@ ], "description": "Symfony Serializer Component", "homepage": "https://symfony.com", - "time": "2015-10-11 09:39:48" + "time": "2016-01-13 10:28:07" }, { "name": "symfony/translation", - "version": "v2.7.6", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "6ccd9289ec1c71d01a49d83480de3b5293ce30c8" + "reference": "bc0b666903944858f4ffec01c4e50c63e5c276c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/6ccd9289ec1c71d01a49d83480de3b5293ce30c8", - "reference": "6ccd9289ec1c71d01a49d83480de3b5293ce30c8", + "url": "https://api.github.com/repos/symfony/translation/zipball/bc0b666903944858f4ffec01c4e50c63e5c276c0", + "reference": "bc0b666903944858f4ffec01c4e50c63e5c276c0", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { "symfony/config": "<2.7" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~2.7", - "symfony/intl": "~2.4", - "symfony/yaml": "~2.2" + "symfony/config": "~2.8", + "symfony/intl": "~2.4|~3.0.0", + "symfony/yaml": "~2.2|~3.0.0" }, "suggest": { "psr/log": "To use logging capability in translator", @@ -1775,13 +2127,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Translation\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1799,37 +2154,36 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2015-10-27 15:38:06" + "time": "2016-01-03 15:33:41" }, { "name": "symfony/validator", - "version": "v2.7.6", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "df9021e689aa3d08367881e7f8917219fabe5e64" + "reference": "7a325d73cb492d244d9107406fe40650ac070a04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/df9021e689aa3d08367881e7f8917219fabe5e64", - "reference": "df9021e689aa3d08367881e7f8917219fabe5e64", + "url": "https://api.github.com/repos/symfony/validator/zipball/7a325d73cb492d244d9107406fe40650ac070a04", + "reference": "7a325d73cb492d244d9107406fe40650ac070a04", "shasum": "" }, "require": { "php": ">=5.3.9", - "symfony/translation": "~2.4" + "symfony/translation": "~2.4|~3.0.0" }, "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/property-access": "~2.3", - "symfony/yaml": "~2.0,>=2.0.5" + "symfony/config": "~2.2|~3.0.0", + "symfony/expression-language": "~2.4|~3.0.0", + "symfony/http-foundation": "~2.1|~3.0.0", + "symfony/intl": "~2.4|~3.0.0", + "symfony/property-access": "~2.3|~3.0.0", + "symfony/yaml": "~2.0,>=2.0.5|~3.0.0" }, "suggest": { "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", @@ -1845,13 +2199,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Validator\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1869,20 +2226,20 @@ ], "description": "Symfony Validator Component", "homepage": "https://symfony.com", - "time": "2015-10-18 20:23:18" + "time": "2016-01-12 17:46:01" }, { "name": "symfony/yaml", - "version": "v2.7.6", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "eca9019c88fbe250164affd107bc8057771f3f4d" + "reference": "34c8a4b51e751e7ea869b8262f883d008a2b81b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/eca9019c88fbe250164affd107bc8057771f3f4d", - "reference": "eca9019c88fbe250164affd107bc8057771f3f4d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/34c8a4b51e751e7ea869b8262f883d008a2b81b8", + "reference": "34c8a4b51e751e7ea869b8262f883d008a2b81b8", "shasum": "" }, "require": { @@ -1891,13 +2248,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1915,7 +2275,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2015-10-11 09:39:48" + "time": "2016-01-13 10:28:07" }, { "name": "twig/twig", diff --git a/core/.gitignore b/core/.gitignore deleted file mode 100644 index 74b6a4c..0000000 --- a/core/.gitignore +++ /dev/null @@ -1,19 +0,0 @@ -# SimpleTest breaks with the following files, so avoid adding them. -vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php -vendor/symfony/class-loader/Symfony/Component/ClassLoader/Tests/Fixtures/php5.4/traits.php -vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services11.php - -# The resources for the Validator component are not required. -vendor/symfony/validator/Symfony/Component/Validator/Resources - -# Symfony Validator depends on Symfony Translation but only requires the -# TranslatorInterface. Thus, we add only the required interface from Symfony -# Translation by ignoring everything except the interface. -vendor/symfony/translation/Symfony/Component/Translation/* -!vendor/symfony/translation/Symfony/Component/Translation/TranslatorInterface.php - -# PHPUnit provides some binary dependencies that are already available. -vendor/phpunit/phpunit/build/dependencies - -# The PHAR file below contains CRLF characters that cause a problem with PIFR. -vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectWithXsdExtensionInPhar.phar diff --git a/core/CHANGELOG.txt b/core/CHANGELOG.txt index c3e7c2a..1df1297 100644 --- a/core/CHANGELOG.txt +++ b/core/CHANGELOG.txt @@ -1,6 +1,12 @@ -Drupal 8.0.4, 2016-02-24 +Drupal 8.1.0, xxxx-xx-xx (development version) ------------------------ -- Fixed security issues (multiple vulnerabilities). See SA-CORE-2016-001. +- Removed Composer-managed vendor from the git repository: + * Drupal.org packager adds dependencies to zip and tar package. This can be + used without any further steps. + * When not using zip / tar files, e.g. when using a git clone, run composer + install to get dependencies. + * See https://www.drupal.org/documentation/install/download#git + for instructions. Drupal 8.0.0, 2015-11-19 ------------------------ diff --git a/core/MAINTAINERS.txt b/core/MAINTAINERS.txt index 63748c2..8f456eb 100644 --- a/core/MAINTAINERS.txt +++ b/core/MAINTAINERS.txt @@ -261,6 +261,10 @@ Basic Auth module - Klaus Purer 'klausi' https://www.drupal.org/u/klausi - Juampy Novillo Requena 'juampy' https://www.drupal.org/u/juampy +BigPipe module +- Wim Leers 'Wim Leers' https://www.drupal.org/u/wim-leers +- Fabian Franz 'Fabianx' https://www.drupal.org/u/fabianx + Block module - Tim Plunkett 'tim.plunkett' https://www.drupal.org/u/tim.plunkett - Ben Dougherty 'benjy' https://www.drupal.org/u/benjy @@ -533,7 +537,7 @@ participate in mentoring. - Lucas Hedding 'heddn' https://www.drupal.org/u/heddn - Valery Lourie 'valthebald' https://www.drupal.org/u/valthebald - Alina Mackenzie 'alimac' https://www.drupal.org/u/alimac -- Chris McCaferty 'cilefen' https://www.drupal.org/u/cilefen +- Chris McCafferty 'cilefen' https://www.drupal.org/u/cilefen - Jess Myrbo 'xjm' https://www.drupal.org/u/xjm - Andrea Soper 'ZenDoodles' https://www.drupal.org/u/zendoodles - Cathy Theys 'YesCT' https://www.drupal.org/u/yesct diff --git a/core/assets/vendor/ckeditor/CHANGES.md b/core/assets/vendor/ckeditor/CHANGES.md index 264066b..a11b9aa 100644 --- a/core/assets/vendor/ckeditor/CHANGES.md +++ b/core/assets/vendor/ckeditor/CHANGES.md @@ -1,6 +1,48 @@ CKEditor 4 Changelog ==================== +## CKEditor 4.5.7 + +New Features: + +* [#14327](http://dev.ckeditor.com/ticket/14327): Added Swiss German localization. + +Other Changes: + +* [#13816](http://dev.ckeditor.com/ticket/13816): Introduced a new strategy for Filling Character handling to avoid changes in DOM. This fixes the following issues: + * [#12727](http://dev.ckeditor.com/ticket/12727): [Blink] `IndexSizeError` when using the [Div Editing Area](http://ckeditor.com/addon/divarea) and [Content Templates](http://ckeditor.com/addon/templates) plugins. + * [#13377](http://dev.ckeditor.com/ticket/13377): [Widget](http://ckeditor.com/addon/widget) plugin issue when typing in Korean. + * [#13389](http://dev.ckeditor.com/ticket/13389): [Blink] [`editor.getData()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-getData) fails when the cursor is next to an `
` tag. + * [#13513](http://dev.ckeditor.com/ticket/13513): [Blink, WebKit] [Div Editing Area](http://ckeditor.com/addon/divarea) and [`editor.getData()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-getData) throw an error when an image is the only data in the editor. +* [#13884](http://dev.ckeditor.com/ticket/13884): Fixed: Copy/paste table in Firefox results in just first cell being pasted. +* [#14234](http://dev.ckeditor.com/ticket/14234): Fixed: URL input field is not marked as required in the [Embed](http://ckeditor.com/addon/embed) dialog. + +## CKEditor 4.5.6 + +New Features: + +* Introduced the [`CKEDITOR.tools.getCookie()`](http://docs.ckeditor.com/#!/api/CKEDITOR.tools-method-getCookie) and [`CKEDITOR.tools.setCookie()`](http://docs.ckeditor.com/#!/api/CKEDITOR.tools-method-setCookie) methods for accessing cookies. +* Introduced the [`CKEDITOR.tools.getCsrfToken()`](http://docs.ckeditor.com/#!/api/CKEDITOR.tools-method-getCsrfToken) method. The CSRF token is now automatically sent by the [File Browser](http://ckeditor.com/addon/filebrowser) and [File Tools](http://ckeditor.com/addon/filetools) plugins during file uploads. The server-side upload handlers may check it and use it to additionally secure the communication. + +Other Changes: + +* Updated [SCAYT](http://ckeditor.com/addon/scayt) (Spell Check As You Type): + - New features: + - CKEditor [Language](http://ckeditor.com/addon/language) plugin support. + - CKEditor [Placeholder](http://ckeditor.com/addon/placeholder) plugin support. + - [Drag&Drop](http://sdk.ckeditor.com/samples/fileupload.html) support. + - **Experimental** [GRAYT](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-grayt_autoStartup) (Grammar As You Type) functionality. + - Fixed issues: + * [#98](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/98): SCAYT affects dialog double-click. Fixed in SCAYT core. + * [#102](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/102): SCAYT core performance enhancements. + * [#104](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/104): SCAYT's spans leak into the clipboard and after pasting. + * [#105](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/105): A JavaScript error fired in case of multiple instances of CKEditor on one page. + * [#107](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/107): SCAYT should not check non-editable parts of content. + * [#108](https://github.com/WebSpellChecker/ckeditor-plugin-scayt/issues/108): Latest SCAYT copies the ID of the editor element to the iframe. + * SCAYT stops working when CKEditor [Undo plugin](http://ckeditor.com/addon/undo) not enabled. + * Issue with pasting SCAYT markup in CKEditor. + * SCAYT stops working after pressing the *Cancel* button in the WSC dialog. + ## CKEditor 4.5.5 Fixed Issues: @@ -23,7 +65,7 @@ Fixed Issues: Other Changes: -* [#13859](http://dev.ckeditor.com/ticket/13859): Test cases created with `benter.tools.createTestsForEditors` will also receive editor bot as a second parameter. +* [#13859](http://dev.ckeditor.com/ticket/13859): Test cases created with `bender.tools.createTestsForEditors` will also receive editor bot as a second parameter. ## CKEditor 4.5.4 @@ -56,7 +98,7 @@ Other Changes: New Features: -* [#13501](http://dev.ckeditor.com/ticket/13501): Added the [`config.fileTools_defaultFileName`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-fileTools_defaultFileName) option to allow setting a default filen ame for paste uploads. +* [#13501](http://dev.ckeditor.com/ticket/13501): Added the [`config.fileTools_defaultFileName`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-fileTools_defaultFileName) option to allow setting a default file name for paste uploads. * [#13603](http://dev.ckeditor.com/ticket/13603): Added support for uploading dropped BMP images. Fixed Issues: diff --git a/core/assets/vendor/ckeditor/LICENSE.md b/core/assets/vendor/ckeditor/LICENSE.md index 5e1e06d..55be53a 100644 --- a/core/assets/vendor/ckeditor/LICENSE.md +++ b/core/assets/vendor/ckeditor/LICENSE.md @@ -2,7 +2,7 @@ Software License Agreement ========================== CKEditor - The text editor for Internet - http://ckeditor.com -Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. Licensed under the terms of any of the following licenses at your choice: @@ -37,7 +37,7 @@ done by developers outside of CKSource with their express permission. The following libraries are included in CKEditor under the MIT license (see Appendix D): -* CKSource Samples Framework (included in the samples) - Copyright (c) 2014-2015, CKSource - Frederico Knabben. +* CKSource Samples Framework (included in the samples) - Copyright (c) 2014-2016, CKSource - Frederico Knabben. * PicoModal (included in `samples/js/sf.js`) - Copyright (c) 2012 James Frasca. * CodeMirror (included in the samples) - Copyright (C) 2014 by Marijn Haverbeke and others. diff --git a/core/assets/vendor/ckeditor/build-config.js b/core/assets/vendor/ckeditor/build-config.js index b44b245..c4eda91 100644 --- a/core/assets/vendor/ckeditor/build-config.js +++ b/core/assets/vendor/ckeditor/build-config.js @@ -40,6 +40,8 @@ var CKBUILDER_CONFIG = { 'bender-err.log', 'bender-out.log', 'node_modules', + '.jscsrc', + '.jshintrc', // Parts of CKEditor that we consciously don't ship with Drupal. 'adapters', 'config.js', @@ -69,6 +71,7 @@ var CKBUILDER_CONFIG = { 'wysiwygarea' : 1, 'indent' : 1, 'indentlist' : 1, + 'language' : 1, 'list' : 1, 'magicline' : 1, 'maximize' : 1, diff --git a/core/assets/vendor/ckeditor/ckeditor.js b/core/assets/vendor/ckeditor/ckeditor.js index 14f1840..63e4fee 100644 --- a/core/assets/vendor/ckeditor/ckeditor.js +++ b/core/assets/vendor/ckeditor/ckeditor.js @@ -1,618 +1,622 @@ /* -Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved. +Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.md or http://ckeditor.com/license */ -(function(){window.CKEDITOR&&window.CKEDITOR.dom||(window.CKEDITOR||(window.CKEDITOR=function(){var a=/(^|.*[\\\/])ckeditor\.js(?:\?.*|;.*)?$/i,e={timestamp:"FAD9",version:"4.5.5",revision:"b34ea4d",rnd:Math.floor(900*Math.random())+100,_:{pending:[],basePathSrcPattern:a},status:"unloaded",basePath:function(){var f=window.CKEDITOR_BASEPATH||"";if(!f)for(var g=document.getElementsByTagName("script"),c=0;ch.getListenerIndex(f)){h=h.listeners;e||(e=this);isNaN(k)&&(k=10);var p=this;b.fn=f;b.priority=k;for(var v=h.length-1;0<=v;v--)if(h[v].priority<=k)return h.splice(v+1,0,b),{removeListener:d};h.unshift(b)}return{removeListener:d}},once:function(){var a=Array.prototype.slice.call(arguments),c=a[1];a[1]=function(a){a.removeListener();return c.apply(this, -arguments)};return this.on.apply(this,a)},capture:function(){CKEDITOR.event.useCapture=1;var a=this.on.apply(this,arguments);CKEDITOR.event.useCapture=0;return a},fire:function(){var a=0,c=function(){a=1},m=0,l=function(){m=1};return function(k,b,d){var h=e(this)[k];k=a;var p=m;a=m=0;if(h){var v=h.listeners;if(v.length)for(var v=v.slice(0),u,t=0;tg.getListenerIndex(h)){g=g.listeners;f||(f=this);isNaN(k)&&(k=10);var p=this;b.fn=h;b.priority=k;for(var u=g.length-1;0<=u;u--)if(g[u].priority<=k)return g.splice(u+1,0,b),{removeListener:d};g.unshift(b)}return{removeListener:d}},once:function(){var a=Array.prototype.slice.call(arguments),c=a[1];a[1]=function(a){a.removeListener();return c.apply(this, +arguments)};return this.on.apply(this,a)},capture:function(){CKEDITOR.event.useCapture=1;var a=this.on.apply(this,arguments);CKEDITOR.event.useCapture=0;return a},fire:function(){var a=0,c=function(){a=1},m=0,l=function(){m=1};return function(k,b,d){var g=f(this)[k];k=a;var p=m;a=m=0;if(g){var u=g.listeners;if(u.length)for(var u=u.slice(0),v,r=0;rdocument.documentMode),mobile:-1g||c.quirks);c.gecko&&(e=a.match(/rv:([\d\.]+)/))&&(e=e[1].split("."),g=1E4*e[0]+100*(e[1]||0)+1*(e[2]||0));c.air&&(g=parseFloat(a.match(/ adobeair\/(\d+)/)[1]));c.webkit&&(g=parseFloat(a.match(/ applewebkit\/(\d+)/)[1]));c.version=g;c.isCompatible=!(c.ie&&7>g)&&!(c.gecko&&4E4>g)&&!(c.webkit&& -534>g);c.hidpi=2<=window.devicePixelRatio;c.needsBrFiller=c.gecko||c.webkit||c.ie&&10g;c.cssClass="cke_browser_"+(c.ie?"ie":c.gecko?"gecko":c.webkit?"webkit":"unknown");c.quirks&&(c.cssClass+=" cke_browser_quirks");c.ie&&(c.cssClass+=" cke_browser_ie"+(c.quirks?"6 cke_browser_iequirks":c.version));c.air&&(c.cssClass+=" cke_browser_air");c.iOS&&(c.cssClass+=" cke_browser_ios");c.hidpi&&(c.cssClass+=" cke_hidpi");return c}()),"unloaded"==CKEDITOR.status&&function(){CKEDITOR.event.implementOn(CKEDITOR); -CKEDITOR.loadFullCore=function(){if("basic_ready"!=CKEDITOR.status)CKEDITOR.loadFullCore._load=1;else{delete CKEDITOR.loadFullCore;var a=document.createElement("script");a.type="text/javascript";a.src=CKEDITOR.basePath+"ckeditor.js";document.getElementsByTagName("head")[0].appendChild(a)}};CKEDITOR.loadFullCoreTimeout=0;CKEDITOR.add=function(a){(this._.pending||(this._.pending=[])).push(a)};(function(){CKEDITOR.domReady(function(){var a=CKEDITOR.loadFullCore,e=CKEDITOR.loadFullCoreTimeout;a&&(CKEDITOR.status= -"basic_ready",a&&a._load?a():e&&setTimeout(function(){CKEDITOR.loadFullCore&&CKEDITOR.loadFullCore()},1E3*e))})})();CKEDITOR.status="basic_loaded"}(),"use strict",CKEDITOR.VERBOSITY_WARN=1,CKEDITOR.VERBOSITY_ERROR=2,CKEDITOR.verbosity=CKEDITOR.VERBOSITY_WARN|CKEDITOR.VERBOSITY_ERROR,CKEDITOR.warn=function(a,e){CKEDITOR.verbosity&CKEDITOR.VERBOSITY_WARN&&CKEDITOR.fire("log",{type:"warn",errorCode:a,additionalData:e})},CKEDITOR.error=function(a,e){CKEDITOR.verbosity&CKEDITOR.VERBOSITY_ERROR&&CKEDITOR.fire("log", -{type:"error",errorCode:a,additionalData:e})},CKEDITOR.on("log",function(a){if(window.console&&window.console.log){var e=console[a.data.type]?a.data.type:"log",c=a.data.errorCode;if(a=a.data.additionalData)console[e]("[CKEDITOR] Error code: "+c+".",a);else console[e]("[CKEDITOR] Error code: "+c+".");console[e]("[CKEDITOR] For more information about this error go to http://docs.ckeditor.com/#!/guide/dev_errors-section-"+c)}},null,null,999),CKEDITOR.dom={},function(){var a=[],e=CKEDITOR.env.gecko?"-moz-": -CKEDITOR.env.webkit?"-webkit-":CKEDITOR.env.ie?"-ms-":"",c=/&/g,g=/>/g,f=/|\s) /g,function(h,d){return d+"\x26nbsp;"}).replace(/ (?=<)/g, -"\x26nbsp;")},getNextNumber:function(){var d=0;return function(){return++d}}(),getNextId:function(){return"cke_"+this.getNextNumber()},getUniqueId:function(){for(var d="e",h=0;8>h;h++)d+=Math.floor(65536*(1+Math.random())).toString(16).substring(1);return d},override:function(d,h){var b=h(d);b.prototype=d.prototype;return b},setTimeout:function(d,h,b,a,k){k||(k=window);b||(b=k);return k.setTimeout(function(){a?d.apply(b,[].concat(a)):d.apply(b)},h||0)},trim:function(){var d=/(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g; -return function(h){return h.replace(d,"")}}(),ltrim:function(){var d=/^[ \t\n\r]+/g;return function(h){return h.replace(d,"")}}(),rtrim:function(){var d=/[ \t\n\r]+$/g;return function(h){return h.replace(d,"")}}(),indexOf:function(d,h){if("function"==typeof h)for(var b=0,a=d.length;bb;b++)h[b]=("0"+parseInt(h[b],10).toString(16)).slice(-2);return"#"+h.join("")})},parseCssText:function(b, -h,a){var k={};a&&(a=new CKEDITOR.dom.element("span"),a.setAttribute("style",b),b=CKEDITOR.tools.convertRgbToHex(a.getAttribute("style")||""));if(!b||";"==b)return k;b.replace(/"/g,'"').replace(/\s*([^:;\s]+)\s*:\s*([^;]+)\s*(?=;|$)/g,function(b,d,a){h&&(d=d.toLowerCase(),"font-family"==d&&(a=a.toLowerCase().replace(/["']/g,"").replace(/\s*,\s*/g,",")),a=CKEDITOR.tools.trim(a));k[d]=a});return k},writeCssText:function(b,h){var a,k=[];for(a in b)k.push(a+":"+b[a]);h&&k.sort();return k.join("; ")}, -objectCompare:function(b,h,a){var k;if(!b&&!h)return!0;if(!b||!h)return!1;for(k in b)if(b[k]!=h[k])return!1;if(!a)for(k in h)if(b[k]!=h[k])return!1;return!0},objectKeys:function(b){var h=[],a;for(a in b)h.push(a);return h},convertArrayToObject:function(b,h){var a={};1==arguments.length&&(h=!0);for(var k=0,c=b.length;kCKEDITOR.env.version&&(this.type==CKEDITOR.NODE_ELEMENT||this.type==CKEDITOR.NODE_DOCUMENT_FRAGMENT)&&g(f);return f},hasPrevious:function(){return!!this.$.previousSibling},hasNext:function(){return!!this.$.nextSibling},insertAfter:function(a){a.$.parentNode.insertBefore(this.$,a.$.nextSibling);return a},insertBefore:function(a){a.$.parentNode.insertBefore(this.$,a.$);return a},insertBeforeMe:function(a){this.$.parentNode.insertBefore(a.$, -this.$);return a},getAddress:function(a){for(var e=[],c=this.getDocument().$.documentElement,g=this.$;g&&g!=c;){var f=g.parentNode;f&&e.unshift(this.getIndex.call({$:g},a));g=f}return e},getDocument:function(){return new CKEDITOR.dom.document(this.$.ownerDocument||this.$.parentNode.ownerDocument)},getIndex:function(a){function e(a,c){var k=c?a.nextSibling:a.previousSibling;return k&&k.nodeType==CKEDITOR.NODE_TEXT?k.nodeValue?k:e(k,c):null}var c=this.$,g=-1,f;if(!this.$.parentNode||a&&c.nodeType== -CKEDITOR.NODE_TEXT&&!c.nodeValue&&!e(c)&&!e(c,!0))return-1;do if(!a||c==this.$||c.nodeType!=CKEDITOR.NODE_TEXT||!f&&c.nodeValue)g++,f=c.nodeType==CKEDITOR.NODE_TEXT;while(c=c.previousSibling);return g},getNextSourceNode:function(a,e,c){if(c&&!c.call){var g=c;c=function(a){return!a.equals(g)}}a=!a&&this.getFirst&&this.getFirst();var f;if(!a){if(this.type==CKEDITOR.NODE_ELEMENT&&c&&!1===c(this,!0))return null;a=this.getNext()}for(;!a&&(f=(f||this).getParent());){if(c&&!1===c(f,!0))return null;a=f.getNext()}return!a|| -c&&!1===c(a)?null:e&&e!=a.type?a.getNextSourceNode(!1,e,c):a},getPreviousSourceNode:function(a,e,c){if(c&&!c.call){var g=c;c=function(a){return!a.equals(g)}}a=!a&&this.getLast&&this.getLast();var f;if(!a){if(this.type==CKEDITOR.NODE_ELEMENT&&c&&!1===c(this,!0))return null;a=this.getPrevious()}for(;!a&&(f=(f||this).getParent());){if(c&&!1===c(f,!0))return null;a=f.getPrevious()}return!a||c&&!1===c(a)?null:e&&a.type!=e?a.getPreviousSourceNode(!1,e,c):a},getPrevious:function(a){var e=this.$,c;do c=(e= -e.previousSibling)&&10!=e.nodeType&&new CKEDITOR.dom.node(e);while(c&&a&&!a(c));return c},getNext:function(a){var e=this.$,c;do c=(e=e.nextSibling)&&new CKEDITOR.dom.node(e);while(c&&a&&!a(c));return c},getParent:function(a){var e=this.$.parentNode;return e&&(e.nodeType==CKEDITOR.NODE_ELEMENT||a&&e.nodeType==CKEDITOR.NODE_DOCUMENT_FRAGMENT)?new CKEDITOR.dom.node(e):null},getParents:function(a){var e=this,c=[];do c[a?"push":"unshift"](e);while(e=e.getParent());return c},getCommonAncestor:function(a){if(a.equals(this))return this; -if(a.contains&&a.contains(this))return a;var e=this.contains?this:this.getParent();do if(e.contains(a))return e;while(e=e.getParent());return null},getPosition:function(a){var e=this.$,c=a.$;if(e.compareDocumentPosition)return e.compareDocumentPosition(c);if(e==c)return CKEDITOR.POSITION_IDENTICAL;if(this.type==CKEDITOR.NODE_ELEMENT&&a.type==CKEDITOR.NODE_ELEMENT){if(e.contains){if(e.contains(c))return CKEDITOR.POSITION_CONTAINS+CKEDITOR.POSITION_PRECEDING;if(c.contains(e))return CKEDITOR.POSITION_IS_CONTAINED+ -CKEDITOR.POSITION_FOLLOWING}if("sourceIndex"in e)return 0>e.sourceIndex||0>c.sourceIndex?CKEDITOR.POSITION_DISCONNECTED:e.sourceIndex=document.documentMode||!e||(a=e+":"+ -a);return new CKEDITOR.dom.nodeList(this.$.getElementsByTagName(a))},getHead:function(){var a=this.$.getElementsByTagName("head")[0];return a=a?new CKEDITOR.dom.element(a):this.getDocumentElement().append(new CKEDITOR.dom.element("head"),!0)},getBody:function(){return new CKEDITOR.dom.element(this.$.body)},getDocumentElement:function(){return new CKEDITOR.dom.element(this.$.documentElement)},getWindow:function(){return new CKEDITOR.dom.window(this.$.parentWindow||this.$.defaultView)},write:function(a){this.$.open("text/html", -"replace");CKEDITOR.env.ie&&(a=a.replace(/(?:^\s*]*?>)|^/i,'$\x26\n\x3cscript data-cke-temp\x3d"1"\x3e('+CKEDITOR.tools.fixDomain+")();\x3c/script\x3e"));this.$.write(a);this.$.close()},find:function(a){return new CKEDITOR.dom.nodeList(this.$.querySelectorAll(a))},findOne:function(a){return(a=this.$.querySelector(a))?new CKEDITOR.dom.element(a):null},_getHtml5ShivFrag:function(){var a=this.getCustomData("html5ShivFrag");a||(a=this.$.createDocumentFragment(),CKEDITOR.tools.enableHtml5Elements(a, -!0),this.setCustomData("html5ShivFrag",a));return a}}),CKEDITOR.dom.nodeList=function(a){this.$=a},CKEDITOR.dom.nodeList.prototype={count:function(){return this.$.length},getItem:function(a){return 0>a||a>=this.$.length?null:(a=this.$[a])?new CKEDITOR.dom.node(a):null}},CKEDITOR.dom.element=function(a,e){"string"==typeof a&&(a=(e?e.$:document).createElement(a));CKEDITOR.dom.domObject.call(this,a)},CKEDITOR.dom.element.get=function(a){return(a="string"==typeof a?document.getElementById(a)||document.getElementsByName(a)[0]: -a)&&(a.$?a:new CKEDITOR.dom.element(a))},CKEDITOR.dom.element.prototype=new CKEDITOR.dom.node,CKEDITOR.dom.element.createFromHtml=function(a,e){var c=new CKEDITOR.dom.element("div",e);c.setHtml(a);return c.getFirst().remove()},CKEDITOR.dom.element.setMarker=function(a,e,c,g){var f=e.getCustomData("list_marker_id")||e.setCustomData("list_marker_id",CKEDITOR.tools.getNextNumber()).getCustomData("list_marker_id"),m=e.getCustomData("list_marker_names")||e.setCustomData("list_marker_names",{}).getCustomData("list_marker_names"); -a[f]=e;m[c]=1;return e.setCustomData(c,g)},CKEDITOR.dom.element.clearAllMarkers=function(a){for(var e in a)CKEDITOR.dom.element.clearMarkers(a,a[e],1)},CKEDITOR.dom.element.clearMarkers=function(a,e,c){var g=e.getCustomData("list_marker_names"),f=e.getCustomData("list_marker_id"),m;for(m in g)e.removeCustomData(m);e.removeCustomData("list_marker_names");c&&(e.removeCustomData("list_marker_id"),delete a[f])},function(){function a(a,b){return-1<(" "+a+" ").replace(m," ").indexOf(" "+b+" ")}function e(a){var b= -!0;a.$.id||(a.$.id="cke_tmp_"+CKEDITOR.tools.getNextNumber(),b=!1);return function(){b||a.removeAttribute("id")}}function c(a,b){return"#"+a.$.id+" "+b.split(/,\s*/).join(", #"+a.$.id+" ")}function g(a){for(var b=0,d=0,h=l[a].length;dCKEDITOR.env.version?this.$.text+=a:this.append(new CKEDITOR.dom.text(a))},appendBogus:function(a){if(a||CKEDITOR.env.needsBrFiller){for(a= -this.getLast();a&&a.type==CKEDITOR.NODE_TEXT&&!CKEDITOR.tools.rtrim(a.getText());)a=a.getPrevious();a&&a.is&&a.is("br")||(a=this.getDocument().createElement("br"),CKEDITOR.env.gecko&&a.setAttribute("type","_moz"),this.append(a))}},breakParent:function(a,b){var d=new CKEDITOR.dom.range(this.getDocument());d.setStartAfter(this);d.setEndAfter(a);var h=d.extractContents(!1,b||!1);d.insertNode(this.remove());h.insertAfterNode(this)},contains:document.compareDocumentPosition?function(a){return!!(this.$.compareDocumentPosition(a.$)& -16)}:function(a){var b=this.$;return a.type!=CKEDITOR.NODE_ELEMENT?b.contains(a.getParent().$):b!=a.$&&b.contains(a.$)},focus:function(){function a(){try{this.$.focus()}catch(b){}}return function(b){b?CKEDITOR.tools.setTimeout(a,100,this):a.call(this)}}(),getHtml:function(){var a=this.$.innerHTML;return CKEDITOR.env.ie?a.replace(/<\?[^>]*>/g,""):a},getOuterHtml:function(){if(this.$.outerHTML)return this.$.outerHTML.replace(/<\?[^>]*>/,"");var a=this.$.ownerDocument.createElement("div");a.appendChild(this.$.cloneNode(!0)); -return a.innerHTML},getClientRect:function(){var a=CKEDITOR.tools.extend({},this.$.getBoundingClientRect());!a.width&&(a.width=a.right-a.left);!a.height&&(a.height=a.bottom-a.top);return a},setHtml:CKEDITOR.env.ie&&9>CKEDITOR.env.version?function(a){try{var b=this.$;if(this.getParent())return b.innerHTML=a;var d=this.getDocument()._getHtml5ShivFrag();d.appendChild(b);b.innerHTML=a;d.removeChild(b);return a}catch(h){this.$.innerHTML="";b=new CKEDITOR.dom.element("body",this.getDocument());b.$.innerHTML= -a;for(b=b.getChildren();b.count();)this.append(b.getItem(0));return a}}:function(a){return this.$.innerHTML=a},setText:function(){var a=document.createElement("p");a.innerHTML="x";a=a.textContent;return function(b){this.$[a?"textContent":"innerText"]=b}}(),getAttribute:function(){var a=function(a){return this.$.getAttribute(a,2)};return CKEDITOR.env.ie&&(CKEDITOR.env.ie7Compat||CKEDITOR.env.quirks)?function(a){switch(a){case "class":a="className";break;case "http-equiv":a="httpEquiv";break;case "name":return this.$.name; -case "tabindex":return a=this.$.getAttribute(a,2),0!==a&&0===this.$.tabIndex&&(a=null),a;case "checked":return a=this.$.attributes.getNamedItem(a),(a.specified?a.nodeValue:this.$.checked)?"checked":null;case "hspace":case "value":return this.$[a];case "style":return this.$.style.cssText;case "contenteditable":case "contentEditable":return this.$.attributes.getNamedItem("contentEditable").specified?this.$.getAttribute("contentEditable"):null}return this.$.getAttribute(a,2)}:a}(),getChildren:function(){return new CKEDITOR.dom.nodeList(this.$.childNodes)}, +!0:c.safari=!0);var e=0;c.ie&&(e=f?parseFloat(f[1]):c.quirks||!document.documentMode?parseFloat(a.match(/msie (\d+)/)[1]):document.documentMode,c.ie9Compat=9==e,c.ie8Compat=8==e,c.ie7Compat=7==e,c.ie6Compat=7>e||c.quirks);c.gecko&&(f=a.match(/rv:([\d\.]+)/))&&(f=f[1].split("."),e=1E4*f[0]+100*(f[1]||0)+1*(f[2]||0));c.air&&(e=parseFloat(a.match(/ adobeair\/(\d+)/)[1]));c.webkit&&(e=parseFloat(a.match(/ applewebkit\/(\d+)/)[1]));c.version=e;c.isCompatible=!(c.ie&&7>e)&&!(c.gecko&&4E4>e)&&!(c.webkit&& +534>e);c.hidpi=2<=window.devicePixelRatio;c.needsBrFiller=c.gecko||c.webkit||c.ie&&10e;c.cssClass="cke_browser_"+(c.ie?"ie":c.gecko?"gecko":c.webkit?"webkit":"unknown");c.quirks&&(c.cssClass+=" cke_browser_quirks");c.ie&&(c.cssClass+=" cke_browser_ie"+(c.quirks?"6 cke_browser_iequirks":c.version));c.air&&(c.cssClass+=" cke_browser_air");c.iOS&&(c.cssClass+=" cke_browser_ios");c.hidpi&&(c.cssClass+=" cke_hidpi");return c}()),"unloaded"==CKEDITOR.status&&function(){CKEDITOR.event.implementOn(CKEDITOR); +CKEDITOR.loadFullCore=function(){if("basic_ready"!=CKEDITOR.status)CKEDITOR.loadFullCore._load=1;else{delete CKEDITOR.loadFullCore;var a=document.createElement("script");a.type="text/javascript";a.src=CKEDITOR.basePath+"ckeditor.js";document.getElementsByTagName("head")[0].appendChild(a)}};CKEDITOR.loadFullCoreTimeout=0;CKEDITOR.add=function(a){(this._.pending||(this._.pending=[])).push(a)};(function(){CKEDITOR.domReady(function(){var a=CKEDITOR.loadFullCore,f=CKEDITOR.loadFullCoreTimeout;a&&(CKEDITOR.status= +"basic_ready",a&&a._load?a():f&&setTimeout(function(){CKEDITOR.loadFullCore&&CKEDITOR.loadFullCore()},1E3*f))})})();CKEDITOR.status="basic_loaded"}(),"use strict",CKEDITOR.VERBOSITY_WARN=1,CKEDITOR.VERBOSITY_ERROR=2,CKEDITOR.verbosity=CKEDITOR.VERBOSITY_WARN|CKEDITOR.VERBOSITY_ERROR,CKEDITOR.warn=function(a,f){CKEDITOR.verbosity&CKEDITOR.VERBOSITY_WARN&&CKEDITOR.fire("log",{type:"warn",errorCode:a,additionalData:f})},CKEDITOR.error=function(a,f){CKEDITOR.verbosity&CKEDITOR.VERBOSITY_ERROR&&CKEDITOR.fire("log", +{type:"error",errorCode:a,additionalData:f})},CKEDITOR.on("log",function(a){if(window.console&&window.console.log){var f=console[a.data.type]?a.data.type:"log",c=a.data.errorCode;if(a=a.data.additionalData)console[f]("[CKEDITOR] Error code: "+c+".",a);else console[f]("[CKEDITOR] Error code: "+c+".");console[f]("[CKEDITOR] For more information about this error go to http://docs.ckeditor.com/#!/guide/dev_errors-section-"+c)}},null,null,999),CKEDITOR.dom={},function(){var a=[],f=CKEDITOR.env.gecko?"-moz-": +CKEDITOR.env.webkit?"-webkit-":CKEDITOR.env.ie?"-ms-":"",c=/&/g,e=/>/g,h=/|\s) /g,function(g,d){return d+"\x26nbsp;"}).replace(/ (?=<)/g, +"\x26nbsp;")},getNextNumber:function(){var d=0;return function(){return++d}}(),getNextId:function(){return"cke_"+this.getNextNumber()},getUniqueId:function(){for(var d="e",g=0;8>g;g++)d+=Math.floor(65536*(1+Math.random())).toString(16).substring(1);return d},override:function(d,g){var b=g(d);b.prototype=d.prototype;return b},setTimeout:function(d,g,b,a,k){k||(k=window);b||(b=k);return k.setTimeout(function(){a?d.apply(b,[].concat(a)):d.apply(b)},g||0)},trim:function(){var d=/(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g; +return function(g){return g.replace(d,"")}}(),ltrim:function(){var d=/^[ \t\n\r]+/g;return function(g){return g.replace(d,"")}}(),rtrim:function(){var d=/[ \t\n\r]+$/g;return function(g){return g.replace(d,"")}}(),indexOf:function(d,g){if("function"==typeof g)for(var b=0,a=d.length;bd;d++)g[d]=("0"+parseInt(g[d],10).toString(16)).slice(-2);return"#"+g.join("")})},parseCssText:function(d, +g,b){var a={};b&&(b=new CKEDITOR.dom.element("span"),b.setAttribute("style",d),d=CKEDITOR.tools.convertRgbToHex(b.getAttribute("style")||""));if(!d||";"==d)return a;d.replace(/"/g,'"').replace(/\s*([^:;\s]+)\s*:\s*([^;]+)\s*(?=;|$)/g,function(d,b,k){g&&(b=b.toLowerCase(),"font-family"==b&&(k=k.toLowerCase().replace(/["']/g,"").replace(/\s*,\s*/g,",")),k=CKEDITOR.tools.trim(k));a[b]=k});return a},writeCssText:function(d,g){var b,a=[];for(b in d)a.push(b+":"+d[b]);g&&a.sort();return a.join("; ")}, +objectCompare:function(d,g,b){var a;if(!d&&!g)return!0;if(!d||!g)return!1;for(a in d)if(d[a]!=g[a])return!1;if(!b)for(a in g)if(d[a]!=g[a])return!1;return!0},objectKeys:function(d){var g=[],b;for(b in d)g.push(b);return g},convertArrayToObject:function(d,g){var b={};1==arguments.length&&(g=!0);for(var a=0,k=d.length;aa;a++)b.push(Math.floor(256*Math.random()));for(a=0;aCKEDITOR.env.version&&(this.type==CKEDITOR.NODE_ELEMENT||this.type==CKEDITOR.NODE_DOCUMENT_FRAGMENT)&&e(h);return h},hasPrevious:function(){return!!this.$.previousSibling}, +hasNext:function(){return!!this.$.nextSibling},insertAfter:function(a){a.$.parentNode.insertBefore(this.$,a.$.nextSibling);return a},insertBefore:function(a){a.$.parentNode.insertBefore(this.$,a.$);return a},insertBeforeMe:function(a){this.$.parentNode.insertBefore(a.$,this.$);return a},getAddress:function(a){for(var f=[],c=this.getDocument().$.documentElement,e=this.$;e&&e!=c;){var h=e.parentNode;h&&f.unshift(this.getIndex.call({$:e},a));e=h}return f},getDocument:function(){return new CKEDITOR.dom.document(this.$.ownerDocument|| +this.$.parentNode.ownerDocument)},getIndex:function(a){function f(a,k){var b=k?a.nextSibling:a.previousSibling;return b&&b.nodeType==CKEDITOR.NODE_TEXT?c(b)?f(b,k):b:null}function c(a){return!a.nodeValue||a.nodeValue==CKEDITOR.dom.selection.FILLING_CHAR_SEQUENCE}var e=this.$,h=-1,m;if(!this.$.parentNode||a&&e.nodeType==CKEDITOR.NODE_TEXT&&c(e)&&!f(e)&&!f(e,!0))return-1;do a&&e!=this.$&&e.nodeType==CKEDITOR.NODE_TEXT&&(m||c(e))||(h++,m=e.nodeType==CKEDITOR.NODE_TEXT);while(e=e.previousSibling);return h}, +getNextSourceNode:function(a,f,c){if(c&&!c.call){var e=c;c=function(a){return!a.equals(e)}}a=!a&&this.getFirst&&this.getFirst();var h;if(!a){if(this.type==CKEDITOR.NODE_ELEMENT&&c&&!1===c(this,!0))return null;a=this.getNext()}for(;!a&&(h=(h||this).getParent());){if(c&&!1===c(h,!0))return null;a=h.getNext()}return!a||c&&!1===c(a)?null:f&&f!=a.type?a.getNextSourceNode(!1,f,c):a},getPreviousSourceNode:function(a,f,c){if(c&&!c.call){var e=c;c=function(a){return!a.equals(e)}}a=!a&&this.getLast&&this.getLast(); +var h;if(!a){if(this.type==CKEDITOR.NODE_ELEMENT&&c&&!1===c(this,!0))return null;a=this.getPrevious()}for(;!a&&(h=(h||this).getParent());){if(c&&!1===c(h,!0))return null;a=h.getPrevious()}return!a||c&&!1===c(a)?null:f&&a.type!=f?a.getPreviousSourceNode(!1,f,c):a},getPrevious:function(a){var f=this.$,c;do c=(f=f.previousSibling)&&10!=f.nodeType&&new CKEDITOR.dom.node(f);while(c&&a&&!a(c));return c},getNext:function(a){var f=this.$,c;do c=(f=f.nextSibling)&&new CKEDITOR.dom.node(f);while(c&&a&&!a(c)); +return c},getParent:function(a){var f=this.$.parentNode;return f&&(f.nodeType==CKEDITOR.NODE_ELEMENT||a&&f.nodeType==CKEDITOR.NODE_DOCUMENT_FRAGMENT)?new CKEDITOR.dom.node(f):null},getParents:function(a){var f=this,c=[];do c[a?"push":"unshift"](f);while(f=f.getParent());return c},getCommonAncestor:function(a){if(a.equals(this))return this;if(a.contains&&a.contains(this))return a;var f=this.contains?this:this.getParent();do if(f.contains(a))return f;while(f=f.getParent());return null},getPosition:function(a){var f= +this.$,c=a.$;if(f.compareDocumentPosition)return f.compareDocumentPosition(c);if(f==c)return CKEDITOR.POSITION_IDENTICAL;if(this.type==CKEDITOR.NODE_ELEMENT&&a.type==CKEDITOR.NODE_ELEMENT){if(f.contains){if(f.contains(c))return CKEDITOR.POSITION_CONTAINS+CKEDITOR.POSITION_PRECEDING;if(c.contains(f))return CKEDITOR.POSITION_IS_CONTAINED+CKEDITOR.POSITION_FOLLOWING}if("sourceIndex"in f)return 0>f.sourceIndex||0>c.sourceIndex?CKEDITOR.POSITION_DISCONNECTED:f.sourceIndex=document.documentMode||!f||(a=f+":"+a);return new CKEDITOR.dom.nodeList(this.$.getElementsByTagName(a))},getHead:function(){var a=this.$.getElementsByTagName("head")[0];return a=a?new CKEDITOR.dom.element(a): +this.getDocumentElement().append(new CKEDITOR.dom.element("head"),!0)},getBody:function(){return new CKEDITOR.dom.element(this.$.body)},getDocumentElement:function(){return new CKEDITOR.dom.element(this.$.documentElement)},getWindow:function(){return new CKEDITOR.dom.window(this.$.parentWindow||this.$.defaultView)},write:function(a){this.$.open("text/html","replace");CKEDITOR.env.ie&&(a=a.replace(/(?:^\s*]*?>)|^/i,'$\x26\n\x3cscript data-cke-temp\x3d"1"\x3e('+CKEDITOR.tools.fixDomain+ +")();\x3c/script\x3e"));this.$.write(a);this.$.close()},find:function(a){return new CKEDITOR.dom.nodeList(this.$.querySelectorAll(a))},findOne:function(a){return(a=this.$.querySelector(a))?new CKEDITOR.dom.element(a):null},_getHtml5ShivFrag:function(){var a=this.getCustomData("html5ShivFrag");a||(a=this.$.createDocumentFragment(),CKEDITOR.tools.enableHtml5Elements(a,!0),this.setCustomData("html5ShivFrag",a));return a}}),CKEDITOR.dom.nodeList=function(a){this.$=a},CKEDITOR.dom.nodeList.prototype={count:function(){return this.$.length}, +getItem:function(a){return 0>a||a>=this.$.length?null:(a=this.$[a])?new CKEDITOR.dom.node(a):null}},CKEDITOR.dom.element=function(a,f){"string"==typeof a&&(a=(f?f.$:document).createElement(a));CKEDITOR.dom.domObject.call(this,a)},CKEDITOR.dom.element.get=function(a){return(a="string"==typeof a?document.getElementById(a)||document.getElementsByName(a)[0]:a)&&(a.$?a:new CKEDITOR.dom.element(a))},CKEDITOR.dom.element.prototype=new CKEDITOR.dom.node,CKEDITOR.dom.element.createFromHtml=function(a,f){var c= +new CKEDITOR.dom.element("div",f);c.setHtml(a);return c.getFirst().remove()},CKEDITOR.dom.element.setMarker=function(a,f,c,e){var h=f.getCustomData("list_marker_id")||f.setCustomData("list_marker_id",CKEDITOR.tools.getNextNumber()).getCustomData("list_marker_id"),m=f.getCustomData("list_marker_names")||f.setCustomData("list_marker_names",{}).getCustomData("list_marker_names");a[h]=f;m[c]=1;return f.setCustomData(c,e)},CKEDITOR.dom.element.clearAllMarkers=function(a){for(var f in a)CKEDITOR.dom.element.clearMarkers(a, +a[f],1)},CKEDITOR.dom.element.clearMarkers=function(a,f,c){var e=f.getCustomData("list_marker_names"),h=f.getCustomData("list_marker_id"),m;for(m in e)f.removeCustomData(m);f.removeCustomData("list_marker_names");c&&(f.removeCustomData("list_marker_id"),delete a[h])},function(){function a(a,b){return-1<(" "+a+" ").replace(m," ").indexOf(" "+b+" ")}function f(a){var b=!0;a.$.id||(a.$.id="cke_tmp_"+CKEDITOR.tools.getNextNumber(),b=!1);return function(){b||a.removeAttribute("id")}}function c(a,b){return"#"+ +a.$.id+" "+b.split(/,\s*/).join(", #"+a.$.id+" ")}function e(a){for(var b=0,d=0,g=l[a].length;dCKEDITOR.env.version?this.$.text+=a:this.append(new CKEDITOR.dom.text(a))},appendBogus:function(a){if(a||CKEDITOR.env.needsBrFiller){for(a=this.getLast();a&&a.type==CKEDITOR.NODE_TEXT&&!CKEDITOR.tools.rtrim(a.getText());)a=a.getPrevious();a&& +a.is&&a.is("br")||(a=this.getDocument().createElement("br"),CKEDITOR.env.gecko&&a.setAttribute("type","_moz"),this.append(a))}},breakParent:function(a,b){var d=new CKEDITOR.dom.range(this.getDocument());d.setStartAfter(this);d.setEndAfter(a);var g=d.extractContents(!1,b||!1);d.insertNode(this.remove());g.insertAfterNode(this)},contains:document.compareDocumentPosition?function(a){return!!(this.$.compareDocumentPosition(a.$)&16)}:function(a){var b=this.$;return a.type!=CKEDITOR.NODE_ELEMENT?b.contains(a.getParent().$): +b!=a.$&&b.contains(a.$)},focus:function(){function a(){try{this.$.focus()}catch(b){}}return function(b){b?CKEDITOR.tools.setTimeout(a,100,this):a.call(this)}}(),getHtml:function(){var a=this.$.innerHTML;return CKEDITOR.env.ie?a.replace(/<\?[^>]*>/g,""):a},getOuterHtml:function(){if(this.$.outerHTML)return this.$.outerHTML.replace(/<\?[^>]*>/,"");var a=this.$.ownerDocument.createElement("div");a.appendChild(this.$.cloneNode(!0));return a.innerHTML},getClientRect:function(){var a=CKEDITOR.tools.extend({}, +this.$.getBoundingClientRect());!a.width&&(a.width=a.right-a.left);!a.height&&(a.height=a.bottom-a.top);return a},setHtml:CKEDITOR.env.ie&&9>CKEDITOR.env.version?function(a){try{var b=this.$;if(this.getParent())return b.innerHTML=a;var d=this.getDocument()._getHtml5ShivFrag();d.appendChild(b);b.innerHTML=a;d.removeChild(b);return a}catch(g){this.$.innerHTML="";b=new CKEDITOR.dom.element("body",this.getDocument());b.$.innerHTML=a;for(b=b.getChildren();b.count();)this.append(b.getItem(0));return a}}: +function(a){return this.$.innerHTML=a},setText:function(){var a=document.createElement("p");a.innerHTML="x";a=a.textContent;return function(b){this.$[a?"textContent":"innerText"]=b}}(),getAttribute:function(){var a=function(a){return this.$.getAttribute(a,2)};return CKEDITOR.env.ie&&(CKEDITOR.env.ie7Compat||CKEDITOR.env.quirks)?function(a){switch(a){case "class":a="className";break;case "http-equiv":a="httpEquiv";break;case "name":return this.$.name;case "tabindex":return a=this.$.getAttribute(a, +2),0!==a&&0===this.$.tabIndex&&(a=null),a;case "checked":return a=this.$.attributes.getNamedItem(a),(a.specified?a.nodeValue:this.$.checked)?"checked":null;case "hspace":case "value":return this.$[a];case "style":return this.$.style.cssText;case "contenteditable":case "contentEditable":return this.$.attributes.getNamedItem("contentEditable").specified?this.$.getAttribute("contentEditable"):null}return this.$.getAttribute(a,2)}:a}(),getChildren:function(){return new CKEDITOR.dom.nodeList(this.$.childNodes)}, getComputedStyle:document.defaultView&&document.defaultView.getComputedStyle?function(a){var b=this.getWindow().$.getComputedStyle(this.$,null);return b?b.getPropertyValue(a):""}:function(a){return this.$.currentStyle[CKEDITOR.tools.cssStyleToDomStyle(a)]},getDtd:function(){var a=CKEDITOR.dtd[this.getName()];this.getDtd=function(){return a};return a},getElementsByTag:CKEDITOR.dom.document.prototype.getElementsByTag,getTabIndex:function(){var a=this.$.tabIndex;return 0!==a||CKEDITOR.dtd.$tabIndex[this.getName()]|| 0===parseInt(this.getAttribute("tabindex"),10)?a:-1},getText:function(){return this.$.textContent||this.$.innerText||""},getWindow:function(){return this.getDocument().getWindow()},getId:function(){return this.$.id||null},getNameAtt:function(){return this.$.name||null},getName:function(){var a=this.$.nodeName.toLowerCase();if(CKEDITOR.env.ie&&8>=document.documentMode){var b=this.$.scopeName;"HTML"!=b&&(a=b.toLowerCase()+":"+a)}this.getName=function(){return a};return this.getName()},getValue:function(){return this.$.value}, getFirst:function(a){var b=this.$.firstChild;(b=b&&new CKEDITOR.dom.node(b))&&a&&!a(b)&&(b=b.getNext(a));return b},getLast:function(a){var b=this.$.lastChild;(b=b&&new CKEDITOR.dom.node(b))&&a&&!a(b)&&(b=b.getPrevious(a));return b},getStyle:function(a){return this.$.style[CKEDITOR.tools.cssStyleToDomStyle(a)]},is:function(){var a=this.getName();if("object"==typeof arguments[0])return!!arguments[0][a];for(var b=0;bCKEDITOR.env.version&&this.is("a")){var d=this.getParent();d.type==CKEDITOR.NODE_ELEMENT&&(d=d.clone(),d.setHtml(b),b=d.getHtml(),d.setHtml(a),a=d.getHtml())}return b== -a},isVisible:function(){var a=(this.$.offsetHeight||this.$.offsetWidth)&&"hidden"!=this.getComputedStyle("visibility"),b,d;a&&CKEDITOR.env.webkit&&(b=this.getWindow(),!b.equals(CKEDITOR.document.getWindow())&&(d=b.$.frameElement)&&(a=(new CKEDITOR.dom.element(d)).isVisible()));return!!a},isEmptyInlineRemoveable:function(){if(!CKEDITOR.dtd.$removeEmpty[this.getName()])return!1;for(var a=this.getChildren(),b=0,d=a.count();bCKEDITOR.env.version?function(b){return"name"==b?!!this.$.name:a.call(this,b)}:a:function(a){return!!this.$.attributes.getNamedItem(a)}}(), -hide:function(){this.setStyle("display","none")},moveChildren:function(a,b){var d=this.$;a=a.$;if(d!=a){var h;if(b)for(;h=d.lastChild;)a.insertBefore(d.removeChild(h),a.firstChild);else for(;h=d.firstChild;)a.appendChild(d.removeChild(h))}},mergeSiblings:function(){function a(b,d,h){if(d&&d.type==CKEDITOR.NODE_ELEMENT){for(var c=[];d.data("cke-bookmark")||d.isEmptyInlineRemoveable();)if(c.push(d),d=h?d.getNext():d.getPrevious(),!d||d.type!=CKEDITOR.NODE_ELEMENT)return;if(b.isIdentical(d)){for(var k= -h?b.getLast():b.getFirst();c.length;)c.shift().move(b,!h);d.moveChildren(b,!h);d.remove();k&&k.type==CKEDITOR.NODE_ELEMENT&&k.mergeSiblings()}}}return function(b){if(!1===b||CKEDITOR.dtd.$removeEmpty[this.getName()]||this.is("a"))a(this,this.getNext(),!0),a(this,this.getPrevious())}}(),show:function(){this.setStyles({display:"",visibility:""})},setAttribute:function(){var a=function(a,d){this.$.setAttribute(a,d);return this};return CKEDITOR.env.ie&&(CKEDITOR.env.ie7Compat||CKEDITOR.env.quirks)?function(b, -d){"class"==b?this.$.className=d:"style"==b?this.$.style.cssText=d:"tabindex"==b?this.$.tabIndex=d:"checked"==b?this.$.checked=d:"contenteditable"==b?a.call(this,"contentEditable",d):a.apply(this,arguments);return this}:CKEDITOR.env.ie8Compat&&CKEDITOR.env.secure?function(b,d){if("src"==b&&d.match(/^http:\/\//))try{a.apply(this,arguments)}catch(h){}else a.apply(this,arguments);return this}:a}(),setAttributes:function(a){for(var b in a)this.setAttribute(b,a[b]);return this},setValue:function(a){this.$.value= +hide:function(){this.setStyle("display","none")},moveChildren:function(a,b){var d=this.$;a=a.$;if(d!=a){var g;if(b)for(;g=d.lastChild;)a.insertBefore(d.removeChild(g),a.firstChild);else for(;g=d.firstChild;)a.appendChild(d.removeChild(g))}},mergeSiblings:function(){function a(b,d,g){if(d&&d.type==CKEDITOR.NODE_ELEMENT){for(var c=[];d.data("cke-bookmark")||d.isEmptyInlineRemoveable();)if(c.push(d),d=g?d.getNext():d.getPrevious(),!d||d.type!=CKEDITOR.NODE_ELEMENT)return;if(b.isIdentical(d)){for(var e= +g?b.getLast():b.getFirst();c.length;)c.shift().move(b,!g);d.moveChildren(b,!g);d.remove();e&&e.type==CKEDITOR.NODE_ELEMENT&&e.mergeSiblings()}}}return function(b){if(!1===b||CKEDITOR.dtd.$removeEmpty[this.getName()]||this.is("a"))a(this,this.getNext(),!0),a(this,this.getPrevious())}}(),show:function(){this.setStyles({display:"",visibility:""})},setAttribute:function(){var a=function(a,d){this.$.setAttribute(a,d);return this};return CKEDITOR.env.ie&&(CKEDITOR.env.ie7Compat||CKEDITOR.env.quirks)?function(b, +d){"class"==b?this.$.className=d:"style"==b?this.$.style.cssText=d:"tabindex"==b?this.$.tabIndex=d:"checked"==b?this.$.checked=d:"contenteditable"==b?a.call(this,"contentEditable",d):a.apply(this,arguments);return this}:CKEDITOR.env.ie8Compat&&CKEDITOR.env.secure?function(b,d){if("src"==b&&d.match(/^http:\/\//))try{a.apply(this,arguments)}catch(g){}else a.apply(this,arguments);return this}:a}(),setAttributes:function(a){for(var b in a)this.setAttribute(b,a[b]);return this},setValue:function(a){this.$.value= a;return this},removeAttribute:function(){var a=function(a){this.$.removeAttribute(a)};return CKEDITOR.env.ie&&(CKEDITOR.env.ie7Compat||CKEDITOR.env.quirks)?function(a){"class"==a?a="className":"tabindex"==a?a="tabIndex":"contenteditable"==a&&(a="contentEditable");this.$.removeAttribute(a)}:a}(),removeAttributes:function(a){if(CKEDITOR.tools.isArray(a))for(var b=0;bCKEDITOR.env.version?(a=Math.round(100*a),this.setStyle("filter",100<=a?"":"progid:DXImageTransform.Microsoft.Alpha(opacity\x3d"+a+")")):this.setStyle("opacity",a)},unselectable:function(){this.setStyles(CKEDITOR.tools.cssVendorPrefix("user-select","none"));if(CKEDITOR.env.ie){this.setAttribute("unselectable","on");for(var a, -b=this.getElementsByTag("*"),d=0,h=b.count();dg||0g?g:c);d&&(0>f||0f?f:h,0)},setState:function(a,b,d){b=b||"cke";switch(a){case CKEDITOR.TRISTATE_ON:this.addClass(b+"_on");this.removeClass(b+"_off");this.removeClass(b+"_disabled");d&&this.setAttribute("aria-pressed",!0);d&&this.removeAttribute("aria-disabled");break;case CKEDITOR.TRISTATE_DISABLED:this.addClass(b+ +b=this.getElementsByTag("*"),d=0,g=b.count();dh||0h?h:c);d&&(0>e||0e?e:g,0)},setState:function(a,b,d){b=b||"cke";switch(a){case CKEDITOR.TRISTATE_ON:this.addClass(b+"_on");this.removeClass(b+"_off");this.removeClass(b+"_disabled");d&&this.setAttribute("aria-pressed",!0);d&&this.removeAttribute("aria-disabled");break;case CKEDITOR.TRISTATE_DISABLED:this.addClass(b+ "_disabled");this.removeClass(b+"_off");this.removeClass(b+"_on");d&&this.setAttribute("aria-disabled",!0);d&&this.removeAttribute("aria-pressed");break;default:this.addClass(b+"_off"),this.removeClass(b+"_on"),this.removeClass(b+"_disabled"),d&&this.removeAttribute("aria-pressed"),d&&this.removeAttribute("aria-disabled")}},getFrameDocument:function(){var a=this.$;try{a.contentWindow.document}catch(b){a.src=a.src}return a&&new CKEDITOR.dom.document(a.contentWindow.document)},copyAttributes:function(a, -b){var d=this.$.attributes;b=b||{};for(var h=0;h=B.getChildCount()?(B=B.getChild(z-1),G=!0):B=B.getChild(z):J=G=!0;x.type==CKEDITOR.NODE_TEXT?r?I=!0:x.split(A):0R)for(;V;)V=g(V,O,!0);O=U}r||e()}}function c(){var a=!1,b=CKEDITOR.dom.walker.whitespaces(),d=CKEDITOR.dom.walker.bookmark(!0), -c=CKEDITOR.dom.walker.bogus();return function(f){return d(f)||b(f)?!0:c(f)&&!a?a=!0:f.type==CKEDITOR.NODE_TEXT&&(f.hasAscendant("pre")||CKEDITOR.tools.trim(f.getText()).length)||f.type==CKEDITOR.NODE_ELEMENT&&!f.is(m)?!1:!0}}function g(a){var b=CKEDITOR.dom.walker.whitespaces(),d=CKEDITOR.dom.walker.bookmark(1);return function(c){return d(c)||b(c)?!0:!a&&l(c)||c.type==CKEDITOR.NODE_ELEMENT&&c.is(CKEDITOR.dtd.$removeEmpty)}}function f(a){return function(){var c;return this[a?"getPreviousNode":"getNextNode"](function(a){!c&& +getEditor:function(){var a=CKEDITOR.instances,b,d;for(b in a)if(d=a[b],d.element.equals(this)&&d.elementMode!=CKEDITOR.ELEMENT_MODE_APPENDTO)return d;return null},find:function(a){var b=f(this);a=new CKEDITOR.dom.nodeList(this.$.querySelectorAll(c(this,a)));b();return a},findOne:function(a){var b=f(this);a=this.$.querySelector(c(this,a));b();return a?new CKEDITOR.dom.element(a):null},forEach:function(a,b,d){if(!(d||b&&this.type!=b))var g=a(this);if(!1!==g){d=this.getChildren();for(var c=0;c=D.getChildCount()?(D=D.getChild(z-1),G=!0):D=D.getChild(z):J=G=!0;x.type==CKEDITOR.NODE_TEXT?t?I=!0:x.split(y):0S)for(;V;)V=h(V,O,!0);O=U}t||f()}}function c(){var a=!1,b=CKEDITOR.dom.walker.whitespaces(),d=CKEDITOR.dom.walker.bookmark(!0), +c=CKEDITOR.dom.walker.bogus();return function(e){return d(e)||b(e)?!0:c(e)&&!a?a=!0:e.type==CKEDITOR.NODE_TEXT&&(e.hasAscendant("pre")||CKEDITOR.tools.trim(e.getText()).length)||e.type==CKEDITOR.NODE_ELEMENT&&!e.is(m)?!1:!0}}function e(a){var b=CKEDITOR.dom.walker.whitespaces(),d=CKEDITOR.dom.walker.bookmark(1);return function(c){return d(c)||b(c)?!0:!a&&l(c)||c.type==CKEDITOR.NODE_ELEMENT&&c.is(CKEDITOR.dtd.$removeEmpty)}}function h(a){return function(){var c;return this[a?"getPreviousNode":"getNextNode"](function(a){!c&& d(a)&&(c=a);return b(a)&&!(l(a)&&a.equals(c))})}}var m={abbr:1,acronym:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,"var":1},l=CKEDITOR.dom.walker.bogus(),k=/^[\t\r\n ]*(?: |\xa0)$/,b=CKEDITOR.dom.walker.editable(),d=CKEDITOR.dom.walker.ignored(!0);CKEDITOR.dom.range.prototype={clone:function(){var a=new CKEDITOR.dom.range(this.root);a._setStartContainer(this.startContainer);a.startOffset=this.startOffset; -a._setEndContainer(this.endContainer);a.endOffset=this.endOffset;a.collapsed=this.collapsed;return a},collapse:function(a){a?(this._setEndContainer(this.startContainer),this.endOffset=this.startOffset):(this._setStartContainer(this.endContainer),this.startOffset=this.endOffset);this.collapsed=!0},cloneContents:function(a){var b=new CKEDITOR.dom.documentFragment(this.document);this.collapsed||e(this,2,b,!1,"undefined"==typeof a?!0:a);return b},deleteContents:function(a){this.collapsed||e(this,0,null, -a)},extractContents:function(a,b){var d=new CKEDITOR.dom.documentFragment(this.document);this.collapsed||e(this,1,d,a,"undefined"==typeof b?!0:b);return d},createBookmark:function(a){var b,d,c,f,g=this.collapsed;b=this.document.createElement("span");b.data("cke-bookmark",1);b.setStyle("display","none");b.setHtml("\x26nbsp;");a&&(c="cke_bm_"+CKEDITOR.tools.getNextNumber(),b.setAttribute("id",c+(g?"C":"S")));g||(d=b.clone(),d.setHtml("\x26nbsp;"),a&&d.setAttribute("id",c+"E"),f=this.clone(),f.collapse(), -f.insertNode(d));f=this.clone();f.collapse(!0);f.insertNode(b);d?(this.setStartAfter(b),this.setEndBefore(d)):this.moveToPosition(b,CKEDITOR.POSITION_AFTER_END);return{startNode:a?c+(g?"C":"S"):b,endNode:a?c+"E":d,serializable:a,collapsed:g}},createBookmark2:function(){function a(h){var d=h.container,c=h.offset,f;f=d;var g=c;f=f.type!=CKEDITOR.NODE_ELEMENT||0===g||g==f.getChildCount()?0:f.getChild(g-1).type==CKEDITOR.NODE_TEXT&&f.getChild(g).type==CKEDITOR.NODE_TEXT;f&&(d=d.getChild(c-1),c=d.getLength()); -d.type==CKEDITOR.NODE_ELEMENT&&1d)a=a.getChild(d);else if(1>f)a=a.getPreviousSourceNode();else{for(a=a.$;a.lastChild;)a=a.lastChild;a=new CKEDITOR.dom.node(a);a=a.getNextSourceNode()||a}if(b.type==CKEDITOR.NODE_ELEMENT)if(f=b.getChildCount(), -f>c)b=b.getChild(c).getPreviousSourceNode(!0);else if(1>f)b=b.getPreviousSourceNode();else{for(b=b.$;b.lastChild;)b=b.lastChild;b=new CKEDITOR.dom.node(b)}a.getPosition(b)&CKEDITOR.POSITION_FOLLOWING&&(a=b);return{startNode:a,endNode:b}},getCommonAncestor:function(a,b){var d=this.startContainer,c=this.endContainer,d=d.equals(c)?a&&d.type==CKEDITOR.NODE_ELEMENT&&this.startOffset==this.endOffset-1?d.getChild(this.startOffset):d:d.getCommonAncestor(c);return b&&!d.is?d.getParent():d},optimize:function(){var a= -this.startContainer,b=this.startOffset;a.type!=CKEDITOR.NODE_ELEMENT&&(b?b>=a.getLength()&&this.setStartAfter(a):this.setStartBefore(a));a=this.endContainer;b=this.endOffset;a.type!=CKEDITOR.NODE_ELEMENT&&(b?b>=a.getLength()&&this.setEndAfter(a):this.setEndBefore(a))},optimizeBookmark:function(){var a=this.startContainer,b=this.endContainer;a.is&&a.is("span")&&a.data("cke-bookmark")&&this.setStartAt(a,CKEDITOR.POSITION_BEFORE_START);b&&b.is&&b.is("span")&&b.data("cke-bookmark")&&this.setEndAt(b,CKEDITOR.POSITION_AFTER_END)}, -trim:function(a,b){var d=this.startContainer,c=this.startOffset,f=this.collapsed;if((!a||f)&&d&&d.type==CKEDITOR.NODE_TEXT){if(c)if(c>=d.getLength())c=d.getIndex()+1,d=d.getParent();else{var g=d.split(c),c=d.getIndex()+1,d=d.getParent();this.startContainer.equals(this.endContainer)?this.setEnd(g,this.endOffset-this.startOffset):d.equals(this.endContainer)&&(this.endOffset+=1)}else c=d.getIndex(),d=d.getParent();this.setStart(d,c);if(f){this.collapse(!0);return}}d=this.endContainer;c=this.endOffset; -b||f||!d||d.type!=CKEDITOR.NODE_TEXT||(c?(c>=d.getLength()||d.split(c),c=d.getIndex()+1):c=d.getIndex(),d=d.getParent(),this.setEnd(d,c))},enlarge:function(a,b){function d(a){return a&&a.type==CKEDITOR.NODE_ELEMENT&&a.hasAttribute("contenteditable")?null:a}var c=new RegExp(/[^\s\ufeff]/);switch(a){case CKEDITOR.ENLARGE_INLINE:var f=1;case CKEDITOR.ENLARGE_ELEMENT:var g=function(a,b){var d=new CKEDITOR.dom.range(e);d.setStart(a,b);d.setEndAt(e,CKEDITOR.POSITION_BEFORE_END);var d=new CKEDITOR.dom.walker(d), -h;for(d.guard=function(a){return!(a.type==CKEDITOR.NODE_ELEMENT&&a.isBlockBoundary())};h=d.next();){if(h.type!=CKEDITOR.NODE_TEXT)return!1;D=h!=a?h.getText():h.substring(b);if(c.test(D))return!1}return!0};if(this.collapsed)break;var k=this.getCommonAncestor(),e=this.root,m,l,r,x,B,A=!1,z,D;z=this.startContainer;var G=this.startOffset;z.type==CKEDITOR.NODE_TEXT?(G&&(z=!CKEDITOR.tools.trim(z.substring(0,G)).length&&z,A=!!z),z&&((x=z.getPrevious())||(r=z.getParent()))):(G&&(x=z.getChild(G-1)||z.getLast()), -x||(r=z));for(r=d(r);r||x;){if(r&&!x){!B&&r.equals(k)&&(B=!0);if(f?r.isBlockBoundary():!e.contains(r))break;A&&"inline"==r.getComputedStyle("display")||(A=!1,B?m=r:this.setStartBefore(r));x=r.getPrevious()}for(;x;)if(z=!1,x.type==CKEDITOR.NODE_COMMENT)x=x.getPrevious();else{if(x.type==CKEDITOR.NODE_TEXT)D=x.getText(),c.test(D)&&(x=null),z=/[\s\ufeff]$/.test(D);else if((x.$.offsetWidth>(CKEDITOR.env.webkit?1:0)||b&&x.is("br"))&&!x.data("cke-bookmark"))if(A&&CKEDITOR.dtd.$removeEmpty[x.getName()]){D= -x.getText();if(c.test(D))x=null;else for(var G=x.$.getElementsByTagName("*"),F=0,J;J=G[F++];)if(!CKEDITOR.dtd.$removeEmpty[J.nodeName.toLowerCase()]){x=null;break}x&&(z=!!D.length)}else x=null;z&&(A?B?m=r:r&&this.setStartBefore(r):A=!0);if(x){z=x.getPrevious();if(!r&&!z){r=x;x=null;break}x=z}else r=null}r&&(r=d(r.getParent()))}z=this.endContainer;G=this.endOffset;r=x=null;B=A=!1;z.type==CKEDITOR.NODE_TEXT?CKEDITOR.tools.trim(z.substring(G)).length?A=!0:(A=!z.getLength(),G==z.getLength()?(x=z.getNext())|| -(r=z.getParent()):g(z,G)&&(r=z.getParent())):(x=z.getChild(G))||(r=z);for(;r||x;){if(r&&!x){!B&&r.equals(k)&&(B=!0);if(f?r.isBlockBoundary():!e.contains(r))break;A&&"inline"==r.getComputedStyle("display")||(A=!1,B?l=r:r&&this.setEndAfter(r));x=r.getNext()}for(;x;){z=!1;if(x.type==CKEDITOR.NODE_TEXT)D=x.getText(),g(x,0)||(x=null),z=/^[\s\ufeff]/.test(D);else if(x.type==CKEDITOR.NODE_ELEMENT){if((0=f.getLength()?c.setStartAfter(f):(c.setStartBefore(f),m= -0):c.setStartBefore(f));g&&g.type==CKEDITOR.NODE_TEXT&&(e?e>=g.getLength()?c.setEndAfter(g):(c.setEndAfter(g),l=0):c.setEndBefore(g));var c=new CKEDITOR.dom.walker(c),r=CKEDITOR.dom.walker.bookmark();c.evaluator=function(b){return b.type==(a==CKEDITOR.SHRINK_ELEMENT?CKEDITOR.NODE_ELEMENT:CKEDITOR.NODE_TEXT)};var x;c.guard=function(b,c){if(r(b))return!0;if(a==CKEDITOR.SHRINK_ELEMENT&&b.type==CKEDITOR.NODE_TEXT||c&&b.equals(x)||!1===d&&b.type==CKEDITOR.NODE_ELEMENT&&b.isBlockBoundary()||b.type==CKEDITOR.NODE_ELEMENT&& -b.hasAttribute("contenteditable"))return!1;c||b.type!=CKEDITOR.NODE_ELEMENT||(x=b);return!0};m&&(f=c[a==CKEDITOR.SHRINK_ELEMENT?"lastForward":"next"]())&&this.setStartAt(f,b?CKEDITOR.POSITION_AFTER_START:CKEDITOR.POSITION_BEFORE_START);l&&(c.reset(),(c=c[a==CKEDITOR.SHRINK_ELEMENT?"lastBackward":"previous"]())&&this.setEndAt(c,b?CKEDITOR.POSITION_BEFORE_END:CKEDITOR.POSITION_AFTER_END));return!(!m&&!l)}},insertNode:function(a){this.optimizeBookmark();this.trim(!1,!0);var b=this.startContainer,d=b.getChild(this.startOffset); -d?a.insertBefore(d):b.append(a);a.getParent()&&a.getParent().equals(this.endContainer)&&this.endOffset++;this.setStartBefore(a)},moveToPosition:function(a,b){this.setStartAt(a,b);this.collapse(!0)},moveToRange:function(a){this.setStart(a.startContainer,a.startOffset);this.setEnd(a.endContainer,a.endOffset)},selectNodeContents:function(a){this.setStart(a,0);this.setEnd(a,a.type==CKEDITOR.NODE_TEXT?a.getLength():a.getChildCount())},setStart:function(b,d){b.type==CKEDITOR.NODE_ELEMENT&&CKEDITOR.dtd.$empty[b.getName()]&& -(d=b.getIndex(),b=b.getParent());this._setStartContainer(b);this.startOffset=d;this.endContainer||(this._setEndContainer(b),this.endOffset=d);a(this)},setEnd:function(b,d){b.type==CKEDITOR.NODE_ELEMENT&&CKEDITOR.dtd.$empty[b.getName()]&&(d=b.getIndex()+1,b=b.getParent());this._setEndContainer(b);this.endOffset=d;this.startContainer||(this._setStartContainer(b),this.startOffset=d);a(this)},setStartAfter:function(a){this.setStart(a.getParent(),a.getIndex()+1)},setStartBefore:function(a){this.setStart(a.getParent(), -a.getIndex())},setEndAfter:function(a){this.setEnd(a.getParent(),a.getIndex()+1)},setEndBefore:function(a){this.setEnd(a.getParent(),a.getIndex())},setStartAt:function(b,d){switch(d){case CKEDITOR.POSITION_AFTER_START:this.setStart(b,0);break;case CKEDITOR.POSITION_BEFORE_END:b.type==CKEDITOR.NODE_TEXT?this.setStart(b,b.getLength()):this.setStart(b,b.getChildCount());break;case CKEDITOR.POSITION_BEFORE_START:this.setStartBefore(b);break;case CKEDITOR.POSITION_AFTER_END:this.setStartAfter(b)}a(this)}, -setEndAt:function(b,d){switch(d){case CKEDITOR.POSITION_AFTER_START:this.setEnd(b,0);break;case CKEDITOR.POSITION_BEFORE_END:b.type==CKEDITOR.NODE_TEXT?this.setEnd(b,b.getLength()):this.setEnd(b,b.getChildCount());break;case CKEDITOR.POSITION_BEFORE_START:this.setEndBefore(b);break;case CKEDITOR.POSITION_AFTER_END:this.setEndAfter(b)}a(this)},fixBlock:function(a,b){var d=this.createBookmark(),c=this.document.createElement(b);this.collapse(a);this.enlarge(CKEDITOR.ENLARGE_BLOCK_CONTENTS);this.extractContents().appendTo(c); -c.trim();this.insertNode(c);var f=c.getBogus();f&&f.remove();c.appendBogus();this.moveToBookmark(d);return c},splitBlock:function(a,b){var d=new CKEDITOR.dom.elementPath(this.startContainer,this.root),c=new CKEDITOR.dom.elementPath(this.endContainer,this.root),f=d.block,g=c.block,k=null;if(!d.blockLimit.equals(c.blockLimit))return null;"br"!=a&&(f||(f=this.fixBlock(!0,a),g=(new CKEDITOR.dom.elementPath(this.endContainer,this.root)).block),g||(g=this.fixBlock(!1,a)));d=f&&this.checkStartOfBlock(); -c=g&&this.checkEndOfBlock();this.deleteContents();f&&f.equals(g)&&(c?(k=new CKEDITOR.dom.elementPath(this.startContainer,this.root),this.moveToPosition(g,CKEDITOR.POSITION_AFTER_END),g=null):d?(k=new CKEDITOR.dom.elementPath(this.startContainer,this.root),this.moveToPosition(f,CKEDITOR.POSITION_BEFORE_START),f=null):(g=this.splitElement(f,b||!1),f.is("ul","ol")||f.appendBogus()));return{previousBlock:f,nextBlock:g,wasStartOfBlock:d,wasEndOfBlock:c,elementPath:k}},splitElement:function(a,b){if(!this.collapsed)return null; -this.setEndAt(a,CKEDITOR.POSITION_BEFORE_END);var d=this.extractContents(!1,b||!1),c=a.clone(!1,b||!1);d.appendTo(c);c.insertAfter(a);this.moveToPosition(a,CKEDITOR.POSITION_AFTER_END);return c},removeEmptyBlocksAtEnd:function(){function a(h){return function(a){return b(a)||d(a)||a.type==CKEDITOR.NODE_ELEMENT&&a.isEmptyInlineRemoveable()||h.is("table")&&a.is("caption")?!1:!0}}var b=CKEDITOR.dom.walker.whitespaces(),d=CKEDITOR.dom.walker.bookmark(!1);return function(b){for(var d=this.createBookmark(), -c=this[b?"endPath":"startPath"](),f=c.block||c.blockLimit,g;f&&!f.equals(c.root)&&!f.getFirst(a(f));)g=f.getParent(),this[b?"setEndAt":"setStartAt"](f,CKEDITOR.POSITION_AFTER_END),f.remove(1),f=g;this.moveToBookmark(d)}}(),startPath:function(){return new CKEDITOR.dom.elementPath(this.startContainer,this.root)},endPath:function(){return new CKEDITOR.dom.elementPath(this.endContainer,this.root)},checkBoundaryOfElement:function(a,b){var d=b==CKEDITOR.START,c=this.clone();c.collapse(d);c[d?"setStartAt": -"setEndAt"](a,d?CKEDITOR.POSITION_AFTER_START:CKEDITOR.POSITION_BEFORE_END);c=new CKEDITOR.dom.walker(c);c.evaluator=g(d);return c[d?"checkBackward":"checkForward"]()},checkStartOfBlock:function(){var a=this.startContainer,b=this.startOffset;CKEDITOR.env.ie&&b&&a.type==CKEDITOR.NODE_TEXT&&(a=CKEDITOR.tools.ltrim(a.substring(0,b)),k.test(a)&&this.trim(0,1));this.trim();a=new CKEDITOR.dom.elementPath(this.startContainer,this.root);b=this.clone();b.collapse(!0);b.setStartAt(a.block||a.blockLimit,CKEDITOR.POSITION_AFTER_START); -a=new CKEDITOR.dom.walker(b);a.evaluator=c();return a.checkBackward()},checkEndOfBlock:function(){var a=this.endContainer,b=this.endOffset;CKEDITOR.env.ie&&a.type==CKEDITOR.NODE_TEXT&&(a=CKEDITOR.tools.rtrim(a.substring(b)),k.test(a)&&this.trim(1,0));this.trim();a=new CKEDITOR.dom.elementPath(this.endContainer,this.root);b=this.clone();b.collapse(!1);b.setEndAt(a.block||a.blockLimit,CKEDITOR.POSITION_BEFORE_END);a=new CKEDITOR.dom.walker(b);a.evaluator=c();return a.checkForward()},getPreviousNode:function(a, -b,d){var c=this.clone();c.collapse(1);c.setStartAt(d||this.root,CKEDITOR.POSITION_AFTER_START);d=new CKEDITOR.dom.walker(c);d.evaluator=a;d.guard=b;return d.previous()},getNextNode:function(a,b,d){var c=this.clone();c.collapse();c.setEndAt(d||this.root,CKEDITOR.POSITION_BEFORE_END);d=new CKEDITOR.dom.walker(c);d.evaluator=a;d.guard=b;return d.next()},checkReadOnly:function(){function a(b,d){for(;b;){if(b.type==CKEDITOR.NODE_ELEMENT){if("false"==b.getAttribute("contentEditable")&&!b.data("cke-editable"))return 0; -if(b.is("html")||"true"==b.getAttribute("contentEditable")&&(b.contains(d)||b.equals(d)))break}b=b.getParent()}return 1}return function(){var b=this.startContainer,d=this.endContainer;return!(a(b,d)&&a(d,b))}}(),moveToElementEditablePosition:function(a,b){if(a.type==CKEDITOR.NODE_ELEMENT&&!a.isEditable(!1))return this.moveToPosition(a,b?CKEDITOR.POSITION_AFTER_END:CKEDITOR.POSITION_BEFORE_START),!0;for(var c=0;a;){if(a.type==CKEDITOR.NODE_TEXT){b&&this.endContainer&&this.checkEndOfBlock()&&k.test(a.getText())? -this.moveToPosition(a,CKEDITOR.POSITION_BEFORE_START):this.moveToPosition(a,b?CKEDITOR.POSITION_AFTER_END:CKEDITOR.POSITION_BEFORE_START);c=1;break}if(a.type==CKEDITOR.NODE_ELEMENT)if(a.isEditable())this.moveToPosition(a,b?CKEDITOR.POSITION_BEFORE_END:CKEDITOR.POSITION_AFTER_START),c=1;else if(b&&a.is("br")&&this.endContainer&&this.checkEndOfBlock())this.moveToPosition(a,CKEDITOR.POSITION_BEFORE_START);else if("false"==a.getAttribute("contenteditable")&&a.is(CKEDITOR.dtd.$block))return this.setStartBefore(a), -this.setEndAfter(a),!0;var f=a,g=c,n=void 0;f.type==CKEDITOR.NODE_ELEMENT&&f.isEditable(!1)&&(n=f[b?"getLast":"getFirst"](d));g||n||(n=f[b?"getPrevious":"getNext"](d));a=n}return!!c},moveToClosestEditablePosition:function(a,b){var d,c=0,f,g,k=[CKEDITOR.POSITION_AFTER_END,CKEDITOR.POSITION_BEFORE_START];a?(d=new CKEDITOR.dom.range(this.root),d.moveToPosition(a,k[b?0:1])):d=this.clone();if(a&&!a.is(CKEDITOR.dtd.$block))c=1;else if(f=d[b?"getNextEditableNode":"getPreviousEditableNode"]())c=1,(g=f.type== -CKEDITOR.NODE_ELEMENT)&&f.is(CKEDITOR.dtd.$block)&&"false"==f.getAttribute("contenteditable")?(d.setStartAt(f,CKEDITOR.POSITION_BEFORE_START),d.setEndAt(f,CKEDITOR.POSITION_AFTER_END)):!CKEDITOR.env.needsBrFiller&&g&&f.is(CKEDITOR.dom.walker.validEmptyBlockContainers)?(d.setEnd(f,0),d.collapse()):d.moveToPosition(f,k[b?1:0]);c&&this.moveToRange(d);return!!c},moveToElementEditStart:function(a){return this.moveToElementEditablePosition(a)},moveToElementEditEnd:function(a){return this.moveToElementEditablePosition(a, -!0)},getEnclosedNode:function(){var a=this.clone();a.optimize();if(a.startContainer.type!=CKEDITOR.NODE_ELEMENT||a.endContainer.type!=CKEDITOR.NODE_ELEMENT)return null;var a=new CKEDITOR.dom.walker(a),b=CKEDITOR.dom.walker.bookmark(!1,!0),d=CKEDITOR.dom.walker.whitespaces(!0);a.evaluator=function(a){return d(a)&&b(a)};var c=a.next();a.reset();return c&&c.equals(a.previous())?c:null},getTouchedStartNode:function(){var a=this.startContainer;return this.collapsed||a.type!=CKEDITOR.NODE_ELEMENT?a:a.getChild(this.startOffset)|| -a},getTouchedEndNode:function(){var a=this.endContainer;return this.collapsed||a.type!=CKEDITOR.NODE_ELEMENT?a:a.getChild(this.endOffset-1)||a},getNextEditableNode:f(),getPreviousEditableNode:f(1),scrollIntoView:function(){var a=new CKEDITOR.dom.element.createFromHtml("\x3cspan\x3e\x26nbsp;\x3c/span\x3e",this.document),b,d,c,f=this.clone();f.optimize();(c=f.startContainer.type==CKEDITOR.NODE_TEXT)?(d=f.startContainer.getText(),b=f.startContainer.split(f.startOffset),a.insertAfter(f.startContainer)): -f.insertNode(a);a.scrollIntoView();c&&(f.startContainer.setText(d),b.remove());a.remove()},_setStartContainer:function(a){this.startContainer=a},_setEndContainer:function(a){this.endContainer=a}}}(),CKEDITOR.POSITION_AFTER_START=1,CKEDITOR.POSITION_BEFORE_END=2,CKEDITOR.POSITION_BEFORE_START=3,CKEDITOR.POSITION_AFTER_END=4,CKEDITOR.ENLARGE_ELEMENT=1,CKEDITOR.ENLARGE_BLOCK_CONTENTS=2,CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS=3,CKEDITOR.ENLARGE_INLINE=4,CKEDITOR.START=1,CKEDITOR.END=2,CKEDITOR.SHRINK_ELEMENT= -1,CKEDITOR.SHRINK_TEXT=2,"use strict",function(){function a(a){1>arguments.length||(this.range=a,this.forceBrBreak=0,this.enlargeBr=1,this.enforceRealBlocks=0,this._||(this._={}))}function e(a){var b=[];a.forEach(function(a){if("true"==a.getAttribute("contenteditable"))return b.push(a),!1},CKEDITOR.NODE_ELEMENT,!0);return b}function c(a,b,f,g){a:{null==g&&(g=e(f));for(var k;k=g.shift();)if(k.getDtd().p){g={element:k,remaining:g};break a}g=null}if(!g)return 0;if((k=CKEDITOR.filter.instances[g.element.data("cke-filter")])&& -!k.check(b))return c(a,b,f,g.remaining);b=new CKEDITOR.dom.range(g.element);b.selectNodeContents(g.element);b=b.createIterator();b.enlargeBr=a.enlargeBr;b.enforceRealBlocks=a.enforceRealBlocks;b.activeFilter=b.filter=k;a._.nestedEditable={element:g.element,container:f,remaining:g.remaining,iterator:b};return 1}function g(a,b,c){if(!b)return!1;a=a.clone();a.collapse(!c);return a.checkBoundaryOfElement(b,c?CKEDITOR.START:CKEDITOR.END)}var f=/^[\r\n\t ]+$/,m=CKEDITOR.dom.walker.bookmark(!1,!0),l=CKEDITOR.dom.walker.whitespaces(!0), -k=function(a){return m(a)&&l(a)},b={dd:1,dt:1,li:1};a.prototype={getNextParagraph:function(a){var h,e,l,u,t;a=a||"p";if(this._.nestedEditable){if(h=this._.nestedEditable.iterator.getNextParagraph(a))return this.activeFilter=this._.nestedEditable.iterator.activeFilter,h;this.activeFilter=this.filter;if(c(this,a,this._.nestedEditable.container,this._.nestedEditable.remaining))return this.activeFilter=this._.nestedEditable.iterator.activeFilter,this._.nestedEditable.iterator.getNextParagraph(a);this._.nestedEditable= -null}if(!this.range.root.getDtd()[a])return null;if(!this._.started){var n=this.range.clone();e=n.startPath();var q=n.endPath(),w=!n.collapsed&&g(n,e.block),y=!n.collapsed&&g(n,q.block,1);n.shrink(CKEDITOR.SHRINK_ELEMENT,!0);w&&n.setStartAt(e.block,CKEDITOR.POSITION_BEFORE_END);y&&n.setEndAt(q.block,CKEDITOR.POSITION_AFTER_START);e=n.endContainer.hasAscendant("pre",!0)||n.startContainer.hasAscendant("pre",!0);n.enlarge(this.forceBrBreak&&!e||!this.enlargeBr?CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS:CKEDITOR.ENLARGE_BLOCK_CONTENTS); -n.collapsed||(e=new CKEDITOR.dom.walker(n.clone()),q=CKEDITOR.dom.walker.bookmark(!0,!0),e.evaluator=q,this._.nextNode=e.next(),e=new CKEDITOR.dom.walker(n.clone()),e.evaluator=q,e=e.previous(),this._.lastNode=e.getNextSourceNode(!0,null,n.root),this._.lastNode&&this._.lastNode.type==CKEDITOR.NODE_TEXT&&!CKEDITOR.tools.trim(this._.lastNode.getText())&&this._.lastNode.getParent().isBlockBoundary()&&(q=this.range.clone(),q.moveToPosition(this._.lastNode,CKEDITOR.POSITION_AFTER_END),q.checkEndOfBlock()&& -(q=new CKEDITOR.dom.elementPath(q.endContainer,q.root),this._.lastNode=(q.block||q.blockLimit).getNextSourceNode(!0))),this._.lastNode&&n.root.contains(this._.lastNode)||(this._.lastNode=this._.docEndMarker=n.document.createText(""),this._.lastNode.insertAfter(e)),n=null);this._.started=1;e=n}q=this._.nextNode;n=this._.lastNode;for(this._.nextNode=null;q;){var w=0,y=q.hasAscendant("pre"),C=q.type!=CKEDITOR.NODE_ELEMENT,r=0;if(C)q.type==CKEDITOR.NODE_TEXT&&f.test(q.getText())&&(C=0);else{var x=q.getName(); -if(CKEDITOR.dtd.$block[x]&&"false"==q.getAttribute("contenteditable")){h=q;c(this,a,h);break}else if(q.isBlockBoundary(this.forceBrBreak&&!y&&{br:1})){if("br"==x)C=1;else if(!e&&!q.getChildCount()&&"hr"!=x){h=q;l=q.equals(n);break}e&&(e.setEndAt(q,CKEDITOR.POSITION_BEFORE_START),"br"!=x&&(this._.nextNode=q));w=1}else{if(q.getFirst()){e||(e=this.range.clone(),e.setStartAt(q,CKEDITOR.POSITION_BEFORE_START));q=q.getFirst();continue}C=1}}C&&!e&&(e=this.range.clone(),e.setStartAt(q,CKEDITOR.POSITION_BEFORE_START)); -l=(!w||C)&&q.equals(n);if(e&&!w)for(;!q.getNext(k)&&!l;){x=q.getParent();if(x.isBlockBoundary(this.forceBrBreak&&!y&&{br:1})){w=1;C=0;l||x.equals(n);e.setEndAt(x,CKEDITOR.POSITION_BEFORE_END);break}q=x;C=1;l=q.equals(n);r=1}C&&e.setEndAt(q,CKEDITOR.POSITION_AFTER_END);q=this._getNextSourceNode(q,r,n);if((l=!q)||w&&e)break}if(!h){if(!e)return this._.docEndMarker&&this._.docEndMarker.remove(),this._.nextNode=null;h=new CKEDITOR.dom.elementPath(e.startContainer,e.root);q=h.blockLimit;w={div:1,th:1,td:1}; -h=h.block;!h&&q&&!this.enforceRealBlocks&&w[q.getName()]&&e.checkStartOfBlock()&&e.checkEndOfBlock()&&!q.equals(e.root)?h=q:!h||this.enforceRealBlocks&&h.is(b)?(h=this.range.document.createElement(a),e.extractContents().appendTo(h),h.trim(),e.insertNode(h),u=t=!0):"li"!=h.getName()?e.checkStartOfBlock()&&e.checkEndOfBlock()||(h=h.clone(!1),e.extractContents().appendTo(h),h.trim(),t=e.splitBlock(),u=!t.wasStartOfBlock,t=!t.wasEndOfBlock,e.insertNode(h)):l||(this._.nextNode=h.equals(n)?null:this._getNextSourceNode(e.getBoundaryNodes().endNode, -1,n))}u&&(u=h.getPrevious())&&u.type==CKEDITOR.NODE_ELEMENT&&("br"==u.getName()?u.remove():u.getLast()&&"br"==u.getLast().$.nodeName.toLowerCase()&&u.getLast().remove());t&&(u=h.getLast())&&u.type==CKEDITOR.NODE_ELEMENT&&"br"==u.getName()&&(!CKEDITOR.env.needsBrFiller||u.getPrevious(m)||u.getNext(m))&&u.remove();this._.nextNode||(this._.nextNode=l||h.equals(n)||!n?null:this._getNextSourceNode(h,1,n));return h},_getNextSourceNode:function(a,b,c){function f(a){return!(a.equals(c)||a.equals(g))}var g= -this.range.root;for(a=a.getNextSourceNode(b,null,f);!m(a);)a=a.getNextSourceNode(b,null,f);return a}};CKEDITOR.dom.range.prototype.createIterator=function(){return new a(this)}}(),CKEDITOR.command=function(a,e){this.uiItems=[];this.exec=function(c){if(this.state==CKEDITOR.TRISTATE_DISABLED||!this.checkAllowed())return!1;this.editorFocus&&a.focus();return!1===this.fire("exec")?!0:!1!==e.exec.call(this,a,c)};this.refresh=function(a,c){if(!this.readOnly&&a.readOnly)return!0;if(this.context&&!c.isContextFor(this.context)|| -!this.checkAllowed(!0))return this.disable(),!0;this.startDisabled||this.enable();this.modes&&!this.modes[a.mode]&&this.disable();return!1===this.fire("refresh",{editor:a,path:c})?!0:e.refresh&&!1!==e.refresh.apply(this,arguments)};var c;this.checkAllowed=function(g){return g||"boolean"!=typeof c?c=a.activeFilter.checkFeature(this):c};CKEDITOR.tools.extend(this,e,{modes:{wysiwyg:1},editorFocus:1,contextSensitive:!!e.context,state:CKEDITOR.TRISTATE_DISABLED});CKEDITOR.event.call(this)},CKEDITOR.command.prototype= +a._setEndContainer(this.endContainer);a.endOffset=this.endOffset;a.collapsed=this.collapsed;return a},collapse:function(a){a?(this._setEndContainer(this.startContainer),this.endOffset=this.startOffset):(this._setStartContainer(this.endContainer),this.startOffset=this.endOffset);this.collapsed=!0},cloneContents:function(a){var b=new CKEDITOR.dom.documentFragment(this.document);this.collapsed||f(this,2,b,!1,"undefined"==typeof a?!0:a);return b},deleteContents:function(a){this.collapsed||f(this,0,null, +a)},extractContents:function(a,b){var d=new CKEDITOR.dom.documentFragment(this.document);this.collapsed||f(this,1,d,a,"undefined"==typeof b?!0:b);return d},createBookmark:function(a){var b,d,c,e,h=this.collapsed;b=this.document.createElement("span");b.data("cke-bookmark",1);b.setStyle("display","none");b.setHtml("\x26nbsp;");a&&(c="cke_bm_"+CKEDITOR.tools.getNextNumber(),b.setAttribute("id",c+(h?"C":"S")));h||(d=b.clone(),d.setHtml("\x26nbsp;"),a&&d.setAttribute("id",c+"E"),e=this.clone(),e.collapse(), +e.insertNode(d));e=this.clone();e.collapse(!0);e.insertNode(b);d?(this.setStartAfter(b),this.setEndBefore(d)):this.moveToPosition(b,CKEDITOR.POSITION_AFTER_END);return{startNode:a?c+(h?"C":"S"):b,endNode:a?c+"E":d,serializable:a,collapsed:h}},createBookmark2:function(){function a(g){var b=g.container,c=g.offset,e;e=b;var h=c;e=e.type!=CKEDITOR.NODE_ELEMENT||0===h||h==e.getChildCount()?0:e.getChild(h-1).type==CKEDITOR.NODE_TEXT&&e.getChild(h).type==CKEDITOR.NODE_TEXT;e&&(b=b.getChild(c-1),c=b.getLength()); +if(b.type==CKEDITOR.NODE_ELEMENT&&0=a.offset&&(a.offset=c.getIndex(),a.container=c.getParent()))}}var d=CKEDITOR.dom.walker.nodeType(CKEDITOR.NODE_TEXT,!0);return function(d){var c=this.collapsed,e={container:this.startContainer,offset:this.startOffset},h={container:this.endContainer,offset:this.endOffset};d&&(a(e),b(e,this.root),c||(a(h),b(h,this.root)));return{start:e.container.getAddress(d),end:c?null:h.container.getAddress(d),startOffset:e.offset, +endOffset:h.offset,normalized:d,collapsed:c,is2:!0}}}(),moveToBookmark:function(a){if(a.is2){var b=this.document.getByAddress(a.start,a.normalized),d=a.startOffset,c=a.end&&this.document.getByAddress(a.end,a.normalized);a=a.endOffset;this.setStart(b,d);c?this.setEnd(c,a):this.collapse(!0)}else b=(d=a.serializable)?this.document.getById(a.startNode):a.startNode,a=d?this.document.getById(a.endNode):a.endNode,this.setStartBefore(b),b.remove(),a?(this.setEndBefore(a),a.remove()):this.collapse(!0)},getBoundaryNodes:function(){var a= +this.startContainer,b=this.endContainer,d=this.startOffset,c=this.endOffset,e;if(a.type==CKEDITOR.NODE_ELEMENT)if(e=a.getChildCount(),e>d)a=a.getChild(d);else if(1>e)a=a.getPreviousSourceNode();else{for(a=a.$;a.lastChild;)a=a.lastChild;a=new CKEDITOR.dom.node(a);a=a.getNextSourceNode()||a}if(b.type==CKEDITOR.NODE_ELEMENT)if(e=b.getChildCount(),e>c)b=b.getChild(c).getPreviousSourceNode(!0);else if(1>e)b=b.getPreviousSourceNode();else{for(b=b.$;b.lastChild;)b=b.lastChild;b=new CKEDITOR.dom.node(b)}a.getPosition(b)& +CKEDITOR.POSITION_FOLLOWING&&(a=b);return{startNode:a,endNode:b}},getCommonAncestor:function(a,b){var d=this.startContainer,c=this.endContainer,d=d.equals(c)?a&&d.type==CKEDITOR.NODE_ELEMENT&&this.startOffset==this.endOffset-1?d.getChild(this.startOffset):d:d.getCommonAncestor(c);return b&&!d.is?d.getParent():d},optimize:function(){var a=this.startContainer,b=this.startOffset;a.type!=CKEDITOR.NODE_ELEMENT&&(b?b>=a.getLength()&&this.setStartAfter(a):this.setStartBefore(a));a=this.endContainer;b=this.endOffset; +a.type!=CKEDITOR.NODE_ELEMENT&&(b?b>=a.getLength()&&this.setEndAfter(a):this.setEndBefore(a))},optimizeBookmark:function(){var a=this.startContainer,b=this.endContainer;a.is&&a.is("span")&&a.data("cke-bookmark")&&this.setStartAt(a,CKEDITOR.POSITION_BEFORE_START);b&&b.is&&b.is("span")&&b.data("cke-bookmark")&&this.setEndAt(b,CKEDITOR.POSITION_AFTER_END)},trim:function(a,b){var d=this.startContainer,c=this.startOffset,e=this.collapsed;if((!a||e)&&d&&d.type==CKEDITOR.NODE_TEXT){if(c)if(c>=d.getLength())c= +d.getIndex()+1,d=d.getParent();else{var h=d.split(c),c=d.getIndex()+1,d=d.getParent();this.startContainer.equals(this.endContainer)?this.setEnd(h,this.endOffset-this.startOffset):d.equals(this.endContainer)&&(this.endOffset+=1)}else c=d.getIndex(),d=d.getParent();this.setStart(d,c);if(e){this.collapse(!0);return}}d=this.endContainer;c=this.endOffset;b||e||!d||d.type!=CKEDITOR.NODE_TEXT||(c?(c>=d.getLength()||d.split(c),c=d.getIndex()+1):c=d.getIndex(),d=d.getParent(),this.setEnd(d,c))},enlarge:function(a, +b){function d(a){return a&&a.type==CKEDITOR.NODE_ELEMENT&&a.hasAttribute("contenteditable")?null:a}var c=new RegExp(/[^\s\ufeff]/);switch(a){case CKEDITOR.ENLARGE_INLINE:var e=1;case CKEDITOR.ENLARGE_ELEMENT:var h=function(a,b){var d=new CKEDITOR.dom.range(f);d.setStart(a,b);d.setEndAt(f,CKEDITOR.POSITION_BEFORE_END);var d=new CKEDITOR.dom.walker(d),g;for(d.guard=function(a){return!(a.type==CKEDITOR.NODE_ELEMENT&&a.isBlockBoundary())};g=d.next();){if(g.type!=CKEDITOR.NODE_TEXT)return!1;C=g!=a?g.getText(): +g.substring(b);if(c.test(C))return!1}return!0};if(this.collapsed)break;var k=this.getCommonAncestor(),f=this.root,m,l,t,x,D,y=!1,z,C;z=this.startContainer;var G=this.startOffset;z.type==CKEDITOR.NODE_TEXT?(G&&(z=!CKEDITOR.tools.trim(z.substring(0,G)).length&&z,y=!!z),z&&((x=z.getPrevious())||(t=z.getParent()))):(G&&(x=z.getChild(G-1)||z.getLast()),x||(t=z));for(t=d(t);t||x;){if(t&&!x){!D&&t.equals(k)&&(D=!0);if(e?t.isBlockBoundary():!f.contains(t))break;y&&"inline"==t.getComputedStyle("display")|| +(y=!1,D?m=t:this.setStartBefore(t));x=t.getPrevious()}for(;x;)if(z=!1,x.type==CKEDITOR.NODE_COMMENT)x=x.getPrevious();else{if(x.type==CKEDITOR.NODE_TEXT)C=x.getText(),c.test(C)&&(x=null),z=/[\s\ufeff]$/.test(C);else if((x.$.offsetWidth>(CKEDITOR.env.webkit?1:0)||b&&x.is("br"))&&!x.data("cke-bookmark"))if(y&&CKEDITOR.dtd.$removeEmpty[x.getName()]){C=x.getText();if(c.test(C))x=null;else for(var G=x.$.getElementsByTagName("*"),E=0,J;J=G[E++];)if(!CKEDITOR.dtd.$removeEmpty[J.nodeName.toLowerCase()]){x= +null;break}x&&(z=!!C.length)}else x=null;z&&(y?D?m=t:t&&this.setStartBefore(t):y=!0);if(x){z=x.getPrevious();if(!t&&!z){t=x;x=null;break}x=z}else t=null}t&&(t=d(t.getParent()))}z=this.endContainer;G=this.endOffset;t=x=null;D=y=!1;z.type==CKEDITOR.NODE_TEXT?CKEDITOR.tools.trim(z.substring(G)).length?y=!0:(y=!z.getLength(),G==z.getLength()?(x=z.getNext())||(t=z.getParent()):h(z,G)&&(t=z.getParent())):(x=z.getChild(G))||(t=z);for(;t||x;){if(t&&!x){!D&&t.equals(k)&&(D=!0);if(e?t.isBlockBoundary():!f.contains(t))break; +y&&"inline"==t.getComputedStyle("display")||(y=!1,D?l=t:t&&this.setEndAfter(t));x=t.getNext()}for(;x;){z=!1;if(x.type==CKEDITOR.NODE_TEXT)C=x.getText(),h(x,0)||(x=null),z=/^[\s\ufeff]/.test(C);else if(x.type==CKEDITOR.NODE_ELEMENT){if((0=e.getLength()?c.setStartAfter(e):(c.setStartBefore(e),m=0):c.setStartBefore(e));h&&h.type==CKEDITOR.NODE_TEXT&&(k?k>=h.getLength()?c.setEndAfter(h):(c.setEndAfter(h),l=0):c.setEndBefore(h));var c=new CKEDITOR.dom.walker(c),t=CKEDITOR.dom.walker.bookmark(); +c.evaluator=function(b){return b.type==(a==CKEDITOR.SHRINK_ELEMENT?CKEDITOR.NODE_ELEMENT:CKEDITOR.NODE_TEXT)};var x;c.guard=function(b,c){if(t(b))return!0;if(a==CKEDITOR.SHRINK_ELEMENT&&b.type==CKEDITOR.NODE_TEXT||c&&b.equals(x)||!1===d&&b.type==CKEDITOR.NODE_ELEMENT&&b.isBlockBoundary()||b.type==CKEDITOR.NODE_ELEMENT&&b.hasAttribute("contenteditable"))return!1;c||b.type!=CKEDITOR.NODE_ELEMENT||(x=b);return!0};m&&(e=c[a==CKEDITOR.SHRINK_ELEMENT?"lastForward":"next"]())&&this.setStartAt(e,b?CKEDITOR.POSITION_AFTER_START: +CKEDITOR.POSITION_BEFORE_START);l&&(c.reset(),(c=c[a==CKEDITOR.SHRINK_ELEMENT?"lastBackward":"previous"]())&&this.setEndAt(c,b?CKEDITOR.POSITION_BEFORE_END:CKEDITOR.POSITION_AFTER_END));return!(!m&&!l)}},insertNode:function(a){this.optimizeBookmark();this.trim(!1,!0);var b=this.startContainer,d=b.getChild(this.startOffset);d?a.insertBefore(d):b.append(a);a.getParent()&&a.getParent().equals(this.endContainer)&&this.endOffset++;this.setStartBefore(a)},moveToPosition:function(a,b){this.setStartAt(a, +b);this.collapse(!0)},moveToRange:function(a){this.setStart(a.startContainer,a.startOffset);this.setEnd(a.endContainer,a.endOffset)},selectNodeContents:function(a){this.setStart(a,0);this.setEnd(a,a.type==CKEDITOR.NODE_TEXT?a.getLength():a.getChildCount())},setStart:function(b,d){b.type==CKEDITOR.NODE_ELEMENT&&CKEDITOR.dtd.$empty[b.getName()]&&(d=b.getIndex(),b=b.getParent());this._setStartContainer(b);this.startOffset=d;this.endContainer||(this._setEndContainer(b),this.endOffset=d);a(this)},setEnd:function(b, +d){b.type==CKEDITOR.NODE_ELEMENT&&CKEDITOR.dtd.$empty[b.getName()]&&(d=b.getIndex()+1,b=b.getParent());this._setEndContainer(b);this.endOffset=d;this.startContainer||(this._setStartContainer(b),this.startOffset=d);a(this)},setStartAfter:function(a){this.setStart(a.getParent(),a.getIndex()+1)},setStartBefore:function(a){this.setStart(a.getParent(),a.getIndex())},setEndAfter:function(a){this.setEnd(a.getParent(),a.getIndex()+1)},setEndBefore:function(a){this.setEnd(a.getParent(),a.getIndex())},setStartAt:function(b, +d){switch(d){case CKEDITOR.POSITION_AFTER_START:this.setStart(b,0);break;case CKEDITOR.POSITION_BEFORE_END:b.type==CKEDITOR.NODE_TEXT?this.setStart(b,b.getLength()):this.setStart(b,b.getChildCount());break;case CKEDITOR.POSITION_BEFORE_START:this.setStartBefore(b);break;case CKEDITOR.POSITION_AFTER_END:this.setStartAfter(b)}a(this)},setEndAt:function(b,d){switch(d){case CKEDITOR.POSITION_AFTER_START:this.setEnd(b,0);break;case CKEDITOR.POSITION_BEFORE_END:b.type==CKEDITOR.NODE_TEXT?this.setEnd(b, +b.getLength()):this.setEnd(b,b.getChildCount());break;case CKEDITOR.POSITION_BEFORE_START:this.setEndBefore(b);break;case CKEDITOR.POSITION_AFTER_END:this.setEndAfter(b)}a(this)},fixBlock:function(a,b){var d=this.createBookmark(),c=this.document.createElement(b);this.collapse(a);this.enlarge(CKEDITOR.ENLARGE_BLOCK_CONTENTS);this.extractContents().appendTo(c);c.trim();this.insertNode(c);var e=c.getBogus();e&&e.remove();c.appendBogus();this.moveToBookmark(d);return c},splitBlock:function(a,b){var d= +new CKEDITOR.dom.elementPath(this.startContainer,this.root),c=new CKEDITOR.dom.elementPath(this.endContainer,this.root),e=d.block,h=c.block,f=null;if(!d.blockLimit.equals(c.blockLimit))return null;"br"!=a&&(e||(e=this.fixBlock(!0,a),h=(new CKEDITOR.dom.elementPath(this.endContainer,this.root)).block),h||(h=this.fixBlock(!1,a)));d=e&&this.checkStartOfBlock();c=h&&this.checkEndOfBlock();this.deleteContents();e&&e.equals(h)&&(c?(f=new CKEDITOR.dom.elementPath(this.startContainer,this.root),this.moveToPosition(h, +CKEDITOR.POSITION_AFTER_END),h=null):d?(f=new CKEDITOR.dom.elementPath(this.startContainer,this.root),this.moveToPosition(e,CKEDITOR.POSITION_BEFORE_START),e=null):(h=this.splitElement(e,b||!1),e.is("ul","ol")||e.appendBogus()));return{previousBlock:e,nextBlock:h,wasStartOfBlock:d,wasEndOfBlock:c,elementPath:f}},splitElement:function(a,b){if(!this.collapsed)return null;this.setEndAt(a,CKEDITOR.POSITION_BEFORE_END);var d=this.extractContents(!1,b||!1),c=a.clone(!1,b||!1);d.appendTo(c);c.insertAfter(a); +this.moveToPosition(a,CKEDITOR.POSITION_AFTER_END);return c},removeEmptyBlocksAtEnd:function(){function a(g){return function(a){return b(a)||d(a)||a.type==CKEDITOR.NODE_ELEMENT&&a.isEmptyInlineRemoveable()||g.is("table")&&a.is("caption")?!1:!0}}var b=CKEDITOR.dom.walker.whitespaces(),d=CKEDITOR.dom.walker.bookmark(!1);return function(b){for(var d=this.createBookmark(),c=this[b?"endPath":"startPath"](),e=c.block||c.blockLimit,h;e&&!e.equals(c.root)&&!e.getFirst(a(e));)h=e.getParent(),this[b?"setEndAt": +"setStartAt"](e,CKEDITOR.POSITION_AFTER_END),e.remove(1),e=h;this.moveToBookmark(d)}}(),startPath:function(){return new CKEDITOR.dom.elementPath(this.startContainer,this.root)},endPath:function(){return new CKEDITOR.dom.elementPath(this.endContainer,this.root)},checkBoundaryOfElement:function(a,b){var d=b==CKEDITOR.START,c=this.clone();c.collapse(d);c[d?"setStartAt":"setEndAt"](a,d?CKEDITOR.POSITION_AFTER_START:CKEDITOR.POSITION_BEFORE_END);c=new CKEDITOR.dom.walker(c);c.evaluator=e(d);return c[d? +"checkBackward":"checkForward"]()},checkStartOfBlock:function(){var a=this.startContainer,b=this.startOffset;CKEDITOR.env.ie&&b&&a.type==CKEDITOR.NODE_TEXT&&(a=CKEDITOR.tools.ltrim(a.substring(0,b)),k.test(a)&&this.trim(0,1));this.trim();a=new CKEDITOR.dom.elementPath(this.startContainer,this.root);b=this.clone();b.collapse(!0);b.setStartAt(a.block||a.blockLimit,CKEDITOR.POSITION_AFTER_START);a=new CKEDITOR.dom.walker(b);a.evaluator=c();return a.checkBackward()},checkEndOfBlock:function(){var a=this.endContainer, +b=this.endOffset;CKEDITOR.env.ie&&a.type==CKEDITOR.NODE_TEXT&&(a=CKEDITOR.tools.rtrim(a.substring(b)),k.test(a)&&this.trim(1,0));this.trim();a=new CKEDITOR.dom.elementPath(this.endContainer,this.root);b=this.clone();b.collapse(!1);b.setEndAt(a.block||a.blockLimit,CKEDITOR.POSITION_BEFORE_END);a=new CKEDITOR.dom.walker(b);a.evaluator=c();return a.checkForward()},getPreviousNode:function(a,b,d){var c=this.clone();c.collapse(1);c.setStartAt(d||this.root,CKEDITOR.POSITION_AFTER_START);d=new CKEDITOR.dom.walker(c); +d.evaluator=a;d.guard=b;return d.previous()},getNextNode:function(a,b,d){var c=this.clone();c.collapse();c.setEndAt(d||this.root,CKEDITOR.POSITION_BEFORE_END);d=new CKEDITOR.dom.walker(c);d.evaluator=a;d.guard=b;return d.next()},checkReadOnly:function(){function a(b,d){for(;b;){if(b.type==CKEDITOR.NODE_ELEMENT){if("false"==b.getAttribute("contentEditable")&&!b.data("cke-editable"))return 0;if(b.is("html")||"true"==b.getAttribute("contentEditable")&&(b.contains(d)||b.equals(d)))break}b=b.getParent()}return 1} +return function(){var b=this.startContainer,d=this.endContainer;return!(a(b,d)&&a(d,b))}}(),moveToElementEditablePosition:function(a,b){if(a.type==CKEDITOR.NODE_ELEMENT&&!a.isEditable(!1))return this.moveToPosition(a,b?CKEDITOR.POSITION_AFTER_END:CKEDITOR.POSITION_BEFORE_START),!0;for(var c=0;a;){if(a.type==CKEDITOR.NODE_TEXT){b&&this.endContainer&&this.checkEndOfBlock()&&k.test(a.getText())?this.moveToPosition(a,CKEDITOR.POSITION_BEFORE_START):this.moveToPosition(a,b?CKEDITOR.POSITION_AFTER_END: +CKEDITOR.POSITION_BEFORE_START);c=1;break}if(a.type==CKEDITOR.NODE_ELEMENT)if(a.isEditable())this.moveToPosition(a,b?CKEDITOR.POSITION_BEFORE_END:CKEDITOR.POSITION_AFTER_START),c=1;else if(b&&a.is("br")&&this.endContainer&&this.checkEndOfBlock())this.moveToPosition(a,CKEDITOR.POSITION_BEFORE_START);else if("false"==a.getAttribute("contenteditable")&&a.is(CKEDITOR.dtd.$block))return this.setStartBefore(a),this.setEndAfter(a),!0;var e=a,h=c,n=void 0;e.type==CKEDITOR.NODE_ELEMENT&&e.isEditable(!1)&& +(n=e[b?"getLast":"getFirst"](d));h||n||(n=e[b?"getPrevious":"getNext"](d));a=n}return!!c},moveToClosestEditablePosition:function(a,b){var d,c=0,e,h,f=[CKEDITOR.POSITION_AFTER_END,CKEDITOR.POSITION_BEFORE_START];a?(d=new CKEDITOR.dom.range(this.root),d.moveToPosition(a,f[b?0:1])):d=this.clone();if(a&&!a.is(CKEDITOR.dtd.$block))c=1;else if(e=d[b?"getNextEditableNode":"getPreviousEditableNode"]())c=1,(h=e.type==CKEDITOR.NODE_ELEMENT)&&e.is(CKEDITOR.dtd.$block)&&"false"==e.getAttribute("contenteditable")? +(d.setStartAt(e,CKEDITOR.POSITION_BEFORE_START),d.setEndAt(e,CKEDITOR.POSITION_AFTER_END)):!CKEDITOR.env.needsBrFiller&&h&&e.is(CKEDITOR.dom.walker.validEmptyBlockContainers)?(d.setEnd(e,0),d.collapse()):d.moveToPosition(e,f[b?1:0]);c&&this.moveToRange(d);return!!c},moveToElementEditStart:function(a){return this.moveToElementEditablePosition(a)},moveToElementEditEnd:function(a){return this.moveToElementEditablePosition(a,!0)},getEnclosedNode:function(){var a=this.clone();a.optimize();if(a.startContainer.type!= +CKEDITOR.NODE_ELEMENT||a.endContainer.type!=CKEDITOR.NODE_ELEMENT)return null;var a=new CKEDITOR.dom.walker(a),b=CKEDITOR.dom.walker.bookmark(!1,!0),d=CKEDITOR.dom.walker.whitespaces(!0);a.evaluator=function(a){return d(a)&&b(a)};var c=a.next();a.reset();return c&&c.equals(a.previous())?c:null},getTouchedStartNode:function(){var a=this.startContainer;return this.collapsed||a.type!=CKEDITOR.NODE_ELEMENT?a:a.getChild(this.startOffset)||a},getTouchedEndNode:function(){var a=this.endContainer;return this.collapsed|| +a.type!=CKEDITOR.NODE_ELEMENT?a:a.getChild(this.endOffset-1)||a},getNextEditableNode:h(),getPreviousEditableNode:h(1),scrollIntoView:function(){var a=new CKEDITOR.dom.element.createFromHtml("\x3cspan\x3e\x26nbsp;\x3c/span\x3e",this.document),b,d,c,e=this.clone();e.optimize();(c=e.startContainer.type==CKEDITOR.NODE_TEXT)?(d=e.startContainer.getText(),b=e.startContainer.split(e.startOffset),a.insertAfter(e.startContainer)):e.insertNode(a);a.scrollIntoView();c&&(e.startContainer.setText(d),b.remove()); +a.remove()},_setStartContainer:function(a){this.startContainer=a},_setEndContainer:function(a){this.endContainer=a}}}(),CKEDITOR.POSITION_AFTER_START=1,CKEDITOR.POSITION_BEFORE_END=2,CKEDITOR.POSITION_BEFORE_START=3,CKEDITOR.POSITION_AFTER_END=4,CKEDITOR.ENLARGE_ELEMENT=1,CKEDITOR.ENLARGE_BLOCK_CONTENTS=2,CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS=3,CKEDITOR.ENLARGE_INLINE=4,CKEDITOR.START=1,CKEDITOR.END=2,CKEDITOR.SHRINK_ELEMENT=1,CKEDITOR.SHRINK_TEXT=2,"use strict",function(){function a(a){1>arguments.length|| +(this.range=a,this.forceBrBreak=0,this.enlargeBr=1,this.enforceRealBlocks=0,this._||(this._={}))}function f(a){var b=[];a.forEach(function(a){if("true"==a.getAttribute("contenteditable"))return b.push(a),!1},CKEDITOR.NODE_ELEMENT,!0);return b}function c(a,b,e,h){a:{null==h&&(h=f(e));for(var k;k=h.shift();)if(k.getDtd().p){h={element:k,remaining:h};break a}h=null}if(!h)return 0;if((k=CKEDITOR.filter.instances[h.element.data("cke-filter")])&&!k.check(b))return c(a,b,e,h.remaining);b=new CKEDITOR.dom.range(h.element); +b.selectNodeContents(h.element);b=b.createIterator();b.enlargeBr=a.enlargeBr;b.enforceRealBlocks=a.enforceRealBlocks;b.activeFilter=b.filter=k;a._.nestedEditable={element:h.element,container:e,remaining:h.remaining,iterator:b};return 1}function e(a,b,c){if(!b)return!1;a=a.clone();a.collapse(!c);return a.checkBoundaryOfElement(b,c?CKEDITOR.START:CKEDITOR.END)}var h=/^[\r\n\t ]+$/,m=CKEDITOR.dom.walker.bookmark(!1,!0),l=CKEDITOR.dom.walker.whitespaces(!0),k=function(a){return m(a)&&l(a)},b={dd:1,dt:1, +li:1};a.prototype={getNextParagraph:function(a){var g,f,l,v,r;a=a||"p";if(this._.nestedEditable){if(g=this._.nestedEditable.iterator.getNextParagraph(a))return this.activeFilter=this._.nestedEditable.iterator.activeFilter,g;this.activeFilter=this.filter;if(c(this,a,this._.nestedEditable.container,this._.nestedEditable.remaining))return this.activeFilter=this._.nestedEditable.iterator.activeFilter,this._.nestedEditable.iterator.getNextParagraph(a);this._.nestedEditable=null}if(!this.range.root.getDtd()[a])return null; +if(!this._.started){var n=this.range.clone();f=n.startPath();var q=n.endPath(),w=!n.collapsed&&e(n,f.block),A=!n.collapsed&&e(n,q.block,1);n.shrink(CKEDITOR.SHRINK_ELEMENT,!0);w&&n.setStartAt(f.block,CKEDITOR.POSITION_BEFORE_END);A&&n.setEndAt(q.block,CKEDITOR.POSITION_AFTER_START);f=n.endContainer.hasAscendant("pre",!0)||n.startContainer.hasAscendant("pre",!0);n.enlarge(this.forceBrBreak&&!f||!this.enlargeBr?CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS:CKEDITOR.ENLARGE_BLOCK_CONTENTS);n.collapsed||(f=new CKEDITOR.dom.walker(n.clone()), +q=CKEDITOR.dom.walker.bookmark(!0,!0),f.evaluator=q,this._.nextNode=f.next(),f=new CKEDITOR.dom.walker(n.clone()),f.evaluator=q,f=f.previous(),this._.lastNode=f.getNextSourceNode(!0,null,n.root),this._.lastNode&&this._.lastNode.type==CKEDITOR.NODE_TEXT&&!CKEDITOR.tools.trim(this._.lastNode.getText())&&this._.lastNode.getParent().isBlockBoundary()&&(q=this.range.clone(),q.moveToPosition(this._.lastNode,CKEDITOR.POSITION_AFTER_END),q.checkEndOfBlock()&&(q=new CKEDITOR.dom.elementPath(q.endContainer, +q.root),this._.lastNode=(q.block||q.blockLimit).getNextSourceNode(!0))),this._.lastNode&&n.root.contains(this._.lastNode)||(this._.lastNode=this._.docEndMarker=n.document.createText(""),this._.lastNode.insertAfter(f)),n=null);this._.started=1;f=n}q=this._.nextNode;n=this._.lastNode;for(this._.nextNode=null;q;){var w=0,A=q.hasAscendant("pre"),B=q.type!=CKEDITOR.NODE_ELEMENT,t=0;if(B)q.type==CKEDITOR.NODE_TEXT&&h.test(q.getText())&&(B=0);else{var x=q.getName();if(CKEDITOR.dtd.$block[x]&&"false"==q.getAttribute("contenteditable")){g= +q;c(this,a,g);break}else if(q.isBlockBoundary(this.forceBrBreak&&!A&&{br:1})){if("br"==x)B=1;else if(!f&&!q.getChildCount()&&"hr"!=x){g=q;l=q.equals(n);break}f&&(f.setEndAt(q,CKEDITOR.POSITION_BEFORE_START),"br"!=x&&(this._.nextNode=q));w=1}else{if(q.getFirst()){f||(f=this.range.clone(),f.setStartAt(q,CKEDITOR.POSITION_BEFORE_START));q=q.getFirst();continue}B=1}}B&&!f&&(f=this.range.clone(),f.setStartAt(q,CKEDITOR.POSITION_BEFORE_START));l=(!w||B)&&q.equals(n);if(f&&!w)for(;!q.getNext(k)&&!l;){x= +q.getParent();if(x.isBlockBoundary(this.forceBrBreak&&!A&&{br:1})){w=1;B=0;l||x.equals(n);f.setEndAt(x,CKEDITOR.POSITION_BEFORE_END);break}q=x;B=1;l=q.equals(n);t=1}B&&f.setEndAt(q,CKEDITOR.POSITION_AFTER_END);q=this._getNextSourceNode(q,t,n);if((l=!q)||w&&f)break}if(!g){if(!f)return this._.docEndMarker&&this._.docEndMarker.remove(),this._.nextNode=null;g=new CKEDITOR.dom.elementPath(f.startContainer,f.root);q=g.blockLimit;w={div:1,th:1,td:1};g=g.block;!g&&q&&!this.enforceRealBlocks&&w[q.getName()]&& +f.checkStartOfBlock()&&f.checkEndOfBlock()&&!q.equals(f.root)?g=q:!g||this.enforceRealBlocks&&g.is(b)?(g=this.range.document.createElement(a),f.extractContents().appendTo(g),g.trim(),f.insertNode(g),v=r=!0):"li"!=g.getName()?f.checkStartOfBlock()&&f.checkEndOfBlock()||(g=g.clone(!1),f.extractContents().appendTo(g),g.trim(),r=f.splitBlock(),v=!r.wasStartOfBlock,r=!r.wasEndOfBlock,f.insertNode(g)):l||(this._.nextNode=g.equals(n)?null:this._getNextSourceNode(f.getBoundaryNodes().endNode,1,n))}v&&(v= +g.getPrevious())&&v.type==CKEDITOR.NODE_ELEMENT&&("br"==v.getName()?v.remove():v.getLast()&&"br"==v.getLast().$.nodeName.toLowerCase()&&v.getLast().remove());r&&(v=g.getLast())&&v.type==CKEDITOR.NODE_ELEMENT&&"br"==v.getName()&&(!CKEDITOR.env.needsBrFiller||v.getPrevious(m)||v.getNext(m))&&v.remove();this._.nextNode||(this._.nextNode=l||g.equals(n)||!n?null:this._getNextSourceNode(g,1,n));return g},_getNextSourceNode:function(a,b,c){function e(a){return!(a.equals(c)||a.equals(h))}var h=this.range.root; +for(a=a.getNextSourceNode(b,null,e);!m(a);)a=a.getNextSourceNode(b,null,e);return a}};CKEDITOR.dom.range.prototype.createIterator=function(){return new a(this)}}(),CKEDITOR.command=function(a,f){this.uiItems=[];this.exec=function(c){if(this.state==CKEDITOR.TRISTATE_DISABLED||!this.checkAllowed())return!1;this.editorFocus&&a.focus();return!1===this.fire("exec")?!0:!1!==f.exec.call(this,a,c)};this.refresh=function(a,c){if(!this.readOnly&&a.readOnly)return!0;if(this.context&&!c.isContextFor(this.context)|| +!this.checkAllowed(!0))return this.disable(),!0;this.startDisabled||this.enable();this.modes&&!this.modes[a.mode]&&this.disable();return!1===this.fire("refresh",{editor:a,path:c})?!0:f.refresh&&!1!==f.refresh.apply(this,arguments)};var c;this.checkAllowed=function(e){return e||"boolean"!=typeof c?c=a.activeFilter.checkFeature(this):c};CKEDITOR.tools.extend(this,f,{modes:{wysiwyg:1},editorFocus:1,contextSensitive:!!f.context,state:CKEDITOR.TRISTATE_DISABLED});CKEDITOR.event.call(this)},CKEDITOR.command.prototype= {enable:function(){this.state==CKEDITOR.TRISTATE_DISABLED&&this.checkAllowed()&&this.setState(this.preserveState&&"undefined"!=typeof this.previousState?this.previousState:CKEDITOR.TRISTATE_OFF)},disable:function(){this.setState(CKEDITOR.TRISTATE_DISABLED)},setState:function(a){if(this.state==a||a!=CKEDITOR.TRISTATE_DISABLED&&!this.checkAllowed())return!1;this.previousState=this.state;this.state=a;this.fire("state");return!0},toggleState:function(){this.state==CKEDITOR.TRISTATE_OFF?this.setState(CKEDITOR.TRISTATE_ON): this.state==CKEDITOR.TRISTATE_ON&&this.setState(CKEDITOR.TRISTATE_OFF)}},CKEDITOR.event.implementOn(CKEDITOR.command.prototype),CKEDITOR.ENTER_P=1,CKEDITOR.ENTER_BR=2,CKEDITOR.ENTER_DIV=3,CKEDITOR.config={customConfig:"config.js",autoUpdateElement:!0,language:"",defaultLanguage:"en",contentsLangDirection:"",enterMode:CKEDITOR.ENTER_P,forceEnterMode:!1,shiftEnterMode:CKEDITOR.ENTER_BR,docType:"\x3c!DOCTYPE html\x3e",bodyId:"",bodyClass:"",fullPage:!1,height:200,contentsCss:CKEDITOR.getUrl("contents.css"), -extraPlugins:"",removePlugins:"",protectedSource:[],tabIndex:0,width:"",baseFloatZIndex:1E4,blockedKeystrokes:[CKEDITOR.CTRL+66,CKEDITOR.CTRL+73,CKEDITOR.CTRL+85]},function(){function a(a,b,d,c,h){var f,g;a=[];for(f in b){g=b[f];g="boolean"==typeof g?{}:"function"==typeof g?{match:g}:F(g);"$"!=f.charAt(0)&&(g.elements=f);d&&(g.featureName=d.toLowerCase());var e=g;e.elements=l(e.elements,/\s+/)||null;e.propertiesOnly=e.propertiesOnly||!0===e.elements;var k=/\s*,\s*/,n=void 0;for(n in N){e[n]=l(e[n], -k)||null;var r=e,m=E[n],p=l(e[E[n]],k),x=e[n],q=[],C=!0,B=void 0;p?C=!1:p={};for(B in x)"!"==B.charAt(0)&&(B=B.slice(1),q.push(B),p[B]=!0,C=!1);for(;B=q.pop();)x[B]=x["!"+B],delete x["!"+B];r[m]=(C?!1:p)||null}e.match=e.match||null;c.push(g);a.push(g)}b=h.elements;h=h.generic;var t;d=0;for(c=a.length;d=--k&&(m&&CKEDITOR.document.getDocumentElement().removeStyle("cursor"),h(c))},v=function(b,d){a[b]=1;var c=e[b];delete e[b];for(var h=0;hCKEDITOR.env.version?c.$.onreadystatechange=function(){if("loaded"==c.$.readyState||"complete"== -c.$.readyState)c.$.onreadystatechange=null,v(b,!0)}:(c.$.onload=function(){setTimeout(function(){v(b,!0)},0)},c.$.onerror=function(){v(b,!1)}));c.appendTo(CKEDITOR.document.getHead())}}};m&&CKEDITOR.document.getDocumentElement().setStyle("cursor","wait");for(var t=0;t=--k&&(m&&CKEDITOR.document.getDocumentElement().removeStyle("cursor"),g(c))},u=function(b,d){a[b]=1;var c=f[b];delete f[b];for(var g=0;gCKEDITOR.env.version?c.$.onreadystatechange=function(){if("loaded"==c.$.readyState||"complete"== +c.$.readyState)c.$.onreadystatechange=null,u(b,!0)}:(c.$.onload=function(){setTimeout(function(){u(b,!0)},0)},c.$.onerror=function(){u(b,!1)}));c.appendTo(CKEDITOR.document.getHead())}}};m&&CKEDITOR.document.getDocumentElement().setStyle("cursor","wait");for(var r=0;r]+)>)|(?:!--([\S|\s]*?)--\x3e)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))/g}},function(){var a=/([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g,e={checked:1,compact:1,declare:1,defer:1,disabled:1,ismap:1,multiple:1,nohref:1,noresize:1,noshade:1,nowrap:1,readonly:1,selected:1};CKEDITOR.htmlParser.prototype={onTagOpen:function(){},onTagClose:function(){}, -onText:function(){},onCDATA:function(){},onComment:function(){},parse:function(c){for(var g,f,m=0,l;g=this._.htmlPartsRegex.exec(c);){f=g.index;if(f>m)if(m=c.substring(m,f),l)l.push(m);else this.onText(m);m=this._.htmlPartsRegex.lastIndex;if(f=g[1])if(f=f.toLowerCase(),l&&CKEDITOR.dtd.$cdata[f]&&(this.onCDATA(l.join("")),l=null),!l){this.onTagClose(f);continue}if(l)l.push(g[0]);else if(f=g[3]){if(f=f.toLowerCase(),!/="/.test(f)){var k={},b,d=g[4];g=!!g[5];if(d)for(;b=a.exec(d);){var h=b[1].toLowerCase(); -b=b[2]||b[3]||b[4]||"";k[h]=!b&&e[h]?h:CKEDITOR.tools.htmlDecodeAttr(b)}this.onTagOpen(f,k,g);!l&&CKEDITOR.dtd.$cdata[f]&&(l=[])}}else if(f=g[2])this.onComment(f)}if(c.length>m)this.onText(c.substring(m,c.length))}}}(),CKEDITOR.htmlParser.basicWriter=CKEDITOR.tools.createClass({$:function(){this._={output:[]}},proto:{openTag:function(a){this._.output.push("\x3c",a)},openTagClose:function(a,e){e?this._.output.push(" /\x3e"):this._.output.push("\x3e")},attribute:function(a,e){"string"==typeof e&&(e= -CKEDITOR.tools.htmlEncodeAttr(e));this._.output.push(" ",a,'\x3d"',e,'"')},closeTag:function(a){this._.output.push("\x3c/",a,"\x3e")},text:function(a){this._.output.push(a)},comment:function(a){this._.output.push("\x3c!--",a,"--\x3e")},write:function(a){this._.output.push(a)},reset:function(){this._.output=[];this._.indent=!1},getHtml:function(a){var e=this._.output.join("");a&&this.reset();return e}}}),"use strict",function(){CKEDITOR.htmlParser.node=function(){};CKEDITOR.htmlParser.node.prototype= -{remove:function(){var a=this.parent.children,e=CKEDITOR.tools.indexOf(a,this),c=this.previous,g=this.next;c&&(c.next=g);g&&(g.previous=c);a.splice(e,1);this.parent=null},replaceWith:function(a){var e=this.parent.children,c=CKEDITOR.tools.indexOf(e,this),g=a.previous=this.previous,f=a.next=this.next;g&&(g.next=a);f&&(f.previous=a);e[c]=a;a.parent=this.parent;this.parent=null},insertAfter:function(a){var e=a.parent.children,c=CKEDITOR.tools.indexOf(e,a),g=a.next;e.splice(c+1,0,this);this.next=a.next; -this.previous=a;a.next=this;g&&(g.previous=this);this.parent=a.parent},insertBefore:function(a){var e=a.parent.children,c=CKEDITOR.tools.indexOf(e,a);e.splice(c,0,this);this.next=a;(this.previous=a.previous)&&(a.previous.next=this);a.previous=this;this.parent=a.parent},getAscendant:function(a){var e="function"==typeof a?a:"string"==typeof a?function(c){return c.name==a}:function(c){return c.name in a},c=this.parent;for(;c&&c.type==CKEDITOR.NODE_ELEMENT;){if(e(c))return c;c=c.parent}return null},wrapWith:function(a){this.replaceWith(a); -a.add(this);return a},getIndex:function(){return CKEDITOR.tools.indexOf(this.parent.children,this)},getFilterContext:function(a){return a||{}}}}(),"use strict",CKEDITOR.htmlParser.comment=function(a){this.value=a;this._={isBlockLike:!1}},CKEDITOR.htmlParser.comment.prototype=CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_COMMENT,filter:function(a,e){var c=this.value;if(!(c=a.onComment(e,c,this)))return this.remove(),!1;if("string"!=typeof c)return this.replaceWith(c),!1;this.value= -c;return!0},writeHtml:function(a,e){e&&this.filter(e);a.comment(this.value)}}),"use strict",function(){CKEDITOR.htmlParser.text=function(a){this.value=a;this._={isBlockLike:!1}};CKEDITOR.htmlParser.text.prototype=CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_TEXT,filter:function(a,e){if(!(this.value=a.onText(e,this.value,this)))return this.remove(),!1},writeHtml:function(a,e){e&&this.filter(e);a.text(this.value)}})}(),"use strict",function(){CKEDITOR.htmlParser.cdata=function(a){this.value= -a};CKEDITOR.htmlParser.cdata.prototype=CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_TEXT,filter:function(){},writeHtml:function(a){a.write(this.value)}})}(),"use strict",CKEDITOR.htmlParser.fragment=function(){this.children=[];this.parent=null;this._={isBlockLike:!0,hasInlineStarted:!1}},function(){function a(a){return a.attributes["data-cke-survive"]?!1:"a"==a.name&&a.attributes.href||CKEDITOR.dtd.$removeEmpty[a.name]}var e=CKEDITOR.tools.extend({table:1,ul:1,ol:1,dl:1}, -CKEDITOR.dtd.table,CKEDITOR.dtd.ul,CKEDITOR.dtd.ol,CKEDITOR.dtd.dl),c={ol:1,ul:1},g=CKEDITOR.tools.extend({},{html:1},CKEDITOR.dtd.html,CKEDITOR.dtd.body,CKEDITOR.dtd.head,{style:1,script:1}),f={ul:"li",ol:"li",dl:"dd",table:"tbody",tbody:"tr",thead:"tr",tfoot:"tr",tr:"td"};CKEDITOR.htmlParser.fragment.fromHtml=function(m,l,k){function b(a){var b;if(0l;l++)if(e=f[l]){e=e.exec(a,g,this);if(!1===e)return null;if(e&&e!=g)return this.onNode(a,e);if(g.parent&&!g.name)break}return g},onNode:function(a,g){var f=g.type;return f==CKEDITOR.NODE_ELEMENT?this.onElement(a,g):f==CKEDITOR.NODE_TEXT?new CKEDITOR.htmlParser.text(this.onText(a,g.value)): -f==CKEDITOR.NODE_COMMENT?new CKEDITOR.htmlParser.comment(this.onComment(a,g.value)):null},onAttribute:function(a,g,f,e){return(f=this.attributesRules[f])?f.exec(a,e,g,this):e}}});CKEDITOR.htmlParser.filterRulesGroup=a;a.prototype={add:function(a,g,f){this.rules.splice(this.findIndex(g),0,{value:a,priority:g,options:f})},addMany:function(a,g,f){for(var e=[this.findIndex(g),0],l=0,k=a.length;l/g, -"\x26gt;")+"\x3c/textarea\x3e");return"\x3ccke:encoded\x3e"+encodeURIComponent(a)+"\x3c/cke:encoded\x3e"})}function p(a){return a.replace(N,function(a,b){return decodeURIComponent(b)})}function v(a){return a.replace(/\x3c!--(?!{cke_protected})[\s\S]+?--\x3e/g,function(a){return"\x3c!--"+w+"{C}"+encodeURIComponent(a).replace(/--/g,"%2D%2D")+"--\x3e"})}function u(a){return a.replace(/\x3c!--\{cke_protected\}\{C\}([\s\S]+?)--\x3e/g,function(a,b){return decodeURIComponent(b)})}function t(a,b){var d=b._.dataStore; -return a.replace(/\x3c!--\{cke_protected\}([\s\S]+?)--\x3e/g,function(a,b){return decodeURIComponent(b)}).replace(/\{cke_protected_(\d+)\}/g,function(a,b){return d&&d[b]||""})}function n(a,b){var d=[],c=b.config.protectedSource,f=b._.dataStore||(b._.dataStore={id:1}),h=/<\!--\{cke_temp(comment)?\}(\d*?)--\x3e/g,c=[/|$)/gi,//gi,//gi].concat(c);a=a.replace(/\x3c!--[\s\S]*?--\x3e/g,function(a){return"\x3c!--{cke_tempcomment}"+(d.push(a)- -1)+"--\x3e"});for(var g=0;g]+\s*=\s*(?:[^'"\s>]+|'[^']*'|"[^"]*"))|[^\s=\/>]+))+\s*\/?>/g,function(a){return a.replace(/\x3c!--\{cke_protected\}([^>]*)--\x3e/g,function(a,b){f[f.id]= -decodeURIComponent(b);return"{cke_protected_"+f.id++ +"}"})});return a=a.replace(/<(title|iframe|textarea)([^>]*)>([\s\S]*?)<\/\1>/g,function(a,d,c,f){return"\x3c"+d+c+"\x3e"+t(u(f),b)+"\x3c/"+d+"\x3e"})}CKEDITOR.htmlDataProcessor=function(b){var c,f,g=this;this.editor=b;this.dataFilter=c=new CKEDITOR.htmlParser.filter;this.htmlFilter=f=new CKEDITOR.htmlParser.filter;this.writer=new CKEDITOR.htmlParser.basicWriter;c.addRules(x);c.addRules(B,{applyToAll:!0});c.addRules(a(b,"data"),{applyToAll:!0}); -f.addRules(A);f.addRules(z,{applyToAll:!0});f.addRules(a(b,"html"),{applyToAll:!0});b.on("toHtml",function(a){a=a.data;var c=a.dataValue,f,c=n(c,b),c=h(c,I),c=d(c),c=h(c,J),c=c.replace(E,"$1cke:$2"),c=c.replace(T,"\x3ccke:$1$2\x3e\x3c/cke:$1\x3e"),c=c.replace(/(]*>)(\r\n|\n)/g,"$1$2$2"),c=c.replace(/([^a-z0-9<\-])(on\w{3,})(?!>)/gi,"$1data-cke-"+CKEDITOR.rnd+"-$2");f=a.context||b.editable().getName();var g;CKEDITOR.env.ie&&9>CKEDITOR.env.version&&"pre"==f&&(f="div",c="\x3cpre\x3e"+c+"\x3c/pre\x3e", -g=1);f=b.document.createElement(f);f.setHtml("a"+c);c=f.getHtml().substr(1);c=c.replace(new RegExp("data-cke-"+CKEDITOR.rnd+"-","ig"),"");g&&(c=c.replace(/^
|<\/pre>$/gi,""));c=c.replace(P,"$1$2");c=p(c);c=u(c);f=!1===a.fixForBody?!1:e(a.enterMode,b.config.autoParagraph);c=CKEDITOR.htmlParser.fragment.fromHtml(c,a.context,f);f&&(g=c,!g.children.length&&CKEDITOR.dtd[g.name][f]&&(f=new CKEDITOR.htmlParser.element(f),g.add(f)));a.dataValue=c},null,null,5);b.on("toHtml",function(a){a.data.filter.applyTo(a.data.dataValue,
-!0,a.data.dontFilter,a.data.enterMode)&&b.fire("dataFiltered")},null,null,6);b.on("toHtml",function(a){a.data.dataValue.filterChildren(g.dataFilter,!0)},null,null,10);b.on("toHtml",function(a){a=a.data;var b=a.dataValue,d=new CKEDITOR.htmlParser.basicWriter;b.writeChildrenHtml(d);b=d.getHtml(!0);a.dataValue=v(b)},null,null,15);b.on("toDataFormat",function(a){var d=a.data.dataValue;a.data.enterMode!=CKEDITOR.ENTER_BR&&(d=d.replace(/^
/i,""));a.data.dataValue=CKEDITOR.htmlParser.fragment.fromHtml(d, -a.data.context,e(a.data.enterMode,b.config.autoParagraph))},null,null,5);b.on("toDataFormat",function(a){a.data.dataValue.filterChildren(g.htmlFilter,!0)},null,null,10);b.on("toDataFormat",function(a){a.data.filter.applyTo(a.data.dataValue,!1,!0)},null,null,11);b.on("toDataFormat",function(a){var d=a.data.dataValue,c=g.writer;c.reset();d.writeChildrenHtml(c);d=c.getHtml(!0);d=u(d);d=t(d,b);a.data.dataValue=d},null,null,15)};CKEDITOR.htmlDataProcessor.prototype={toHtml:function(a,b,d,c){var f=this.editor, -h,g,k,e;b&&"object"==typeof b?(h=b.context,d=b.fixForBody,c=b.dontFilter,g=b.filter,k=b.enterMode,e=b.protectedWhitespaces):h=b;h||null===h||(h=f.editable().getName());return f.fire("toHtml",{dataValue:a,context:h,fixForBody:d,dontFilter:c,filter:g||f.filter,enterMode:k||f.enterMode,protectedWhitespaces:e}).dataValue},toDataFormat:function(a,b){var d,c,f;b&&(d=b.context,c=b.filter,f=b.enterMode);d||null===d||(d=this.editor.editable().getName());return this.editor.fire("toDataFormat",{dataValue:a, -filter:c||this.editor.filter,context:d,enterMode:f||this.editor.enterMode}).dataValue}};var q=/(?: |\xa0)$/,w="{cke_protected}",y=CKEDITOR.dtd,C="caption colgroup col thead tfoot tbody".split(" "),r=CKEDITOR.tools.extend({},y.$blockLimit,y.$block),x={elements:{input:k,textarea:k}},B={attributeNames:[[/^on/,"data-cke-pa-on"],[/^data-cke-expando$/,""]]},A={elements:{embed:function(a){var b=a.parent;if(b&&"object"==b.name){var d=b.attributes.width,b=b.attributes.height;d&&(a.attributes.width=d); -b&&(a.attributes.height=b)}},a:function(a){var b=a.attributes;if(!(a.children.length||b.name||b.id||a.attributes["data-cke-saved-name"]))return!1}}},z={elementNames:[[/^cke:/,""],[/^\?xml:namespace$/,""]],attributeNames:[[/^data-cke-(saved|pa)-/,""],[/^data-cke-.*/,""],["hidefocus",""]],elements:{$:function(a){var b=a.attributes;if(b){if(b["data-cke-temp"])return!1;for(var d=["name","href","src"],c,f=0;fc?1:-1})},param:function(a){a.children=[];a.isEmpty=!0;return a},span:function(a){"Apple-style-span"==a.attributes["class"]&&delete a.name},html:function(a){delete a.attributes.contenteditable;delete a.attributes["class"]},body:function(a){delete a.attributes.spellcheck;delete a.attributes.contenteditable}, -style:function(a){var b=a.children[0];b&&b.value&&(b.value=CKEDITOR.tools.trim(b.value));a.attributes.type||(a.attributes.type="text/css")},title:function(a){var b=a.children[0];!b&&l(a,b=new CKEDITOR.htmlParser.text);b.value=a.attributes["data-cke-title"]||""},input:b,textarea:b},attributes:{"class":function(a){return CKEDITOR.tools.ltrim(a.replace(/(?:^|\s+)cke_[^\s]*/g,""))||!1}}};CKEDITOR.env.ie&&(z.attributes.style=function(a){return a.replace(/(^|;)([^\:]+)/g,function(a){return a.toLowerCase()})}); -var D=/<(a|area|img|input|source)\b([^>]*)>/gi,G=/([\w-:]+)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+))/gi,F=/^(href|src|name)$/i,J=/(?:])[^>]*>[\s\S]*?<\/style>)|(?:<(:?link|meta|base)[^>]*>)/gi,I=/(])[^>]*>)([\s\S]*?)(?:<\/textarea>)/gi,N=/([^<]*)<\/cke:encoded>/gi,E=/(<\/?)((?:object|embed|param|html|body|head|title)[^>]*>)/gi,P=/(<\/?)cke:((?:html|body|head|title)[^>]*>)/gi,T=/]*?)\/?>(?!\s*<\/cke:\1)/gi}(),"use strict",CKEDITOR.htmlParser.element= -function(a,e){this.name=a;this.attributes=e||{};this.children=[];var c=a||"",g=c.match(/^cke:(.*)/);g&&(c=g[1]);c=!!(CKEDITOR.dtd.$nonBodyContent[c]||CKEDITOR.dtd.$block[c]||CKEDITOR.dtd.$listItem[c]||CKEDITOR.dtd.$tableContent[c]||CKEDITOR.dtd.$nonEditable[c]||"br"==c);this.isEmpty=!!CKEDITOR.dtd.$empty[a];this.isUnknown=!CKEDITOR.dtd[a];this._={isBlockLike:c,hasInlineStarted:this.isEmpty||!c}},CKEDITOR.htmlParser.cssStyle=function(a){var e={};((a instanceof CKEDITOR.htmlParser.element?a.attributes.style: -a)||"").replace(/"/g,'"').replace(/\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g,function(a,g,f){"font-family"==g&&(f=f.replace(/["']/g,""));e[g.toLowerCase()]=f});return{rules:e,populate:function(a){var g=this.toString();g&&(a instanceof CKEDITOR.dom.element?a.setAttribute("style",g):a instanceof CKEDITOR.htmlParser.element?a.attributes.style=g:a.style=g)},toString:function(){var a=[],g;for(g in e)e[g]&&a.push(g,":",e[g],";");return a.join("")}}},function(){function a(a){return function(c){return c.type== -CKEDITOR.NODE_ELEMENT&&("string"==typeof a?c.name==a:c.name in a)}}var e=function(a,c){a=a[0];c=c[0];return ac?1:0},c=CKEDITOR.htmlParser.fragment.prototype;CKEDITOR.htmlParser.element.prototype=CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_ELEMENT,add:c.add,clone:function(){return new CKEDITOR.htmlParser.element(this.name,this.attributes)},filter:function(a,c){var e=this,l,k;c=e.getFilterContext(c);if(c.off)return!0;if(!e.parent)a.onRoot(c,e);for(;;){l=e.name;if(!(k= -a.onElementName(c,l)))return this.remove(),!1;e.name=k;if(!(e=a.onElement(c,e)))return this.remove(),!1;if(e!==this)return this.replaceWith(e),!1;if(e.name==l)break;if(e.type!=CKEDITOR.NODE_ELEMENT)return this.replaceWith(e),!1;if(!e.name)return this.replaceWithChildren(),!1}l=e.attributes;var b,d;for(b in l){for(k=l[b];;)if(d=a.onAttributeName(c,b))if(d!=b)delete l[b],b=d;else break;else{delete l[b];break}d&&(!1===(k=a.onAttribute(c,e,d,k))?delete l[d]:l[d]=k)}e.isEmpty||this.filterChildren(a,!1, -c);return!0},filterChildren:c.filterChildren,writeHtml:function(a,c){c&&this.filter(c);var m=this.name,l=[],k=this.attributes,b,d;a.openTag(m,k);for(b in k)l.push([b,k[b]]);a.sortAttributes&&l.sort(e);b=0;for(d=l.length;bCKEDITOR.env.version||CKEDITOR.env.quirks))this.hasFocus&& -(this.focus(),b());else if(this.hasFocus)this.focus(),a();else this.once("focus",function(){a()},null,null,-999)},getHtmlFromRange:function(a){if(a.collapsed)return new CKEDITOR.dom.documentFragment(a.document);a={doc:this.getDocument(),range:a.clone()};w.eol.detect(a,this);w.bogus.exclude(a);w.cell.shrink(a);a.fragment=a.range.cloneContents();w.tree.rebuild(a,this);w.eol.fix(a,this);return new CKEDITOR.dom.documentFragment(a.fragment.$)},extractHtmlFromRange:function(a,b){var d=y,c={range:a,doc:a.document}, -h=this.getHtmlFromRange(a);if(a.collapsed)return a.optimize(),h;a.enlarge(CKEDITOR.ENLARGE_INLINE,1);d.table.detectPurge(c);c.bookmark=a.createBookmark();delete c.range;var f=this.editor.createRange();f.moveToPosition(c.bookmark.startNode,CKEDITOR.POSITION_BEFORE_START);c.targetBookmark=f.createBookmark();d.list.detectMerge(c,this);d.table.detectRanges(c,this);d.block.detectMerge(c,this);c.tableContentsRanges?(d.table.deleteRanges(c),a.moveToBookmark(c.bookmark),c.range=a):(a.moveToBookmark(c.bookmark), -c.range=a,a.extractContents(d.detectExtractMerge(c)));a.moveToBookmark(c.targetBookmark);a.optimize();d.fixUneditableRangePosition(a);d.list.merge(c,this);d.table.purge(c,this);d.block.merge(c,this);if(b){d=a.startPath();if(c=a.checkStartOfBlock()&&a.checkEndOfBlock()&&d.block&&!a.root.equals(d.block)){a:{var c=d.block.getElementsByTag("span"),f=0,e;if(c)for(;e=c.getItem(f++);)if(!p(e)){c=!0;break a}c=!1}c=!c}c&&(a.moveToPosition(d.block,CKEDITOR.POSITION_BEFORE_START),d.block.remove())}else d.autoParagraph(this.editor, -a),v(a.startContainer)&&a.startContainer.appendBogus();a.startContainer.mergeSiblings();return h},setup:function(){var a=this.editor;this.attachListener(a,"beforeGetData",function(){var b=this.getData();this.is("textarea")||!1!==a.config.ignoreEmptyParagraph&&(b=b.replace(t,function(a,b){return b}));a.setData(b,null,1)},this);this.attachListener(a,"getSnapshot",function(a){a.data=this.getData(1)},this);this.attachListener(a,"afterSetData",function(){this.setData(a.getData(1))},this);this.attachListener(a, -"loadSnapshot",function(a){this.setData(a.data,1)},this);this.attachListener(a,"beforeFocus",function(){var b=a.getSelection();(b=b&&b.getNative())&&"Control"==b.type||this.focus()},this);this.attachListener(a,"insertHtml",function(a){this.insertHtml(a.data.dataValue,a.data.mode,a.data.range)},this);this.attachListener(a,"insertElement",function(a){this.insertElement(a.data)},this);this.attachListener(a,"insertText",function(a){this.insertText(a.data)},this);this.setReadOnly(a.readOnly);this.attachClass("cke_editable"); -a.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?this.attachClass("cke_editable_inline"):a.elementMode!=CKEDITOR.ELEMENT_MODE_REPLACE&&a.elementMode!=CKEDITOR.ELEMENT_MODE_APPENDTO||this.attachClass("cke_editable_themed");this.attachClass("cke_contents_"+a.config.contentsLangDirection);a.keystrokeHandler.blockedKeystrokes[8]=+a.readOnly;a.keystrokeHandler.attach(this);this.on("blur",function(){this.hasFocus=!1},null,null,-1);this.on("focus",function(){this.hasFocus=!0},null,null,-1);a.focusManager.add(this); -this.equals(CKEDITOR.document.getActive())&&(this.hasFocus=!0,a.once("contentDom",function(){a.focusManager.focus(this)},this));this.isInline()&&this.changeAttr("tabindex",a.tabIndex);if(!this.is("textarea")){a.document=this.getDocument();a.window=this.getWindow();var b=a.document;this.changeAttr("spellcheck",!a.config.disableNativeSpellChecker);var f=a.config.contentsLangDirection;this.getDirection(1)!=f&&this.changeAttr("dir",f);var e=CKEDITOR.getCss();e&&(f=b.getHead(),f.getCustomData("stylesheet")|| -(e=b.appendStyleText(e),e=new CKEDITOR.dom.element(e.ownerNode||e.owningElement),f.setCustomData("stylesheet",e),e.data("cke-temp",1)));f=b.getCustomData("stylesheet_ref")||0;b.setCustomData("stylesheet_ref",f+1);this.setCustomData("cke_includeReadonly",!a.config.disableReadonlyStyling);this.attachListener(this,"click",function(a){a=a.data;var b=(new CKEDITOR.dom.elementPath(a.getTarget(),this)).contains("a");b&&2!=a.$.button&&b.isReadOnly()&&a.preventDefault()});var k={8:1,46:1};this.attachListener(a, -"key",function(b){if(a.readOnly)return!0;var d=b.data.domEvent.getKey(),c;if(d in k){b=a.getSelection();var f,e=b.getRanges()[0],g=e.startPath(),p,E,r,d=8==d;CKEDITOR.env.ie&&11>CKEDITOR.env.version&&(f=b.getSelectedElement())||(f=m(b))?(a.fire("saveSnapshot"),e.moveToPosition(f,CKEDITOR.POSITION_BEFORE_START),f.remove(),e.select(),a.fire("saveSnapshot"),c=1):e.collapsed&&((p=g.block)&&(r=p[d?"getPrevious":"getNext"](h))&&r.type==CKEDITOR.NODE_ELEMENT&&r.is("table")&&e[d?"checkStartOfBlock":"checkEndOfBlock"]()? -(a.fire("saveSnapshot"),e[d?"checkEndOfBlock":"checkStartOfBlock"]()&&p.remove(),e["moveToElementEdit"+(d?"End":"Start")](r),e.select(),a.fire("saveSnapshot"),c=1):g.blockLimit&&g.blockLimit.is("td")&&(E=g.blockLimit.getAscendant("table"))&&e.checkBoundaryOfElement(E,d?CKEDITOR.START:CKEDITOR.END)&&(r=E[d?"getPrevious":"getNext"](h))?(a.fire("saveSnapshot"),e["moveToElementEdit"+(d?"End":"Start")](r),e.checkStartOfBlock()&&e.checkEndOfBlock()?r.remove():e.select(),a.fire("saveSnapshot"),c=1):(E=g.contains(["td", -"th","caption"]))&&e.checkBoundaryOfElement(E,d?CKEDITOR.START:CKEDITOR.END)&&(c=1))}return!c});a.blockless&&CKEDITOR.env.ie&&CKEDITOR.env.needsBrFiller&&this.attachListener(this,"keyup",function(b){b.data.getKeystroke()in k&&!this.getFirst(g)&&(this.appendBogus(),b=a.createRange(),b.moveToPosition(this,CKEDITOR.POSITION_AFTER_START),b.select())});this.attachListener(this,"dblclick",function(b){if(a.readOnly)return!1;b={element:b.data.getTarget()};a.fire("doubleclick",b)});CKEDITOR.env.ie&&this.attachListener(this, -"click",c);CKEDITOR.env.ie&&!CKEDITOR.env.edge||this.attachListener(this,"mousedown",function(b){var d=b.data.getTarget();d.is("img","hr","input","textarea","select")&&!d.isReadOnly()&&(a.getSelection().selectElement(d),d.is("input","textarea","select")&&b.data.preventDefault())});CKEDITOR.env.edge&&this.attachListener(this,"mouseup",function(b){(b=b.data.getTarget())&&b.is("img")&&a.getSelection().selectElement(b)});CKEDITOR.env.gecko&&this.attachListener(this,"mouseup",function(b){if(2==b.data.$.button&& -(b=b.data.getTarget(),!b.getOuterHtml().replace(t,""))){var d=a.createRange();d.moveToElementEditStart(b);d.select(!0)}});CKEDITOR.env.webkit&&(this.attachListener(this,"click",function(a){a.data.getTarget().is("input","select")&&a.data.preventDefault()}),this.attachListener(this,"mouseup",function(a){a.data.getTarget().is("input","textarea")&&a.data.preventDefault()}));CKEDITOR.env.webkit&&this.attachListener(a,"key",function(b){if(a.readOnly)return!0;b=b.data.domEvent.getKey();if(b in k){var c= -8==b,h=a.getSelection().getRanges()[0];b=h.startPath();if(h.collapsed)a:{var f=b.block;if(f&&h[c?"checkStartOfBlock":"checkEndOfBlock"]()&&h.moveToClosestEditablePosition(f,!c)&&h.collapsed){if(h.startContainer.type==CKEDITOR.NODE_ELEMENT){var e=h.startContainer.getChild(h.startOffset-(c?1:0));if(e&&e.type==CKEDITOR.NODE_ELEMENT&&e.is("hr")){a.fire("saveSnapshot");e.remove();b=!0;break a}}h=h.startPath().block;if(!h||h&&h.contains(f))b=void 0;else{a.fire("saveSnapshot");var g;(g=(c?h:f).getBogus())&& -g.remove();g=a.getSelection();e=g.createBookmarks();(c?f:h).moveChildren(c?h:f,!1);b.lastElement.mergeSiblings();d(f,h,!c);g.selectBookmarks(e);b=!0}}else b=!1}else c=h,g=b.block,h=c.endPath().block,g&&h&&!g.equals(h)?(a.fire("saveSnapshot"),(f=g.getBogus())&&f.remove(),c.enlarge(CKEDITOR.ENLARGE_INLINE),c.deleteContents(),h.getParent()&&(h.moveChildren(g,!1),b.lastElement.mergeSiblings(),d(g,h,!0)),c=a.getSelection().getRanges()[0],c.collapse(1),c.optimize(),""===c.startContainer.getHtml()&&c.startContainer.appendBogus(), -c.select(),b=!0):b=!1;if(!b)return;a.getSelection().scrollIntoView();a.fire("saveSnapshot");return!1}},this,null,100)}}},_:{detach:function(){this.editor.setData(this.editor.getData(),0,1);this.clearListeners();this.restoreAttrs();var a;if(a=this.removeCustomData("classes"))for(;a.length;)this.removeClass(a.pop());if(!this.is("textarea")){a=this.getDocument();var b=a.getHead();if(b.getCustomData("stylesheet")){var d=a.getCustomData("stylesheet_ref");--d?a.setCustomData("stylesheet_ref",d):(a.removeCustomData("stylesheet_ref"), -b.removeCustomData("stylesheet").remove())}}this.editor.fire("contentDomUnload");delete this.editor}}});CKEDITOR.editor.prototype.editable=function(a){var b=this._.editable;if(b&&a)return 0;arguments.length&&(b=this._.editable=a?a instanceof CKEDITOR.editable?a:new CKEDITOR.editable(this,a):(b&&b.detach(),null));return b};CKEDITOR.on("instanceLoaded",function(b){var d=b.editor;d.on("insertElement",function(a){a=a.data;a.type==CKEDITOR.NODE_ELEMENT&&(a.is("input")||a.is("textarea"))&&("false"!=a.getAttribute("contentEditable")&& -a.data("cke-editable",a.hasAttribute("contenteditable")?"true":"1"),a.setAttribute("contentEditable",!1))});d.on("selectionChange",function(b){if(!d.readOnly){var c=d.getSelection();c&&!c.isLocked&&(c=d.checkDirty(),d.fire("lockSnapshot"),a(b),d.fire("unlockSnapshot"),!c&&d.resetDirty())}})});CKEDITOR.on("instanceCreated",function(a){var b=a.editor;b.on("mode",function(){var a=b.editable();if(a&&a.isInline()){var d=b.title;a.changeAttr("role","textbox");a.changeAttr("aria-label",d);d&&a.changeAttr("title", -d);var c=b.fire("ariaEditorHelpLabel",{}).label;if(c&&(d=this.ui.space(this.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?"top":"contents"))){var h=CKEDITOR.tools.getNextId(),c=CKEDITOR.dom.element.createFromHtml('\x3cspan id\x3d"'+h+'" class\x3d"cke_voice_label"\x3e'+c+"\x3c/span\x3e");d.append(c);a.changeAttr("aria-describedby",h)}}})});CKEDITOR.addCss(".cke_editable{cursor:text}.cke_editable img,.cke_editable input,.cke_editable textarea{cursor:default}");var h=CKEDITOR.dom.walker.whitespaces(!0), -p=CKEDITOR.dom.walker.bookmark(!1,!0),v=CKEDITOR.dom.walker.empty(),u=CKEDITOR.dom.walker.bogus(),t=/(^|]*>)\s*<(p|div|address|h\d|center|pre)[^>]*>\s*(?:]*>| |\u00A0| )?\s*(:?<\/\2>)?\s*(?=$|<\/body>)/gi,n=function(){function a(b){return b.type==CKEDITOR.NODE_ELEMENT}function b(d,c){var h,f,e,g,k=[],n=c.range.startContainer;h=c.range.startPath();for(var n=p[n.getName()],l=0,m=d.getChildren(),t=m.count(),q=-1,x=-1,w=0,u=h.contains(p.$list);lCKEDITOR.env.version&&c.getChildCount()&&c.getFirst().remove())}return function(c){var h=c.startContainer,f=h.getAscendant("table",1),e=!1;d(f.getElementsByTag("td"));d(f.getElementsByTag("th"));f=c.clone();f.setStart(h,0);f=a(f).lastBackward();f||(f=c.clone(),f.setEndAt(h,CKEDITOR.POSITION_BEFORE_END), -f=a(f).lastForward(),e=!0);f||(f=h);f.is("table")?(c.setStartAt(f,CKEDITOR.POSITION_BEFORE_START),c.collapse(!0),f.remove()):(f.is({tbody:1,thead:1,tfoot:1})&&(f=b(f,"tr",e)),f.is("tr")&&(f=b(f,f.getParent().is("thead")?"th":"td",e)),(h=f.getBogus())&&h.remove(),c.moveToPosition(f,e?CKEDITOR.POSITION_AFTER_START:CKEDITOR.POSITION_BEFORE_END))}}(),w={eol:{detect:function(a,b){var d=a.range,c=d.clone(),h=d.clone(),f=new CKEDITOR.dom.elementPath(d.startContainer,b),e=new CKEDITOR.dom.elementPath(d.endContainer, -b);c.collapse(1);h.collapse();f.block&&c.checkBoundaryOfElement(f.block,CKEDITOR.END)&&(d.setStartAfter(f.block),a.prependEolBr=1);e.block&&h.checkBoundaryOfElement(e.block,CKEDITOR.START)&&(d.setEndBefore(e.block),a.appendEolBr=1)},fix:function(a,b){var d=b.getDocument(),c;a.appendEolBr&&(c=this.createEolBr(d),a.fragment.append(c));!a.prependEolBr||c&&!c.getPrevious()||a.fragment.append(this.createEolBr(d),1)},createEolBr:function(a){return a.createElement("br",{attributes:{"data-cke-eol":1}})}}, -bogus:{exclude:function(a){var b=a.range.getBoundaryNodes(),d=b.startNode,b=b.endNode;!b||!u(b)||d&&d.equals(b)||a.range.setEndBefore(b)}},tree:{rebuild:function(a,b){var d=a.range,c=d.getCommonAncestor(),h=new CKEDITOR.dom.elementPath(c,b),f=new CKEDITOR.dom.elementPath(d.startContainer,b),d=new CKEDITOR.dom.elementPath(d.endContainer,b),e;c.type==CKEDITOR.NODE_TEXT&&(c=c.getParent());if(h.blockLimit.is({tr:1,table:1})){var g=h.contains("table").getParent();e=function(a){return!a.equals(g)}}else if(h.block&& -h.block.is(CKEDITOR.dtd.$listItem)&&(f=f.contains(CKEDITOR.dtd.$list),d=d.contains(CKEDITOR.dtd.$list),!f.equals(d))){var k=h.contains(CKEDITOR.dtd.$list).getParent();e=function(a){return!a.equals(k)}}e||(e=function(a){return!a.equals(h.block)&&!a.equals(h.blockLimit)});this.rebuildFragment(a,b,c,e)},rebuildFragment:function(a,b,d,c){for(var h;d&&!d.equals(b)&&c(d);)h=d.clone(0,1),a.fragment.appendTo(h),a.fragment=h,d=d.getParent()}},cell:{shrink:function(a){a=a.range;var b=a.startContainer,d=a.endContainer, -c=a.startOffset,h=a.endOffset;b.type==CKEDITOR.NODE_ELEMENT&&b.equals(d)&&b.is("tr")&&++c==h&&a.shrink(CKEDITOR.SHRINK_TEXT)}}},y=function(){function a(b,d){var c=b.getParent();if(c.is(CKEDITOR.dtd.$inline))b[d?"insertBefore":"insertAfter"](c)}function b(d,c,h){a(c);a(h,1);for(var f;f=h.getNext();)f.insertAfter(c),c=f;v(d)&&d.remove()}function d(a,b){var c=new CKEDITOR.dom.range(a);c.setStartAfter(b.startNode);c.setEndBefore(b.endNode);return c}return{list:{detectMerge:function(a,b){var c=d(b,a.bookmark), -h=c.startPath(),f=c.endPath(),e=h.contains(CKEDITOR.dtd.$list),g=f.contains(CKEDITOR.dtd.$list);a.mergeList=e&&g&&e.getParent().equals(g.getParent())&&!e.equals(g);a.mergeListItems=h.block&&f.block&&h.block.is(CKEDITOR.dtd.$listItem)&&f.block.is(CKEDITOR.dtd.$listItem);if(a.mergeList||a.mergeListItems)c=c.clone(),c.setStartBefore(a.bookmark.startNode),c.setEndAfter(a.bookmark.endNode),a.mergeListBookmark=c.createBookmark()},merge:function(a,d){if(a.mergeListBookmark){var c=a.mergeListBookmark.startNode, -h=a.mergeListBookmark.endNode,f=new CKEDITOR.dom.elementPath(c,d),e=new CKEDITOR.dom.elementPath(h,d);if(a.mergeList){var g=f.contains(CKEDITOR.dtd.$list),k=e.contains(CKEDITOR.dtd.$list);g.equals(k)||(k.moveChildren(g),k.remove())}a.mergeListItems&&(f=f.contains(CKEDITOR.dtd.$listItem),e=e.contains(CKEDITOR.dtd.$listItem),f.equals(e)||b(e,c,h));c.remove();h.remove()}}},block:{detectMerge:function(a,b){if(!a.tableContentsRanges&&!a.mergeListBookmark){var d=new CKEDITOR.dom.range(b);d.setStartBefore(a.bookmark.startNode); -d.setEndAfter(a.bookmark.endNode);a.mergeBlockBookmark=d.createBookmark()}},merge:function(a,d){if(a.mergeBlockBookmark&&!a.purgeTableBookmark){var c=a.mergeBlockBookmark.startNode,h=a.mergeBlockBookmark.endNode,f=new CKEDITOR.dom.elementPath(c,d),e=new CKEDITOR.dom.elementPath(h,d),f=f.block,e=e.block;f&&e&&!f.equals(e)&&b(e,c,h);c.remove();h.remove()}}},table:function(){function a(d){var h=[],f,e=new CKEDITOR.dom.walker(d),g=d.startPath().contains(c),k=d.endPath().contains(c),p={};e.guard=function(a, -e){if(a.type==CKEDITOR.NODE_ELEMENT){var n="visited_"+(e?"out":"in");if(a.getCustomData(n))return;CKEDITOR.dom.element.setMarker(p,a,n,1)}if(e&&g&&a.equals(g))f=d.clone(),f.setEndAt(g,CKEDITOR.POSITION_BEFORE_END),h.push(f);else if(!e&&k&&a.equals(k))f=d.clone(),f.setStartAt(k,CKEDITOR.POSITION_AFTER_START),h.push(f);else{if(n=!e)n=a.type==CKEDITOR.NODE_ELEMENT&&a.is(c)&&(!g||b(a,g))&&(!k||b(a,k));n&&(f=d.clone(),f.selectNodeContents(a),h.push(f))}};e.lastForward();CKEDITOR.dom.element.clearAllMarkers(p); -return h}function b(a,d){var c=CKEDITOR.POSITION_CONTAINS+CKEDITOR.POSITION_IS_CONTAINED,h=a.getPosition(d);return h===CKEDITOR.POSITION_IDENTICAL?!1:0===(h&c)}var c={td:1,th:1,caption:1};return{detectPurge:function(a){var b=a.range,d=b.clone();d.enlarge(CKEDITOR.ENLARGE_ELEMENT);var d=new CKEDITOR.dom.walker(d),h=0;d.evaluator=function(a){a.type==CKEDITOR.NODE_ELEMENT&&a.is(c)&&++h};d.checkForward();if(1c?"getPreviousEditableNode":"getNextEditableNode"]())&&c.type==CKEDITOR.NODE_ELEMENT&&"false"==c.getAttribute("contenteditable")&& -(a.getSelection().fake(c),d.data.preventDefault(),d.cancel())}}}function v(a){for(var b=0;b=c.getLength()?g.setStartAfter(c):g.setStartBefore(c));h&&h.type==CKEDITOR.NODE_TEXT&&(e?g.setEndAfter(h):g.setEndBefore(h));c=new CKEDITOR.dom.walker(g);c.evaluator=function(c){if(c.type==CKEDITOR.NODE_ELEMENT&&c.isReadOnly()){var h=d.clone();d.setEndBefore(c);d.collapsed&&a.splice(b--,1);c.getPosition(g.endContainer)&CKEDITOR.POSITION_CONTAINS||(h.setStartAfter(c),h.collapsed||a.splice(b+1,0,h));return!0}return!1};c.next()}}return a}var u,t,n=CKEDITOR.dom.walker.invisible(1),q=function(){function a(b){return function(a){var d= -a.editor.createRange();d.moveToClosestEditablePosition(a.selected,b)&&a.editor.getSelection().selectRanges([d]);return!1}}function b(a){return function(b){var d=b.editor,c=d.createRange(),h;(h=c.moveToClosestEditablePosition(b.selected,a))||(h=c.moveToClosestEditablePosition(b.selected,!a));h&&d.getSelection().selectRanges([c]);d.fire("saveSnapshot");b.selected.remove();h||(c.moveToElementEditablePosition(d.editable()),d.getSelection().selectRanges([c]));d.fire("saveSnapshot");return!1}}var d=a(), -c=a(1);return{37:d,38:d,39:c,40:c,8:b(),46:b(1)}}();CKEDITOR.on("instanceCreated",function(b){function d(){var a=c.getSelection();a&&a.removeAllRanges()}var c=b.editor;c.on("contentDom",function(){function b(){r=new CKEDITOR.dom.selection(c.getSelection());r.lock()}function d(){f.removeListener("mouseup",d);n.removeListener("mouseup",d);var a=CKEDITOR.document.$.selection,b=a.createRange();"None"!=a.type&&b.parentElement().ownerDocument==h.$&&b.select()}var h=c.document,f=CKEDITOR.document,g=c.editable(), -k=h.getBody(),n=h.getDocumentElement(),m=g.isInline(),E,r;CKEDITOR.env.gecko&&g.attachListener(g,"focus",function(a){a.removeListener();0!==E&&(a=c.getSelection().getNative())&&a.isCollapsed&&a.anchorNode==g.$&&(a=c.createRange(),a.moveToElementEditStart(g),a.select())},null,null,-2);g.attachListener(g,CKEDITOR.env.webkit?"DOMFocusIn":"focus",function(){E&&CKEDITOR.env.webkit&&(E=c._.previousActive&&c._.previousActive.equals(h.getActive()));c.unlockSelection(E);E=0},null,null,-1);g.attachListener(g, -"mousedown",function(){E=0});if(CKEDITOR.env.ie||m)w?g.attachListener(g,"beforedeactivate",b,null,null,-1):g.attachListener(c,"selectionCheck",b,null,null,-1),g.attachListener(g,CKEDITOR.env.webkit?"DOMFocusOut":"blur",function(){c.lockSelection(r);E=1},null,null,-1),g.attachListener(g,"mousedown",function(){E=0});if(CKEDITOR.env.ie&&!m){var t;g.attachListener(g,"mousedown",function(a){2==a.data.$.button&&((a=c.document.getSelection())&&a.getType()!=CKEDITOR.SELECTION_NONE||(t=c.window.getScrollPosition()))}); -g.attachListener(g,"mouseup",function(a){2==a.data.$.button&&t&&(c.document.$.documentElement.scrollLeft=t.x,c.document.$.documentElement.scrollTop=t.y);t=null});if("BackCompat"!=h.$.compatMode){if(CKEDITOR.env.ie7Compat||CKEDITOR.env.ie6Compat)n.on("mousedown",function(a){function b(a){a=a.data.$;if(c){var d=k.$.createTextRange();try{d.moveToPoint(a.clientX,a.clientY)}catch(h){}c.setEndPoint(0>e.compareEndPoints("StartToStart",d)?"EndToEnd":"StartToStart",d);c.select()}}function d(){n.removeListener("mousemove", -b);f.removeListener("mouseup",d);n.removeListener("mouseup",d);c.select()}a=a.data;if(a.getTarget().is("html")&&a.$.yCKEDITOR.env.version)n.on("mousedown",function(a){a.data.getTarget().is("html")&&(f.on("mouseup",d),n.on("mouseup",d))})}}g.attachListener(g,"selectionchange", -a,c);g.attachListener(g,"keyup",e,c);g.attachListener(g,CKEDITOR.env.webkit?"DOMFocusIn":"focus",function(){c.forceNextSelectionCheck();c.selectionChange(1)});if(m&&(CKEDITOR.env.webkit||CKEDITOR.env.gecko)){var q;g.attachListener(g,"mousedown",function(){q=1});g.attachListener(h.getDocumentElement(),"mouseup",function(){q&&e.call(c);q=0})}else g.attachListener(CKEDITOR.env.ie?g:h.getDocumentElement(),"mouseup",e,c);CKEDITOR.env.webkit&&g.attachListener(h,"keydown",function(a){switch(a.data.getKey()){case 13:case 33:case 34:case 35:case 36:case 37:case 39:case 8:case 45:case 46:l(g)}}, -null,null,-1);g.attachListener(g,"keydown",p(c),null,null,-1)});c.on("setData",function(){c.unlockSelection();CKEDITOR.env.webkit&&d()});c.on("contentDomUnload",function(){c.unlockSelection()});if(CKEDITOR.env.ie9Compat)c.on("beforeDestroy",d,null,null,9);c.on("dataReady",function(){delete c._.fakeSelection;delete c._.hiddenSelectionContainer;c.selectionChange(1)});c.on("loadSnapshot",function(){var a=CKEDITOR.dom.walker.nodeType(CKEDITOR.NODE_ELEMENT),b=c.editable().getLast(a);b&&b.hasAttribute("data-cke-hidden-sel")&& -(b.remove(),CKEDITOR.env.gecko&&(a=c.editable().getFirst(a))&&a.is("br")&&a.getAttribute("_moz_editor_bogus_node")&&a.remove())},null,null,100);c.on("key",function(a){if("wysiwyg"==c.mode){var b=c.getSelection();if(b.isFake){var d=q[a.data.keyCode];if(d)return d({editor:c,selected:b.getSelectedElement(),selection:b,keyEvent:a})}}})});CKEDITOR.on("instanceReady",function(a){function c(){var a=f.editable();if(a&&(a=m(a))){var d=f.document.$.getSelection();"None"==d.type||d.anchorNode!=a.$&&d.focusNode!= -a.$||(g=b(d));e=a.getText();a.setText(k(e))}}function h(){var a=f.editable();a&&(a=m(a))&&(a.setText(e),g&&(d(f.document.$,g),g=null))}var f=a.editor,e,g;CKEDITOR.env.webkit&&(f.on("selectionChange",function(){var a=f.editable(),b=m(a);b&&(b.getCustomData("ready")?l(a):b.setCustomData("ready",1))},null,null,-1),f.on("beforeSetMode",function(){l(f.editable())},null,null,-1),f.on("beforeUndoImage",c),f.on("afterUndoImage",h),f.on("beforeGetData",c,null,null,0),f.on("getData",h))});CKEDITOR.editor.prototype.selectionChange= -function(b){(b?a:e).call(this)};CKEDITOR.editor.prototype.getSelection=function(a){return!this._.savedSelection&&!this._.fakeSelection||a?(a=this.editable())&&"wysiwyg"==this.mode?new CKEDITOR.dom.selection(a):null:this._.savedSelection||this._.fakeSelection};CKEDITOR.editor.prototype.lockSelection=function(a){a=a||this.getSelection(1);return a.getType()!=CKEDITOR.SELECTION_NONE?(!a.isLocked&&a.lock(),this._.savedSelection=a,!0):!1};CKEDITOR.editor.prototype.unlockSelection=function(a){var b=this._.savedSelection; -return b?(b.unlock(a),delete this._.savedSelection,!0):!1};CKEDITOR.editor.prototype.forceNextSelectionCheck=function(){delete this._.selectionPreviousPath};CKEDITOR.dom.document.prototype.getSelection=function(){return new CKEDITOR.dom.selection(this)};CKEDITOR.dom.range.prototype.select=function(){var a=this.root instanceof CKEDITOR.editable?this.root.editor.getSelection():new CKEDITOR.dom.selection(this.root);a.selectRanges([this]);return a};CKEDITOR.SELECTION_NONE=1;CKEDITOR.SELECTION_TEXT=2; -CKEDITOR.SELECTION_ELEMENT=3;var w="function"!=typeof window.getSelection,y=1;CKEDITOR.dom.selection=function(a){if(a instanceof CKEDITOR.dom.selection){var b=a;a=a.root}var d=a instanceof CKEDITOR.dom.element;this.rev=b?b.rev:y++;this.document=a instanceof CKEDITOR.dom.document?a:a.getDocument();this.root=d?a:this.document.getBody();this.isLocked=0;this._={cache:{}};if(b)return CKEDITOR.tools.extend(this._.cache,b._.cache),this.isFake=b.isFake,this.isLocked=b.isLocked,this;a=this.getNative();var c, -h;if(a)if(a.getRangeAt)c=(h=a.rangeCount&&a.getRangeAt(0))&&new CKEDITOR.dom.node(h.commonAncestorContainer);else{try{h=a.createRange()}catch(f){}c=h&&CKEDITOR.dom.element.get(h.item&&h.item(0)||h.parentElement())}if(!c||c.type!=CKEDITOR.NODE_ELEMENT&&c.type!=CKEDITOR.NODE_TEXT||!this.root.equals(c)&&!this.root.contains(c))this._.cache.type=CKEDITOR.SELECTION_NONE,this._.cache.startElement=null,this._.cache.selectedElement=null,this._.cache.selectedText="",this._.cache.ranges=new CKEDITOR.dom.rangeList; -return this};var C={img:1,hr:1,li:1,table:1,tr:1,td:1,th:1,embed:1,object:1,ol:1,ul:1,a:1,input:1,form:1,select:1,textarea:1,button:1,fieldset:1,thead:1,tfoot:1};CKEDITOR.dom.selection.prototype={getNative:function(){return void 0!==this._.cache.nativeSel?this._.cache.nativeSel:this._.cache.nativeSel=w?this.document.$.selection:this.document.getWindow().$.getSelection()},getType:w?function(){var a=this._.cache;if(a.type)return a.type;var b=CKEDITOR.SELECTION_NONE;try{var d=this.getNative(),c=d.type; -"Text"==c&&(b=CKEDITOR.SELECTION_TEXT);"Control"==c&&(b=CKEDITOR.SELECTION_ELEMENT);d.createRange().parentElement()&&(b=CKEDITOR.SELECTION_TEXT)}catch(h){}return a.type=b}:function(){var a=this._.cache;if(a.type)return a.type;var b=CKEDITOR.SELECTION_TEXT,d=this.getNative();if(!d||!d.rangeCount)b=CKEDITOR.SELECTION_NONE;else if(1==d.rangeCount){var d=d.getRangeAt(0),c=d.startContainer;c==d.endContainer&&1==c.nodeType&&1==d.endOffset-d.startOffset&&C[c.childNodes[d.startOffset].nodeName.toLowerCase()]&& -(b=CKEDITOR.SELECTION_ELEMENT)}return a.type=b},getRanges:function(){var a=w?function(){function a(b){return(new CKEDITOR.dom.node(b)).getIndex()}var b=function(b,d){b=b.duplicate();b.collapse(d);var c=b.parentElement();if(!c.hasChildNodes())return{container:c,offset:0};for(var h=c.children,f,e,g=b.duplicate(),k=0,n=h.length-1,p=-1,l,m;k<=n;)if(p=Math.floor((k+n)/2),f=h[p],g.moveToElementText(f),l=g.compareEndPoints("StartToStart",b),0l)k=p+1;else return{container:c,offset:a(f)}; -if(-1==p||p==h.length-1&&0>l){g.moveToElementText(c);g.setEndPoint("StartToStart",b);g=g.text.replace(/(\r\n|\r)/g,"\n").length;h=c.childNodes;if(!g)return f=h[h.length-1],f.nodeType!=CKEDITOR.NODE_TEXT?{container:c,offset:h.length}:{container:f,offset:f.nodeValue.length};for(c=h.length;0]+)>)|(?:!--([\S|\s]*?)--\x3e)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))/g}}, +function(){var a=/([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g,f={checked:1,compact:1,declare:1,defer:1,disabled:1,ismap:1,multiple:1,nohref:1,noresize:1,noshade:1,nowrap:1,readonly:1,selected:1};CKEDITOR.htmlParser.prototype={onTagOpen:function(){},onTagClose:function(){},onText:function(){},onCDATA:function(){},onComment:function(){},parse:function(c){for(var e,h,m=0,l;e=this._.htmlPartsRegex.exec(c);){h=e.index;if(h>m)if(m=c.substring(m,h),l)l.push(m);else this.onText(m); +m=this._.htmlPartsRegex.lastIndex;if(h=e[1])if(h=h.toLowerCase(),l&&CKEDITOR.dtd.$cdata[h]&&(this.onCDATA(l.join("")),l=null),!l){this.onTagClose(h);continue}if(l)l.push(e[0]);else if(h=e[3]){if(h=h.toLowerCase(),!/="/.test(h)){var k={},b,d=e[4];e=!!e[5];if(d)for(;b=a.exec(d);){var g=b[1].toLowerCase();b=b[2]||b[3]||b[4]||"";k[g]=!b&&f[g]?g:CKEDITOR.tools.htmlDecodeAttr(b)}this.onTagOpen(h,k,e);!l&&CKEDITOR.dtd.$cdata[h]&&(l=[])}}else if(h=e[2])this.onComment(h)}if(c.length>m)this.onText(c.substring(m, +c.length))}}}(),CKEDITOR.htmlParser.basicWriter=CKEDITOR.tools.createClass({$:function(){this._={output:[]}},proto:{openTag:function(a){this._.output.push("\x3c",a)},openTagClose:function(a,f){f?this._.output.push(" /\x3e"):this._.output.push("\x3e")},attribute:function(a,f){"string"==typeof f&&(f=CKEDITOR.tools.htmlEncodeAttr(f));this._.output.push(" ",a,'\x3d"',f,'"')},closeTag:function(a){this._.output.push("\x3c/",a,"\x3e")},text:function(a){this._.output.push(a)},comment:function(a){this._.output.push("\x3c!--", +a,"--\x3e")},write:function(a){this._.output.push(a)},reset:function(){this._.output=[];this._.indent=!1},getHtml:function(a){var f=this._.output.join("");a&&this.reset();return f}}}),"use strict",function(){CKEDITOR.htmlParser.node=function(){};CKEDITOR.htmlParser.node.prototype={remove:function(){var a=this.parent.children,f=CKEDITOR.tools.indexOf(a,this),c=this.previous,e=this.next;c&&(c.next=e);e&&(e.previous=c);a.splice(f,1);this.parent=null},replaceWith:function(a){var f=this.parent.children, +c=CKEDITOR.tools.indexOf(f,this),e=a.previous=this.previous,h=a.next=this.next;e&&(e.next=a);h&&(h.previous=a);f[c]=a;a.parent=this.parent;this.parent=null},insertAfter:function(a){var f=a.parent.children,c=CKEDITOR.tools.indexOf(f,a),e=a.next;f.splice(c+1,0,this);this.next=a.next;this.previous=a;a.next=this;e&&(e.previous=this);this.parent=a.parent},insertBefore:function(a){var f=a.parent.children,c=CKEDITOR.tools.indexOf(f,a);f.splice(c,0,this);this.next=a;(this.previous=a.previous)&&(a.previous.next= +this);a.previous=this;this.parent=a.parent},getAscendant:function(a){var f="function"==typeof a?a:"string"==typeof a?function(c){return c.name==a}:function(c){return c.name in a},c=this.parent;for(;c&&c.type==CKEDITOR.NODE_ELEMENT;){if(f(c))return c;c=c.parent}return null},wrapWith:function(a){this.replaceWith(a);a.add(this);return a},getIndex:function(){return CKEDITOR.tools.indexOf(this.parent.children,this)},getFilterContext:function(a){return a||{}}}}(),"use strict",CKEDITOR.htmlParser.comment= +function(a){this.value=a;this._={isBlockLike:!1}},CKEDITOR.htmlParser.comment.prototype=CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_COMMENT,filter:function(a,f){var c=this.value;if(!(c=a.onComment(f,c,this)))return this.remove(),!1;if("string"!=typeof c)return this.replaceWith(c),!1;this.value=c;return!0},writeHtml:function(a,f){f&&this.filter(f);a.comment(this.value)}}),"use strict",function(){CKEDITOR.htmlParser.text=function(a){this.value=a;this._={isBlockLike:!1}};CKEDITOR.htmlParser.text.prototype= +CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_TEXT,filter:function(a,f){if(!(this.value=a.onText(f,this.value,this)))return this.remove(),!1},writeHtml:function(a,f){f&&this.filter(f);a.text(this.value)}})}(),"use strict",function(){CKEDITOR.htmlParser.cdata=function(a){this.value=a};CKEDITOR.htmlParser.cdata.prototype=CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_TEXT,filter:function(){},writeHtml:function(a){a.write(this.value)}})}(),"use strict", +CKEDITOR.htmlParser.fragment=function(){this.children=[];this.parent=null;this._={isBlockLike:!0,hasInlineStarted:!1}},function(){function a(a){return a.attributes["data-cke-survive"]?!1:"a"==a.name&&a.attributes.href||CKEDITOR.dtd.$removeEmpty[a.name]}var f=CKEDITOR.tools.extend({table:1,ul:1,ol:1,dl:1},CKEDITOR.dtd.table,CKEDITOR.dtd.ul,CKEDITOR.dtd.ol,CKEDITOR.dtd.dl),c={ol:1,ul:1},e=CKEDITOR.tools.extend({},{html:1},CKEDITOR.dtd.html,CKEDITOR.dtd.body,CKEDITOR.dtd.head,{style:1,script:1}),h={ul:"li", +ol:"li",dl:"dd",table:"tbody",tbody:"tr",thead:"tr",tfoot:"tr",tr:"td"};CKEDITOR.htmlParser.fragment.fromHtml=function(m,l,k){function b(a){var b;if(0l;l++)if(f=h[l]){f= +f.exec(a,e,this);if(!1===f)return null;if(f&&f!=e)return this.onNode(a,f);if(e.parent&&!e.name)break}return e},onNode:function(a,e){var h=e.type;return h==CKEDITOR.NODE_ELEMENT?this.onElement(a,e):h==CKEDITOR.NODE_TEXT?new CKEDITOR.htmlParser.text(this.onText(a,e.value)):h==CKEDITOR.NODE_COMMENT?new CKEDITOR.htmlParser.comment(this.onComment(a,e.value)):null},onAttribute:function(a,e,h,f){return(h=this.attributesRules[h])?h.exec(a,f,e,this):f}}});CKEDITOR.htmlParser.filterRulesGroup=a;a.prototype= +{add:function(a,e,h){this.rules.splice(this.findIndex(e),0,{value:a,priority:e,options:h})},addMany:function(a,e,h){for(var f=[this.findIndex(e),0],l=0,k=a.length;l/g,"\x26gt;")+"\x3c/textarea\x3e");return"\x3ccke:encoded\x3e"+encodeURIComponent(a)+"\x3c/cke:encoded\x3e"})}function p(a){return a.replace(N,function(a,b){return decodeURIComponent(b)})}function u(a){return a.replace(/\x3c!--(?!{cke_protected})[\s\S]+?--\x3e/g, +function(a){return"\x3c!--"+w+"{C}"+encodeURIComponent(a).replace(/--/g,"%2D%2D")+"--\x3e"})}function v(a){return a.replace(/\x3c!--\{cke_protected\}\{C\}([\s\S]+?)--\x3e/g,function(a,b){return decodeURIComponent(b)})}function r(a,b){var d=b._.dataStore;return a.replace(/\x3c!--\{cke_protected\}([\s\S]+?)--\x3e/g,function(a,b){return decodeURIComponent(b)}).replace(/\{cke_protected_(\d+)\}/g,function(a,b){return d&&d[b]||""})}function n(a,b){var d=[],c=b.config.protectedSource,g=b._.dataStore||(b._.dataStore= +{id:1}),e=/<\!--\{cke_temp(comment)?\}(\d*?)--\x3e/g,c=[/|$)/gi,//gi,//gi].concat(c);a=a.replace(/\x3c!--[\s\S]*?--\x3e/g,function(a){return"\x3c!--{cke_tempcomment}"+(d.push(a)-1)+"--\x3e"});for(var h=0;h]+\s*=\s*(?:[^'"\s>]+|'[^']*'|"[^"]*"))|[^\s=\/>]+))+\s*\/?>/g,function(a){return a.replace(/\x3c!--\{cke_protected\}([^>]*)--\x3e/g,function(a,b){g[g.id]=decodeURIComponent(b);return"{cke_protected_"+g.id++ +"}"})});return a=a.replace(/<(title|iframe|textarea)([^>]*)>([\s\S]*?)<\/\1>/g,function(a,d,c,g){return"\x3c"+d+c+"\x3e"+r(v(g),b)+"\x3c/"+d+"\x3e"})}CKEDITOR.htmlDataProcessor=function(b){var c, +e,h=this;this.editor=b;this.dataFilter=c=new CKEDITOR.htmlParser.filter;this.htmlFilter=e=new CKEDITOR.htmlParser.filter;this.writer=new CKEDITOR.htmlParser.basicWriter;c.addRules(x);c.addRules(D,{applyToAll:!0});c.addRules(a(b,"data"),{applyToAll:!0});e.addRules(y);e.addRules(z,{applyToAll:!0});e.addRules(a(b,"html"),{applyToAll:!0});b.on("toHtml",function(a){a=a.data;var c=a.dataValue,e,c=n(c,b),c=g(c,I),c=d(c),c=g(c,J),c=c.replace(F,"$1cke:$2"),c=c.replace(R,"\x3ccke:$1$2\x3e\x3c/cke:$1\x3e"), +c=c.replace(/(]*>)(\r\n|\n)/g,"$1$2$2"),c=c.replace(/([^a-z0-9<\-])(on\w{3,})(?!>)/gi,"$1data-cke-"+CKEDITOR.rnd+"-$2");e=a.context||b.editable().getName();var h;CKEDITOR.env.ie&&9>CKEDITOR.env.version&&"pre"==e&&(e="div",c="\x3cpre\x3e"+c+"\x3c/pre\x3e",h=1);e=b.document.createElement(e);e.setHtml("a"+c);c=e.getHtml().substr(1);c=c.replace(new RegExp("data-cke-"+CKEDITOR.rnd+"-","ig"),"");h&&(c=c.replace(/^
|<\/pre>$/gi,""));c=c.replace(P,"$1$2");c=p(c);c=v(c);e=!1===a.fixForBody?!1:
+f(a.enterMode,b.config.autoParagraph);c=CKEDITOR.htmlParser.fragment.fromHtml(c,a.context,e);e&&(h=c,!h.children.length&&CKEDITOR.dtd[h.name][e]&&(e=new CKEDITOR.htmlParser.element(e),h.add(e)));a.dataValue=c},null,null,5);b.on("toHtml",function(a){a.data.filter.applyTo(a.data.dataValue,!0,a.data.dontFilter,a.data.enterMode)&&b.fire("dataFiltered")},null,null,6);b.on("toHtml",function(a){a.data.dataValue.filterChildren(h.dataFilter,!0)},null,null,10);b.on("toHtml",function(a){a=a.data;var b=a.dataValue,
+d=new CKEDITOR.htmlParser.basicWriter;b.writeChildrenHtml(d);b=d.getHtml(!0);a.dataValue=u(b)},null,null,15);b.on("toDataFormat",function(a){var d=a.data.dataValue;a.data.enterMode!=CKEDITOR.ENTER_BR&&(d=d.replace(/^
/i,""));a.data.dataValue=CKEDITOR.htmlParser.fragment.fromHtml(d,a.data.context,f(a.data.enterMode,b.config.autoParagraph))},null,null,5);b.on("toDataFormat",function(a){a.data.dataValue.filterChildren(h.htmlFilter,!0)},null,null,10);b.on("toDataFormat",function(a){a.data.filter.applyTo(a.data.dataValue, +!1,!0)},null,null,11);b.on("toDataFormat",function(a){var d=a.data.dataValue,c=h.writer;c.reset();d.writeChildrenHtml(c);d=c.getHtml(!0);d=v(d);d=r(d,b);a.data.dataValue=d},null,null,15)};CKEDITOR.htmlDataProcessor.prototype={toHtml:function(a,b,d,c){var g=this.editor,e,h,f,k;b&&"object"==typeof b?(e=b.context,d=b.fixForBody,c=b.dontFilter,h=b.filter,f=b.enterMode,k=b.protectedWhitespaces):e=b;e||null===e||(e=g.editable().getName());return g.fire("toHtml",{dataValue:a,context:e,fixForBody:d,dontFilter:c, +filter:h||g.filter,enterMode:f||g.enterMode,protectedWhitespaces:k}).dataValue},toDataFormat:function(a,b){var d,c,g;b&&(d=b.context,c=b.filter,g=b.enterMode);d||null===d||(d=this.editor.editable().getName());return this.editor.fire("toDataFormat",{dataValue:a,filter:c||this.editor.filter,context:d,enterMode:g||this.editor.enterMode}).dataValue}};var q=/(?: |\xa0)$/,w="{cke_protected}",A=CKEDITOR.dtd,B="caption colgroup col thead tfoot tbody".split(" "),t=CKEDITOR.tools.extend({},A.$blockLimit, +A.$block),x={elements:{input:k,textarea:k}},D={attributeNames:[[/^on/,"data-cke-pa-on"],[/^data-cke-expando$/,""]]},y={elements:{embed:function(a){var b=a.parent;if(b&&"object"==b.name){var d=b.attributes.width,b=b.attributes.height;d&&(a.attributes.width=d);b&&(a.attributes.height=b)}},a:function(a){var b=a.attributes;if(!(a.children.length||b.name||b.id||a.attributes["data-cke-saved-name"]))return!1}}},z={elementNames:[[/^cke:/,""],[/^\?xml:namespace$/,""]],attributeNames:[[/^data-cke-(saved|pa)-/, +""],[/^data-cke-.*/,""],["hidefocus",""]],elements:{$:function(a){var b=a.attributes;if(b){if(b["data-cke-temp"])return!1;for(var d=["name","href","src"],c,g=0;gc? +1:-1})},param:function(a){a.children=[];a.isEmpty=!0;return a},span:function(a){"Apple-style-span"==a.attributes["class"]&&delete a.name},html:function(a){delete a.attributes.contenteditable;delete a.attributes["class"]},body:function(a){delete a.attributes.spellcheck;delete a.attributes.contenteditable},style:function(a){var b=a.children[0];b&&b.value&&(b.value=CKEDITOR.tools.trim(b.value));a.attributes.type||(a.attributes.type="text/css")},title:function(a){var b=a.children[0];!b&&l(a,b=new CKEDITOR.htmlParser.text); +b.value=a.attributes["data-cke-title"]||""},input:b,textarea:b},attributes:{"class":function(a){return CKEDITOR.tools.ltrim(a.replace(/(?:^|\s+)cke_[^\s]*/g,""))||!1}}};CKEDITOR.env.ie&&(z.attributes.style=function(a){return a.replace(/(^|;)([^\:]+)/g,function(a){return a.toLowerCase()})});var C=/<(a|area|img|input|source)\b([^>]*)>/gi,G=/([\w-:]+)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+))/gi,E=/^(href|src|name)$/i,J=/(?:])[^>]*>[\s\S]*?<\/style>)|(?:<(:?link|meta|base)[^>]*>)/gi, +I=/(])[^>]*>)([\s\S]*?)(?:<\/textarea>)/gi,N=/([^<]*)<\/cke:encoded>/gi,F=/(<\/?)((?:object|embed|param|html|body|head|title)[^>]*>)/gi,P=/(<\/?)cke:((?:html|body|head|title)[^>]*>)/gi,R=/]*?)\/?>(?!\s*<\/cke:\1)/gi}(),"use strict",CKEDITOR.htmlParser.element=function(a,f){this.name=a;this.attributes=f||{};this.children=[];var c=a||"",e=c.match(/^cke:(.*)/);e&&(c=e[1]);c=!!(CKEDITOR.dtd.$nonBodyContent[c]||CKEDITOR.dtd.$block[c]||CKEDITOR.dtd.$listItem[c]|| +CKEDITOR.dtd.$tableContent[c]||CKEDITOR.dtd.$nonEditable[c]||"br"==c);this.isEmpty=!!CKEDITOR.dtd.$empty[a];this.isUnknown=!CKEDITOR.dtd[a];this._={isBlockLike:c,hasInlineStarted:this.isEmpty||!c}},CKEDITOR.htmlParser.cssStyle=function(a){var f={};((a instanceof CKEDITOR.htmlParser.element?a.attributes.style:a)||"").replace(/"/g,'"').replace(/\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g,function(a,e,h){"font-family"==e&&(h=h.replace(/["']/g,""));f[e.toLowerCase()]=h});return{rules:f,populate:function(a){var e= +this.toString();e&&(a instanceof CKEDITOR.dom.element?a.setAttribute("style",e):a instanceof CKEDITOR.htmlParser.element?a.attributes.style=e:a.style=e)},toString:function(){var a=[],e;for(e in f)f[e]&&a.push(e,":",f[e],";");return a.join("")}}},function(){function a(a){return function(c){return c.type==CKEDITOR.NODE_ELEMENT&&("string"==typeof a?c.name==a:c.name in a)}}var f=function(a,c){a=a[0];c=c[0];return ac?1:0},c=CKEDITOR.htmlParser.fragment.prototype;CKEDITOR.htmlParser.element.prototype= +CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_ELEMENT,add:c.add,clone:function(){return new CKEDITOR.htmlParser.element(this.name,this.attributes)},filter:function(a,c){var f=this,l,k;c=f.getFilterContext(c);if(c.off)return!0;if(!f.parent)a.onRoot(c,f);for(;;){l=f.name;if(!(k=a.onElementName(c,l)))return this.remove(),!1;f.name=k;if(!(f=a.onElement(c,f)))return this.remove(),!1;if(f!==this)return this.replaceWith(f),!1;if(f.name==l)break;if(f.type!=CKEDITOR.NODE_ELEMENT)return this.replaceWith(f), +!1;if(!f.name)return this.replaceWithChildren(),!1}l=f.attributes;var b,d;for(b in l){for(k=l[b];;)if(d=a.onAttributeName(c,b))if(d!=b)delete l[b],b=d;else break;else{delete l[b];break}d&&(!1===(k=a.onAttribute(c,f,d,k))?delete l[d]:l[d]=k)}f.isEmpty||this.filterChildren(a,!1,c);return!0},filterChildren:c.filterChildren,writeHtml:function(a,c){c&&this.filter(c);var m=this.name,l=[],k=this.attributes,b,d;a.openTag(m,k);for(b in k)l.push([b,k[b]]);a.sortAttributes&&l.sort(f);b=0;for(d=l.length;bCKEDITOR.env.version||CKEDITOR.env.quirks))this.hasFocus&&(this.focus(),b());else if(this.hasFocus)this.focus(),a();else this.once("focus", +function(){a()},null,null,-999)},getHtmlFromRange:function(a){if(a.collapsed)return new CKEDITOR.dom.documentFragment(a.document);a={doc:this.getDocument(),range:a.clone()};w.eol.detect(a,this);w.bogus.exclude(a);w.cell.shrink(a);a.fragment=a.range.cloneContents();w.tree.rebuild(a,this);w.eol.fix(a,this);return new CKEDITOR.dom.documentFragment(a.fragment.$)},extractHtmlFromRange:function(a,b){var d=A,c={range:a,doc:a.document},g=this.getHtmlFromRange(a);if(a.collapsed)return a.optimize(),g;a.enlarge(CKEDITOR.ENLARGE_INLINE, +1);d.table.detectPurge(c);c.bookmark=a.createBookmark();delete c.range;var e=this.editor.createRange();e.moveToPosition(c.bookmark.startNode,CKEDITOR.POSITION_BEFORE_START);c.targetBookmark=e.createBookmark();d.list.detectMerge(c,this);d.table.detectRanges(c,this);d.block.detectMerge(c,this);c.tableContentsRanges?(d.table.deleteRanges(c),a.moveToBookmark(c.bookmark),c.range=a):(a.moveToBookmark(c.bookmark),c.range=a,a.extractContents(d.detectExtractMerge(c)));a.moveToBookmark(c.targetBookmark);a.optimize(); +d.fixUneditableRangePosition(a);d.list.merge(c,this);d.table.purge(c,this);d.block.merge(c,this);if(b){d=a.startPath();if(c=a.checkStartOfBlock()&&a.checkEndOfBlock()&&d.block&&!a.root.equals(d.block)){a:{var c=d.block.getElementsByTag("span"),e=0,h;if(c)for(;h=c.getItem(e++);)if(!p(h)){c=!0;break a}c=!1}c=!c}c&&(a.moveToPosition(d.block,CKEDITOR.POSITION_BEFORE_START),d.block.remove())}else d.autoParagraph(this.editor,a),u(a.startContainer)&&a.startContainer.appendBogus();a.startContainer.mergeSiblings(); +return g},setup:function(){var a=this.editor;this.attachListener(a,"beforeGetData",function(){var b=this.getData();this.is("textarea")||!1!==a.config.ignoreEmptyParagraph&&(b=b.replace(r,function(a,b){return b}));a.setData(b,null,1)},this);this.attachListener(a,"getSnapshot",function(a){a.data=this.getData(1)},this);this.attachListener(a,"afterSetData",function(){this.setData(a.getData(1))},this);this.attachListener(a,"loadSnapshot",function(a){this.setData(a.data,1)},this);this.attachListener(a, +"beforeFocus",function(){var b=a.getSelection();(b=b&&b.getNative())&&"Control"==b.type||this.focus()},this);this.attachListener(a,"insertHtml",function(a){this.insertHtml(a.data.dataValue,a.data.mode,a.data.range)},this);this.attachListener(a,"insertElement",function(a){this.insertElement(a.data)},this);this.attachListener(a,"insertText",function(a){this.insertText(a.data)},this);this.setReadOnly(a.readOnly);this.attachClass("cke_editable");a.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?this.attachClass("cke_editable_inline"): +a.elementMode!=CKEDITOR.ELEMENT_MODE_REPLACE&&a.elementMode!=CKEDITOR.ELEMENT_MODE_APPENDTO||this.attachClass("cke_editable_themed");this.attachClass("cke_contents_"+a.config.contentsLangDirection);a.keystrokeHandler.blockedKeystrokes[8]=+a.readOnly;a.keystrokeHandler.attach(this);this.on("blur",function(){this.hasFocus=!1},null,null,-1);this.on("focus",function(){this.hasFocus=!0},null,null,-1);a.focusManager.add(this);this.equals(CKEDITOR.document.getActive())&&(this.hasFocus=!0,a.once("contentDom", +function(){a.focusManager.focus(this)},this));this.isInline()&&this.changeAttr("tabindex",a.tabIndex);if(!this.is("textarea")){a.document=this.getDocument();a.window=this.getWindow();var b=a.document;this.changeAttr("spellcheck",!a.config.disableNativeSpellChecker);var h=a.config.contentsLangDirection;this.getDirection(1)!=h&&this.changeAttr("dir",h);var f=CKEDITOR.getCss();f&&(h=b.getHead(),h.getCustomData("stylesheet")||(f=b.appendStyleText(f),f=new CKEDITOR.dom.element(f.ownerNode||f.owningElement), +h.setCustomData("stylesheet",f),f.data("cke-temp",1)));h=b.getCustomData("stylesheet_ref")||0;b.setCustomData("stylesheet_ref",h+1);this.setCustomData("cke_includeReadonly",!a.config.disableReadonlyStyling);this.attachListener(this,"click",function(a){a=a.data;var b=(new CKEDITOR.dom.elementPath(a.getTarget(),this)).contains("a");b&&2!=a.$.button&&b.isReadOnly()&&a.preventDefault()});var k={8:1,46:1};this.attachListener(a,"key",function(b){if(a.readOnly)return!0;var d=b.data.domEvent.getKey(),c;if(d in +k){b=a.getSelection();var e,h=b.getRanges()[0],f=h.startPath(),p,F,t,d=8==d;CKEDITOR.env.ie&&11>CKEDITOR.env.version&&(e=b.getSelectedElement())||(e=m(b))?(a.fire("saveSnapshot"),h.moveToPosition(e,CKEDITOR.POSITION_BEFORE_START),e.remove(),h.select(),a.fire("saveSnapshot"),c=1):h.collapsed&&((p=f.block)&&(t=p[d?"getPrevious":"getNext"](g))&&t.type==CKEDITOR.NODE_ELEMENT&&t.is("table")&&h[d?"checkStartOfBlock":"checkEndOfBlock"]()?(a.fire("saveSnapshot"),h[d?"checkEndOfBlock":"checkStartOfBlock"]()&& +p.remove(),h["moveToElementEdit"+(d?"End":"Start")](t),h.select(),a.fire("saveSnapshot"),c=1):f.blockLimit&&f.blockLimit.is("td")&&(F=f.blockLimit.getAscendant("table"))&&h.checkBoundaryOfElement(F,d?CKEDITOR.START:CKEDITOR.END)&&(t=F[d?"getPrevious":"getNext"](g))?(a.fire("saveSnapshot"),h["moveToElementEdit"+(d?"End":"Start")](t),h.checkStartOfBlock()&&h.checkEndOfBlock()?t.remove():h.select(),a.fire("saveSnapshot"),c=1):(F=f.contains(["td","th","caption"]))&&h.checkBoundaryOfElement(F,d?CKEDITOR.START: +CKEDITOR.END)&&(c=1))}return!c});a.blockless&&CKEDITOR.env.ie&&CKEDITOR.env.needsBrFiller&&this.attachListener(this,"keyup",function(b){b.data.getKeystroke()in k&&!this.getFirst(e)&&(this.appendBogus(),b=a.createRange(),b.moveToPosition(this,CKEDITOR.POSITION_AFTER_START),b.select())});this.attachListener(this,"dblclick",function(b){if(a.readOnly)return!1;b={element:b.data.getTarget()};a.fire("doubleclick",b)});CKEDITOR.env.ie&&this.attachListener(this,"click",c);CKEDITOR.env.ie&&!CKEDITOR.env.edge|| +this.attachListener(this,"mousedown",function(b){var d=b.data.getTarget();d.is("img","hr","input","textarea","select")&&!d.isReadOnly()&&(a.getSelection().selectElement(d),d.is("input","textarea","select")&&b.data.preventDefault())});CKEDITOR.env.edge&&this.attachListener(this,"mouseup",function(b){(b=b.data.getTarget())&&b.is("img")&&a.getSelection().selectElement(b)});CKEDITOR.env.gecko&&this.attachListener(this,"mouseup",function(b){if(2==b.data.$.button&&(b=b.data.getTarget(),!b.getOuterHtml().replace(r, +""))){var d=a.createRange();d.moveToElementEditStart(b);d.select(!0)}});CKEDITOR.env.webkit&&(this.attachListener(this,"click",function(a){a.data.getTarget().is("input","select")&&a.data.preventDefault()}),this.attachListener(this,"mouseup",function(a){a.data.getTarget().is("input","textarea")&&a.data.preventDefault()}));CKEDITOR.env.webkit&&this.attachListener(a,"key",function(b){if(a.readOnly)return!0;b=b.data.domEvent.getKey();if(b in k){var c=8==b,g=a.getSelection().getRanges()[0];b=g.startPath(); +if(g.collapsed)a:{var e=b.block;if(e&&g[c?"checkStartOfBlock":"checkEndOfBlock"]()&&g.moveToClosestEditablePosition(e,!c)&&g.collapsed){if(g.startContainer.type==CKEDITOR.NODE_ELEMENT){var h=g.startContainer.getChild(g.startOffset-(c?1:0));if(h&&h.type==CKEDITOR.NODE_ELEMENT&&h.is("hr")){a.fire("saveSnapshot");h.remove();b=!0;break a}}g=g.startPath().block;if(!g||g&&g.contains(e))b=void 0;else{a.fire("saveSnapshot");var f;(f=(c?g:e).getBogus())&&f.remove();f=a.getSelection();h=f.createBookmarks(); +(c?e:g).moveChildren(c?g:e,!1);b.lastElement.mergeSiblings();d(e,g,!c);f.selectBookmarks(h);b=!0}}else b=!1}else c=g,f=b.block,g=c.endPath().block,f&&g&&!f.equals(g)?(a.fire("saveSnapshot"),(e=f.getBogus())&&e.remove(),c.enlarge(CKEDITOR.ENLARGE_INLINE),c.deleteContents(),g.getParent()&&(g.moveChildren(f,!1),b.lastElement.mergeSiblings(),d(f,g,!0)),c=a.getSelection().getRanges()[0],c.collapse(1),c.optimize(),""===c.startContainer.getHtml()&&c.startContainer.appendBogus(),c.select(),b=!0):b=!1;if(!b)return; +a.getSelection().scrollIntoView();a.fire("saveSnapshot");return!1}},this,null,100)}}},_:{detach:function(){this.editor.setData(this.editor.getData(),0,1);this.clearListeners();this.restoreAttrs();var a;if(a=this.removeCustomData("classes"))for(;a.length;)this.removeClass(a.pop());if(!this.is("textarea")){a=this.getDocument();var b=a.getHead();if(b.getCustomData("stylesheet")){var d=a.getCustomData("stylesheet_ref");--d?a.setCustomData("stylesheet_ref",d):(a.removeCustomData("stylesheet_ref"),b.removeCustomData("stylesheet").remove())}}this.editor.fire("contentDomUnload"); +delete this.editor}}});CKEDITOR.editor.prototype.editable=function(a){var b=this._.editable;if(b&&a)return 0;arguments.length&&(b=this._.editable=a?a instanceof CKEDITOR.editable?a:new CKEDITOR.editable(this,a):(b&&b.detach(),null));return b};CKEDITOR.on("instanceLoaded",function(b){var d=b.editor;d.on("insertElement",function(a){a=a.data;a.type==CKEDITOR.NODE_ELEMENT&&(a.is("input")||a.is("textarea"))&&("false"!=a.getAttribute("contentEditable")&&a.data("cke-editable",a.hasAttribute("contenteditable")? +"true":"1"),a.setAttribute("contentEditable",!1))});d.on("selectionChange",function(b){if(!d.readOnly){var c=d.getSelection();c&&!c.isLocked&&(c=d.checkDirty(),d.fire("lockSnapshot"),a(b),d.fire("unlockSnapshot"),!c&&d.resetDirty())}})});CKEDITOR.on("instanceCreated",function(a){var b=a.editor;b.on("mode",function(){var a=b.editable();if(a&&a.isInline()){var d=b.title;a.changeAttr("role","textbox");a.changeAttr("aria-label",d);d&&a.changeAttr("title",d);var c=b.fire("ariaEditorHelpLabel",{}).label; +if(c&&(d=this.ui.space(this.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?"top":"contents"))){var g=CKEDITOR.tools.getNextId(),c=CKEDITOR.dom.element.createFromHtml('\x3cspan id\x3d"'+g+'" class\x3d"cke_voice_label"\x3e'+c+"\x3c/span\x3e");d.append(c);a.changeAttr("aria-describedby",g)}}})});CKEDITOR.addCss(".cke_editable{cursor:text}.cke_editable img,.cke_editable input,.cke_editable textarea{cursor:default}");var g=CKEDITOR.dom.walker.whitespaces(!0),p=CKEDITOR.dom.walker.bookmark(!1,!0),u=CKEDITOR.dom.walker.empty(), +v=CKEDITOR.dom.walker.bogus(),r=/(^|]*>)\s*<(p|div|address|h\d|center|pre)[^>]*>\s*(?:]*>| |\u00A0| )?\s*(:?<\/\2>)?\s*(?=$|<\/body>)/gi,n=function(){function a(b){return b.type==CKEDITOR.NODE_ELEMENT}function b(d,c){var g,e,h,f,k=[],l=c.range.startContainer;g=c.range.startPath();for(var l=p[l.getName()],n=0,m=d.getChildren(),r=m.count(),q=-1,x=-1,w=0,u=g.contains(p.$list);nCKEDITOR.env.version&&c.getChildCount()&&c.getFirst().remove())}return function(c){var g=c.startContainer,e=g.getAscendant("table",1),h=!1;d(e.getElementsByTag("td"));d(e.getElementsByTag("th"));e=c.clone();e.setStart(g,0);e=a(e).lastBackward();e||(e=c.clone(),e.setEndAt(g,CKEDITOR.POSITION_BEFORE_END),e=a(e).lastForward(),h=!0);e||(e=g);e.is("table")?(c.setStartAt(e,CKEDITOR.POSITION_BEFORE_START), +c.collapse(!0),e.remove()):(e.is({tbody:1,thead:1,tfoot:1})&&(e=b(e,"tr",h)),e.is("tr")&&(e=b(e,e.getParent().is("thead")?"th":"td",h)),(g=e.getBogus())&&g.remove(),c.moveToPosition(e,h?CKEDITOR.POSITION_AFTER_START:CKEDITOR.POSITION_BEFORE_END))}}(),w={eol:{detect:function(a,b){var d=a.range,c=d.clone(),g=d.clone(),e=new CKEDITOR.dom.elementPath(d.startContainer,b),h=new CKEDITOR.dom.elementPath(d.endContainer,b);c.collapse(1);g.collapse();e.block&&c.checkBoundaryOfElement(e.block,CKEDITOR.END)&& +(d.setStartAfter(e.block),a.prependEolBr=1);h.block&&g.checkBoundaryOfElement(h.block,CKEDITOR.START)&&(d.setEndBefore(h.block),a.appendEolBr=1)},fix:function(a,b){var d=b.getDocument(),c;a.appendEolBr&&(c=this.createEolBr(d),a.fragment.append(c));!a.prependEolBr||c&&!c.getPrevious()||a.fragment.append(this.createEolBr(d),1)},createEolBr:function(a){return a.createElement("br",{attributes:{"data-cke-eol":1}})}},bogus:{exclude:function(a){var b=a.range.getBoundaryNodes(),d=b.startNode,b=b.endNode; +!b||!v(b)||d&&d.equals(b)||a.range.setEndBefore(b)}},tree:{rebuild:function(a,b){var d=a.range,c=d.getCommonAncestor(),g=new CKEDITOR.dom.elementPath(c,b),e=new CKEDITOR.dom.elementPath(d.startContainer,b),d=new CKEDITOR.dom.elementPath(d.endContainer,b),h;c.type==CKEDITOR.NODE_TEXT&&(c=c.getParent());if(g.blockLimit.is({tr:1,table:1})){var f=g.contains("table").getParent();h=function(a){return!a.equals(f)}}else if(g.block&&g.block.is(CKEDITOR.dtd.$listItem)&&(e=e.contains(CKEDITOR.dtd.$list),d=d.contains(CKEDITOR.dtd.$list), +!e.equals(d))){var k=g.contains(CKEDITOR.dtd.$list).getParent();h=function(a){return!a.equals(k)}}h||(h=function(a){return!a.equals(g.block)&&!a.equals(g.blockLimit)});this.rebuildFragment(a,b,c,h)},rebuildFragment:function(a,b,d,c){for(var g;d&&!d.equals(b)&&c(d);)g=d.clone(0,1),a.fragment.appendTo(g),a.fragment=g,d=d.getParent()}},cell:{shrink:function(a){a=a.range;var b=a.startContainer,d=a.endContainer,c=a.startOffset,g=a.endOffset;b.type==CKEDITOR.NODE_ELEMENT&&b.equals(d)&&b.is("tr")&&++c== +g&&a.shrink(CKEDITOR.SHRINK_TEXT)}}},A=function(){function a(b,d){var c=b.getParent();if(c.is(CKEDITOR.dtd.$inline))b[d?"insertBefore":"insertAfter"](c)}function b(d,c,g){a(c);a(g,1);for(var e;e=g.getNext();)e.insertAfter(c),c=e;u(d)&&d.remove()}function d(a,b){var c=new CKEDITOR.dom.range(a);c.setStartAfter(b.startNode);c.setEndBefore(b.endNode);return c}return{list:{detectMerge:function(a,b){var c=d(b,a.bookmark),g=c.startPath(),e=c.endPath(),h=g.contains(CKEDITOR.dtd.$list),f=e.contains(CKEDITOR.dtd.$list); +a.mergeList=h&&f&&h.getParent().equals(f.getParent())&&!h.equals(f);a.mergeListItems=g.block&&e.block&&g.block.is(CKEDITOR.dtd.$listItem)&&e.block.is(CKEDITOR.dtd.$listItem);if(a.mergeList||a.mergeListItems)c=c.clone(),c.setStartBefore(a.bookmark.startNode),c.setEndAfter(a.bookmark.endNode),a.mergeListBookmark=c.createBookmark()},merge:function(a,d){if(a.mergeListBookmark){var c=a.mergeListBookmark.startNode,g=a.mergeListBookmark.endNode,e=new CKEDITOR.dom.elementPath(c,d),h=new CKEDITOR.dom.elementPath(g, +d);if(a.mergeList){var f=e.contains(CKEDITOR.dtd.$list),k=h.contains(CKEDITOR.dtd.$list);f.equals(k)||(k.moveChildren(f),k.remove())}a.mergeListItems&&(e=e.contains(CKEDITOR.dtd.$listItem),h=h.contains(CKEDITOR.dtd.$listItem),e.equals(h)||b(h,c,g));c.remove();g.remove()}}},block:{detectMerge:function(a,b){if(!a.tableContentsRanges&&!a.mergeListBookmark){var d=new CKEDITOR.dom.range(b);d.setStartBefore(a.bookmark.startNode);d.setEndAfter(a.bookmark.endNode);a.mergeBlockBookmark=d.createBookmark()}}, +merge:function(a,d){if(a.mergeBlockBookmark&&!a.purgeTableBookmark){var c=a.mergeBlockBookmark.startNode,g=a.mergeBlockBookmark.endNode,e=new CKEDITOR.dom.elementPath(c,d),h=new CKEDITOR.dom.elementPath(g,d),e=e.block,h=h.block;e&&h&&!e.equals(h)&&b(h,c,g);c.remove();g.remove()}}},table:function(){function a(d){var g=[],e,h=new CKEDITOR.dom.walker(d),f=d.startPath().contains(c),k=d.endPath().contains(c),p={};h.guard=function(a,h){if(a.type==CKEDITOR.NODE_ELEMENT){var n="visited_"+(h?"out":"in");if(a.getCustomData(n))return; +CKEDITOR.dom.element.setMarker(p,a,n,1)}if(h&&f&&a.equals(f))e=d.clone(),e.setEndAt(f,CKEDITOR.POSITION_BEFORE_END),g.push(e);else if(!h&&k&&a.equals(k))e=d.clone(),e.setStartAt(k,CKEDITOR.POSITION_AFTER_START),g.push(e);else{if(n=!h)n=a.type==CKEDITOR.NODE_ELEMENT&&a.is(c)&&(!f||b(a,f))&&(!k||b(a,k));n&&(e=d.clone(),e.selectNodeContents(a),g.push(e))}};h.lastForward();CKEDITOR.dom.element.clearAllMarkers(p);return g}function b(a,d){var c=CKEDITOR.POSITION_CONTAINS+CKEDITOR.POSITION_IS_CONTAINED, +g=a.getPosition(d);return g===CKEDITOR.POSITION_IDENTICAL?!1:0===(g&c)}var c={td:1,th:1,caption:1};return{detectPurge:function(a){var b=a.range,d=b.clone();d.enlarge(CKEDITOR.ENLARGE_ELEMENT);var d=new CKEDITOR.dom.walker(d),g=0;d.evaluator=function(a){a.type==CKEDITOR.NODE_ELEMENT&&a.is(c)&&++g};d.checkForward();if(1e&&g&&g.intersectsNode(d.$)){var h=[{node:c.anchorNode,offset:c.anchorOffset},{node:c.focusNode,offset:c.focusOffset}];c.anchorNode==d.$&&c.anchorOffset>e&&(h[0].offset-=e);c.focusNode==d.$&&c.focusOffset>e&&(h[1].offset-=e)}}d.setText(k(d.getText(),1));h&&(d=a.getDocument().$,c=d.getSelection(),d=d.createRange(),d.setStart(h[0].node,h[0].offset),d.collapse(!0),c.removeAllRanges(), +c.addRange(d),c.extend(h[1].node,h[1].offset))}}function k(a,b){return b?a.replace(B,function(a,b){return b?" ":""}):a.replace(A,"")}function b(a){var b=CKEDITOR.dom.element.createFromHtml('\x3cdiv data-cke-hidden-sel\x3d"1" data-cke-temp\x3d"1" style\x3d"'+(CKEDITOR.env.ie?"display:none":"position:fixed;top:0;left:-1000px")+'"\x3e\x26nbsp;\x3c/div\x3e',a.document);a.fire("lockSnapshot");a.editable().append(b);var d=a.getSelection(1),c=a.createRange(),g=d.root.on("selectionchange",function(a){a.cancel()}, +null,null,0);c.setStartAt(b,CKEDITOR.POSITION_AFTER_START);c.setEndAt(b,CKEDITOR.POSITION_BEFORE_END);d.selectRanges([c]);g.removeListener();a.fire("unlockSnapshot");a._.hiddenSelectionContainer=b}function d(a){var b={37:1,39:1,8:1,46:1};return function(d){var c=d.data.getKeystroke();if(b[c]){var g=a.getSelection().getRanges(),e=g[0];1==g.length&&e.collapsed&&(c=e[38>c?"getPreviousEditableNode":"getNextEditableNode"]())&&c.type==CKEDITOR.NODE_ELEMENT&&"false"==c.getAttribute("contenteditable")&&(a.getSelection().fake(c), +d.data.preventDefault(),d.cancel())}}}function g(a){for(var b=0;b=c.getLength()? +f.setStartAfter(c):f.setStartBefore(c));g&&g.type==CKEDITOR.NODE_TEXT&&(h?f.setEndAfter(g):f.setEndBefore(g));c=new CKEDITOR.dom.walker(f);c.evaluator=function(c){if(c.type==CKEDITOR.NODE_ELEMENT&&c.isReadOnly()){var g=d.clone();d.setEndBefore(c);d.collapsed&&a.splice(b--,1);c.getPosition(f.endContainer)&CKEDITOR.POSITION_CONTAINS||(g.setStartAfter(c),g.collapsed||a.splice(b+1,0,g));return!0}return!1};c.next()}}return a}var p,u,v=CKEDITOR.dom.walker.invisible(1),r=function(){function a(b){return function(a){var d= +a.editor.createRange();d.moveToClosestEditablePosition(a.selected,b)&&a.editor.getSelection().selectRanges([d]);return!1}}function b(a){return function(b){var d=b.editor,c=d.createRange(),g;(g=c.moveToClosestEditablePosition(b.selected,a))||(g=c.moveToClosestEditablePosition(b.selected,!a));g&&d.getSelection().selectRanges([c]);d.fire("saveSnapshot");b.selected.remove();g||(c.moveToElementEditablePosition(d.editable()),d.getSelection().selectRanges([c]));d.fire("saveSnapshot");return!1}}var d=a(), +c=a(1);return{37:d,38:d,39:c,40:c,8:b(),46:b(1)}}();CKEDITOR.on("instanceCreated",function(b){function c(){var a=g.getSelection();a&&a.removeAllRanges()}var g=b.editor;g.on("contentDom",function(){function b(){r=new CKEDITOR.dom.selection(g.getSelection());r.lock()}function c(){h.removeListener("mouseup",c);m.removeListener("mouseup",c);var a=CKEDITOR.document.$.selection,b=a.createRange();"None"!=a.type&&b.parentElement().ownerDocument==e.$&&b.select()}var e=g.document,h=CKEDITOR.document,k=g.editable(), +p=e.getBody(),m=e.getDocumentElement(),t=k.isInline(),F,r;CKEDITOR.env.gecko&&k.attachListener(k,"focus",function(a){a.removeListener();0!==F&&(a=g.getSelection().getNative())&&a.isCollapsed&&a.anchorNode==k.$&&(a=g.createRange(),a.moveToElementEditStart(k),a.select())},null,null,-2);k.attachListener(k,CKEDITOR.env.webkit?"DOMFocusIn":"focus",function(){F&&CKEDITOR.env.webkit&&(F=g._.previousActive&&g._.previousActive.equals(e.getActive()));g.unlockSelection(F);F=0},null,null,-1);k.attachListener(k, +"mousedown",function(){F=0});if(CKEDITOR.env.ie||t)n?k.attachListener(k,"beforedeactivate",b,null,null,-1):k.attachListener(g,"selectionCheck",b,null,null,-1),k.attachListener(k,CKEDITOR.env.webkit?"DOMFocusOut":"blur",function(){g.lockSelection(r);F=1},null,null,-1),k.attachListener(k,"mousedown",function(){F=0});if(CKEDITOR.env.ie&&!t){var q;k.attachListener(k,"mousedown",function(a){2==a.data.$.button&&((a=g.document.getSelection())&&a.getType()!=CKEDITOR.SELECTION_NONE||(q=g.window.getScrollPosition()))}); +k.attachListener(k,"mouseup",function(a){2==a.data.$.button&&q&&(g.document.$.documentElement.scrollLeft=q.x,g.document.$.documentElement.scrollTop=q.y);q=null});if("BackCompat"!=e.$.compatMode){if(CKEDITOR.env.ie7Compat||CKEDITOR.env.ie6Compat)m.on("mousedown",function(a){function b(a){a=a.data.$;if(c){var d=p.$.createTextRange();try{d.moveToPoint(a.clientX,a.clientY)}catch(g){}c.setEndPoint(0>e.compareEndPoints("StartToStart",d)?"EndToEnd":"StartToStart",d);c.select()}}function d(){m.removeListener("mousemove", +b);h.removeListener("mouseup",d);m.removeListener("mouseup",d);c.select()}a=a.data;if(a.getTarget().is("html")&&a.$.yCKEDITOR.env.version)m.on("mousedown",function(a){a.data.getTarget().is("html")&&(h.on("mouseup",c),m.on("mouseup",c))})}}k.attachListener(k,"selectionchange", +a,g);k.attachListener(k,"keyup",f,g);k.attachListener(k,CKEDITOR.env.webkit?"DOMFocusIn":"focus",function(){g.forceNextSelectionCheck();g.selectionChange(1)});if(t&&(CKEDITOR.env.webkit||CKEDITOR.env.gecko)){var w;k.attachListener(k,"mousedown",function(){w=1});k.attachListener(e.getDocumentElement(),"mouseup",function(){w&&f.call(g);w=0})}else k.attachListener(CKEDITOR.env.ie?k:e.getDocumentElement(),"mouseup",f,g);CKEDITOR.env.webkit&&k.attachListener(e,"keydown",function(a){switch(a.data.getKey()){case 13:case 33:case 34:case 35:case 36:case 37:case 39:case 8:case 45:case 46:l(k)}}, +null,null,-1);k.attachListener(k,"keydown",d(g),null,null,-1)});g.on("setData",function(){g.unlockSelection();CKEDITOR.env.webkit&&c()});g.on("contentDomUnload",function(){g.unlockSelection()});if(CKEDITOR.env.ie9Compat)g.on("beforeDestroy",c,null,null,9);g.on("dataReady",function(){delete g._.fakeSelection;delete g._.hiddenSelectionContainer;g.selectionChange(1)});g.on("loadSnapshot",function(){var a=CKEDITOR.dom.walker.nodeType(CKEDITOR.NODE_ELEMENT),b=g.editable().getLast(a);b&&b.hasAttribute("data-cke-hidden-sel")&& +(b.remove(),CKEDITOR.env.gecko&&(a=g.editable().getFirst(a))&&a.is("br")&&a.getAttribute("_moz_editor_bogus_node")&&a.remove())},null,null,100);g.on("key",function(a){if("wysiwyg"==g.mode){var b=g.getSelection();if(b.isFake){var d=r[a.data.keyCode];if(d)return d({editor:g,selected:b.getSelectedElement(),selection:b,keyEvent:a})}}})});if(CKEDITOR.env.webkit)CKEDITOR.on("instanceReady",function(a){var b=a.editor;b.on("selectionChange",function(){var a=b.editable(),d=a.getCustomData("cke-fillingChar"); +d&&(d.getCustomData("ready")?l(a):d.setCustomData("ready",1))},null,null,-1);b.on("beforeSetMode",function(){l(b.editable())},null,null,-1);b.on("getSnapshot",function(a){a.data&&(a.data=k(a.data))},b,null,20);b.on("toDataFormat",function(a){a.data.dataValue=k(a.data.dataValue)},null,null,0)});CKEDITOR.editor.prototype.selectionChange=function(b){(b?a:f).call(this)};CKEDITOR.editor.prototype.getSelection=function(a){return!this._.savedSelection&&!this._.fakeSelection||a?(a=this.editable())&&"wysiwyg"== +this.mode?new CKEDITOR.dom.selection(a):null:this._.savedSelection||this._.fakeSelection};CKEDITOR.editor.prototype.lockSelection=function(a){a=a||this.getSelection(1);return a.getType()!=CKEDITOR.SELECTION_NONE?(!a.isLocked&&a.lock(),this._.savedSelection=a,!0):!1};CKEDITOR.editor.prototype.unlockSelection=function(a){var b=this._.savedSelection;return b?(b.unlock(a),delete this._.savedSelection,!0):!1};CKEDITOR.editor.prototype.forceNextSelectionCheck=function(){delete this._.selectionPreviousPath}; +CKEDITOR.dom.document.prototype.getSelection=function(){return new CKEDITOR.dom.selection(this)};CKEDITOR.dom.range.prototype.select=function(){var a=this.root instanceof CKEDITOR.editable?this.root.editor.getSelection():new CKEDITOR.dom.selection(this.root);a.selectRanges([this]);return a};CKEDITOR.SELECTION_NONE=1;CKEDITOR.SELECTION_TEXT=2;CKEDITOR.SELECTION_ELEMENT=3;var n="function"!=typeof window.getSelection,q=1;CKEDITOR.dom.selection=function(a){if(a instanceof CKEDITOR.dom.selection){var b= +a;a=a.root}var d=a instanceof CKEDITOR.dom.element;this.rev=b?b.rev:q++;this.document=a instanceof CKEDITOR.dom.document?a:a.getDocument();this.root=d?a:this.document.getBody();this.isLocked=0;this._={cache:{}};if(b)return CKEDITOR.tools.extend(this._.cache,b._.cache),this.isFake=b.isFake,this.isLocked=b.isLocked,this;a=this.getNative();var c,g;if(a)if(a.getRangeAt)c=(g=a.rangeCount&&a.getRangeAt(0))&&new CKEDITOR.dom.node(g.commonAncestorContainer);else{try{g=a.createRange()}catch(e){}c=g&&CKEDITOR.dom.element.get(g.item&& +g.item(0)||g.parentElement())}if(!c||c.type!=CKEDITOR.NODE_ELEMENT&&c.type!=CKEDITOR.NODE_TEXT||!this.root.equals(c)&&!this.root.contains(c))this._.cache.type=CKEDITOR.SELECTION_NONE,this._.cache.startElement=null,this._.cache.selectedElement=null,this._.cache.selectedText="",this._.cache.ranges=new CKEDITOR.dom.rangeList;return this};var w={img:1,hr:1,li:1,table:1,tr:1,td:1,th:1,embed:1,object:1,ol:1,ul:1,a:1,input:1,form:1,select:1,textarea:1,button:1,fieldset:1,thead:1,tfoot:1},A=CKEDITOR.tools.repeat("​", +7),B=new RegExp(A+"( )?","g");CKEDITOR.tools.extend(CKEDITOR.dom.selection,{_removeFillingCharSequenceString:k,_createFillingCharSequenceNode:m,FILLING_CHAR_SEQUENCE:A});CKEDITOR.dom.selection.prototype={getNative:function(){return void 0!==this._.cache.nativeSel?this._.cache.nativeSel:this._.cache.nativeSel=n?this.document.$.selection:this.document.getWindow().$.getSelection()},getType:n?function(){var a=this._.cache;if(a.type)return a.type;var b=CKEDITOR.SELECTION_NONE;try{var d=this.getNative(), +c=d.type;"Text"==c&&(b=CKEDITOR.SELECTION_TEXT);"Control"==c&&(b=CKEDITOR.SELECTION_ELEMENT);d.createRange().parentElement()&&(b=CKEDITOR.SELECTION_TEXT)}catch(g){}return a.type=b}:function(){var a=this._.cache;if(a.type)return a.type;var b=CKEDITOR.SELECTION_TEXT,d=this.getNative();if(!d||!d.rangeCount)b=CKEDITOR.SELECTION_NONE;else if(1==d.rangeCount){var d=d.getRangeAt(0),c=d.startContainer;c==d.endContainer&&1==c.nodeType&&1==d.endOffset-d.startOffset&&w[c.childNodes[d.startOffset].nodeName.toLowerCase()]&& +(b=CKEDITOR.SELECTION_ELEMENT)}return a.type=b},getRanges:function(){var a=n?function(){function a(b){return(new CKEDITOR.dom.node(b)).getIndex()}var b=function(b,d){b=b.duplicate();b.collapse(d);var c=b.parentElement();if(!c.hasChildNodes())return{container:c,offset:0};for(var g=c.children,e,h,f=b.duplicate(),k=0,p=g.length-1,n=-1,l,m;k<=p;)if(n=Math.floor((k+p)/2),e=g[n],f.moveToElementText(e),l=f.compareEndPoints("StartToStart",b),0l)k=n+1;else return{container:c,offset:a(e)}; +if(-1==n||n==g.length-1&&0>l){f.moveToElementText(c);f.setEndPoint("StartToStart",b);f=f.text.replace(/(\r\n|\r)/g,"\n").length;g=c.childNodes;if(!f)return e=g[g.length-1],e.nodeType!=CKEDITOR.NODE_TEXT?{container:c,offset:g.length}:{container:e,offset:e.nodeValue.length};for(c=g.length;0]*>)[ \t\r\n]*/gi,"$1");e=e.replace(/([ \t\n\r]+| )/g," ");e=e.replace(/]*>/gi,"\n");if(CKEDITOR.env.ie){var g=a.getDocument().createElement("div");g.append(f);f.$.outerHTML="\x3cpre\x3e"+e+"\x3c/pre\x3e";f.copyAttributes(g.getFirst());f=g.getFirst().remove()}else f.setHtml(e);b=f}else e?b=v(d? -[a.getHtml()]:h(a),b):a.moveChildren(b);b.replace(a);if(c){var d=b,k;(k=d.getPrevious(J))&&k.type==CKEDITOR.NODE_ELEMENT&&k.is("pre")&&(c=p(k.getHtml(),/\n$/,"")+"\n\n"+p(d.getHtml(),/^\n/,""),CKEDITOR.env.ie?d.$.outerHTML="\x3cpre\x3e"+c+"\x3c/pre\x3e":d.setHtml(c),k.remove())}else d&&q(b)}function h(a){var b=[];p(a.getOuterHtml(),/(\S\s*)\n(?:\s|(]+data-cke-bookmark.*?\/span>))*\n(?!$)/gi,function(a,b,d){return b+"\x3c/pre\x3e"+d+"\x3cpre\x3e"}).replace(/([\s\S]*?)<\/pre>/gi,function(a, -d){b.push(d)});return b}function p(a,b,d){var c="",h="";a=a.replace(/(^]+data-cke-bookmark.*?\/span>)|(]+data-cke-bookmark.*?\/span>$)/gi,function(a,b,d){b&&(c=b);d&&(h=d);return""});return c+a.replace(b,d)+h}function v(a,b){var d;1=g?(m=f.createText(""), -m.insertAfter(this)):(a=f.createText(""),a.insertAfter(m),a.remove()));return m},substring:function(a,e){return"number"!=typeof e?this.$.nodeValue.substr(a):this.$.nodeValue.substring(a,e)}}),function(){function a(a,e,f){var m=a.serializable,l=e[f?"endContainer":"startContainer"],k=f?"endOffset":"startOffset",b=m?e.document.getById(a.startNode):a.startNode;a=m?e.document.getById(a.endNode):a.endNode;l.equals(b.getPrevious())?(e.startOffset=e.startOffset-l.getLength()-a.getPrevious().getLength(),l= -a.getNext()):l.equals(a.getPrevious())&&(e.startOffset-=l.getLength(),l=a.getNext());l.equals(b.getParent())&&e[k]++;l.equals(a.getParent())&&e[k]++;e[f?"endContainer":"startContainer"]=l;return e}CKEDITOR.dom.rangeList=function(a){if(a instanceof CKEDITOR.dom.rangeList)return a;a?a instanceof CKEDITOR.dom.range&&(a=[a]):a=[];return CKEDITOR.tools.extend(a,e)};var e={createIterator:function(){var a=this,e=CKEDITOR.dom.walker.bookmark(),f=[],m;return{getNextRange:function(l){m=void 0===m?0:m+1;var k= -a[m];if(k&&1b?-1:1}),e=0,g;eCKEDITOR.env.version?a[k].$.styleSheet.cssText+=g:a[k].$.innerHTML+=g}}var m={};CKEDITOR.skin={path:a,loadPart:function(b,h){CKEDITOR.skin.name!=CKEDITOR.skinName.split(",")[0]? -CKEDITOR.scriptLoader.load(CKEDITOR.getUrl(a()+"skin.js"),function(){c(b,h)}):c(b,h)},getPath:function(a){return CKEDITOR.getUrl(e(a))},icons:{},addIcon:function(a,b,c,f){a=a.toLowerCase();this.icons[a]||(this.icons[a]={path:b,offset:c||0,bgsize:f||"16px"})},getIconStyle:function(a,b,c,f,e){var g;a&&(a=a.toLowerCase(),b&&(g=this.icons[a+"-rtl"]),g||(g=this.icons[a]));a=c||g&&g.path||"";f=f||g&&g.offset;e=e||g&&g.bgsize||"16px";a&&(a=a.replace(/'/g,"\\'"));return a&&"background-image:url('"+CKEDITOR.getUrl(a)+ -"');background-position:0 "+f+"px;background-size:"+e+";"}};CKEDITOR.tools.extend(CKEDITOR.editor.prototype,{getUiColor:function(){return this.uiColor},setUiColor:function(a){var c=g(CKEDITOR.document);return(this.setUiColor=function(a){this.uiColor=a;var d=CKEDITOR.skin.chameleon,e="",g="";"function"==typeof d&&(e=d(this,"editor"),g=d(this,"panel"));a=[[b,a]];f([c],e,a);f(k,g,a)}).call(this,a)}});var l="cke_ui_color",k=[],b=/\$color/g;CKEDITOR.on("instanceLoaded",function(a){if(!CKEDITOR.env.ie|| -!CKEDITOR.env.quirks){var c=a.editor;a=function(a){a=(a.data[0]||a.data).element.getElementsByTag("iframe").getItem(0).getFrameDocument();if(!a.getById("cke_ui_color")){a=g(a);k.push(a);var d=c.getUiColor();d&&f([a],CKEDITOR.skin.chameleon(c,"panel"),[[b,d]])}};c.on("panelShow",a);c.on("menuShow",a);c.config.uiColor&&c.setUiColor(c.config.uiColor)}})}(),function(){if(CKEDITOR.env.webkit)CKEDITOR.env.hc=!1;else{var a=CKEDITOR.dom.element.createFromHtml('\x3cdiv style\x3d"width:0;height:0;position:absolute;left:-10000px;border:1px solid;border-color:red blue"\x3e\x3c/div\x3e', -CKEDITOR.document);a.appendTo(CKEDITOR.document.getHead());try{var e=a.getComputedStyle("border-top-color"),c=a.getComputedStyle("border-right-color");CKEDITOR.env.hc=!(!e||e!=c)}catch(g){CKEDITOR.env.hc=!1}a.remove()}CKEDITOR.env.hc&&(CKEDITOR.env.cssClass+=" cke_hc");CKEDITOR.document.appendStyleText(".cke{visibility:hidden;}");CKEDITOR.status="loaded";CKEDITOR.fireOnce("loaded");if(a=CKEDITOR._.pending)for(delete CKEDITOR._.pending,e=0;el;l++){var k=l,b;b=parseInt(e[l],16);b=("0"+(0>c?0|b*(1+c):0|b+(255-b)*c).toString(16)).slice(-2);e[k]=b}return"#"+e.join("")}}(),e=function(){var a=new CKEDITOR.template("background:#{to};background-image:linear-gradient(to bottom,{from},{to});filter:progid:DXImageTransform.Microsoft.gradient(gradientType\x3d0,startColorstr\x3d'{from}',endColorstr\x3d'{to}');"); -return function(c,e){return a.output({from:c,to:e})}}(),c={editor:new CKEDITOR.template("{id}.cke_chrome [border-color:{defaultBorder};] {id} .cke_top [ {defaultGradient}border-bottom-color:{defaultBorder};] {id} .cke_bottom [{defaultGradient}border-top-color:{defaultBorder};] {id} .cke_resizer [border-right-color:{ckeResizer}] {id} .cke_dialog_title [{defaultGradient}border-bottom-color:{defaultBorder};] {id} .cke_dialog_footer [{defaultGradient}outline-color:{defaultBorder};border-top-color:{defaultBorder};] {id} .cke_dialog_tab [{lightGradient}border-color:{defaultBorder};] {id} .cke_dialog_tab:hover [{mediumGradient}] {id} .cke_dialog_contents [border-top-color:{defaultBorder};] {id} .cke_dialog_tab_selected, {id} .cke_dialog_tab_selected:hover [background:{dialogTabSelected};border-bottom-color:{dialogTabSelectedBorder};] {id} .cke_dialog_body [background:{dialogBody};border-color:{defaultBorder};] {id} .cke_toolgroup [{lightGradient}border-color:{defaultBorder};] {id} a.cke_button_off:hover, {id} a.cke_button_off:focus, {id} a.cke_button_off:active [{mediumGradient}] {id} .cke_button_on [{ckeButtonOn}] {id} .cke_toolbar_separator [background-color: {ckeToolbarSeparator};] {id} .cke_combo_button [border-color:{defaultBorder};{lightGradient}] {id} a.cke_combo_button:hover, {id} a.cke_combo_button:focus, {id} .cke_combo_on a.cke_combo_button [border-color:{defaultBorder};{mediumGradient}] {id} .cke_path_item [color:{elementsPathColor};] {id} a.cke_path_item:hover, {id} a.cke_path_item:focus, {id} a.cke_path_item:active [background-color:{elementsPathBg};] {id}.cke_panel [border-color:{defaultBorder};] "), +a.resetDirty()}delete a._.hiddenSelectionContainer}else CKEDITOR.warn("selection-fake-reset");this.rev=q++},selectElement:function(a){var b=new CKEDITOR.dom.range(this.root);b.setStartBefore(a);b.setEndAfter(a);this.selectRanges([b])},selectRanges:function(a){var b=this.root.editor,b=b&&b._.hiddenSelectionContainer;this.reset();if(b)for(var b=this.root,d,c=0;c]*>)[ \t\r\n]*/gi,"$1");h=h.replace(/([ \t\n\r]+| )/g," ");h=h.replace(/]*>/gi,"\n");if(CKEDITOR.env.ie){var f=a.getDocument().createElement("div");f.append(e);e.$.outerHTML="\x3cpre\x3e"+h+"\x3c/pre\x3e";e.copyAttributes(f.getFirst());e=f.getFirst().remove()}else e.setHtml(h);b=e}else h?b=u(d?[a.getHtml()]:g(a),b):a.moveChildren(b);b.replace(a);if(c){var d=b,k;(k=d.getPrevious(J))&& +k.type==CKEDITOR.NODE_ELEMENT&&k.is("pre")&&(c=p(k.getHtml(),/\n$/,"")+"\n\n"+p(d.getHtml(),/^\n/,""),CKEDITOR.env.ie?d.$.outerHTML="\x3cpre\x3e"+c+"\x3c/pre\x3e":d.setHtml(c),k.remove())}else d&&q(b)}function g(a){var b=[];p(a.getOuterHtml(),/(\S\s*)\n(?:\s|(]+data-cke-bookmark.*?\/span>))*\n(?!$)/gi,function(a,b,d){return b+"\x3c/pre\x3e"+d+"\x3cpre\x3e"}).replace(/([\s\S]*?)<\/pre>/gi,function(a,d){b.push(d)});return b}function p(a,b,d){var c="",g="";a=a.replace(/(^]+data-cke-bookmark.*?\/span>)|(]+data-cke-bookmark.*?\/span>$)/gi, +function(a,b,d){b&&(c=b);d&&(g=d);return""});return c+a.replace(b,d)+g}function u(a,b){var d;1=e?(m=h.createText(""),m.insertAfter(this)):(a=h.createText(""),a.insertAfter(m),a.remove()));return m},substring:function(a,f){return"number"!=typeof f?this.$.nodeValue.substr(a):this.$.nodeValue.substring(a, +f)}}),function(){function a(a,e,h){var f=a.serializable,l=e[h?"endContainer":"startContainer"],k=h?"endOffset":"startOffset",b=f?e.document.getById(a.startNode):a.startNode;a=f?e.document.getById(a.endNode):a.endNode;l.equals(b.getPrevious())?(e.startOffset=e.startOffset-l.getLength()-a.getPrevious().getLength(),l=a.getNext()):l.equals(a.getPrevious())&&(e.startOffset-=l.getLength(),l=a.getNext());l.equals(b.getParent())&&e[k]++;l.equals(a.getParent())&&e[k]++;e[h?"endContainer":"startContainer"]= +l;return e}CKEDITOR.dom.rangeList=function(a){if(a instanceof CKEDITOR.dom.rangeList)return a;a?a instanceof CKEDITOR.dom.range&&(a=[a]):a=[];return CKEDITOR.tools.extend(a,f)};var f={createIterator:function(){var a=this,e=CKEDITOR.dom.walker.bookmark(),h=[],f;return{getNextRange:function(l){f=void 0===f?0:f+1;var k=a[f];if(k&&1b?-1:1}),h=0,f;hCKEDITOR.env.version?a[k].$.styleSheet.cssText+=f:a[k].$.innerHTML+=f}}var m={};CKEDITOR.skin={path:a,loadPart:function(b,g){CKEDITOR.skin.name!=CKEDITOR.skinName.split(",")[0]?CKEDITOR.scriptLoader.load(CKEDITOR.getUrl(a()+"skin.js"),function(){c(b,g)}):c(b,g)},getPath:function(a){return CKEDITOR.getUrl(f(a))},icons:{},addIcon:function(a,b,c,e){a= +a.toLowerCase();this.icons[a]||(this.icons[a]={path:b,offset:c||0,bgsize:e||"16px"})},getIconStyle:function(a,b,c,e,h){var f;a&&(a=a.toLowerCase(),b&&(f=this.icons[a+"-rtl"]),f||(f=this.icons[a]));a=c||f&&f.path||"";e=e||f&&f.offset;h=h||f&&f.bgsize||"16px";a&&(a=a.replace(/'/g,"\\'"));return a&&"background-image:url('"+CKEDITOR.getUrl(a)+"');background-position:0 "+e+"px;background-size:"+h+";"}};CKEDITOR.tools.extend(CKEDITOR.editor.prototype,{getUiColor:function(){return this.uiColor},setUiColor:function(a){var c= +e(CKEDITOR.document);return(this.setUiColor=function(a){this.uiColor=a;var d=CKEDITOR.skin.chameleon,e="",f="";"function"==typeof d&&(e=d(this,"editor"),f=d(this,"panel"));a=[[b,a]];h([c],e,a);h(k,f,a)}).call(this,a)}});var l="cke_ui_color",k=[],b=/\$color/g;CKEDITOR.on("instanceLoaded",function(a){if(!CKEDITOR.env.ie||!CKEDITOR.env.quirks){var c=a.editor;a=function(a){a=(a.data[0]||a.data).element.getElementsByTag("iframe").getItem(0).getFrameDocument();if(!a.getById("cke_ui_color")){a=e(a);k.push(a); +var d=c.getUiColor();d&&h([a],CKEDITOR.skin.chameleon(c,"panel"),[[b,d]])}};c.on("panelShow",a);c.on("menuShow",a);c.config.uiColor&&c.setUiColor(c.config.uiColor)}})}(),function(){if(CKEDITOR.env.webkit)CKEDITOR.env.hc=!1;else{var a=CKEDITOR.dom.element.createFromHtml('\x3cdiv style\x3d"width:0;height:0;position:absolute;left:-10000px;border:1px solid;border-color:red blue"\x3e\x3c/div\x3e',CKEDITOR.document);a.appendTo(CKEDITOR.document.getHead());try{var f=a.getComputedStyle("border-top-color"), +c=a.getComputedStyle("border-right-color");CKEDITOR.env.hc=!(!f||f!=c)}catch(e){CKEDITOR.env.hc=!1}a.remove()}CKEDITOR.env.hc&&(CKEDITOR.env.cssClass+=" cke_hc");CKEDITOR.document.appendStyleText(".cke{visibility:hidden;}");CKEDITOR.status="loaded";CKEDITOR.fireOnce("loaded");if(a=CKEDITOR._.pending)for(delete CKEDITOR._.pending,f=0;fl;l++){var k=l,b;b=parseInt(f[l],16);b=("0"+(0>c?0|b*(1+c):0|b+(255-b)*c).toString(16)).slice(-2);f[k]=b}return"#"+f.join("")}}(),f=function(){var a=new CKEDITOR.template("background:#{to};background-image:linear-gradient(to bottom,{from},{to});filter:progid:DXImageTransform.Microsoft.gradient(gradientType\x3d0,startColorstr\x3d'{from}',endColorstr\x3d'{to}');"); +return function(c,f){return a.output({from:c,to:f})}}(),c={editor:new CKEDITOR.template("{id}.cke_chrome [border-color:{defaultBorder};] {id} .cke_top [ {defaultGradient}border-bottom-color:{defaultBorder};] {id} .cke_bottom [{defaultGradient}border-top-color:{defaultBorder};] {id} .cke_resizer [border-right-color:{ckeResizer}] {id} .cke_dialog_title [{defaultGradient}border-bottom-color:{defaultBorder};] {id} .cke_dialog_footer [{defaultGradient}outline-color:{defaultBorder};border-top-color:{defaultBorder};] {id} .cke_dialog_tab [{lightGradient}border-color:{defaultBorder};] {id} .cke_dialog_tab:hover [{mediumGradient}] {id} .cke_dialog_contents [border-top-color:{defaultBorder};] {id} .cke_dialog_tab_selected, {id} .cke_dialog_tab_selected:hover [background:{dialogTabSelected};border-bottom-color:{dialogTabSelectedBorder};] {id} .cke_dialog_body [background:{dialogBody};border-color:{defaultBorder};] {id} .cke_toolgroup [{lightGradient}border-color:{defaultBorder};] {id} a.cke_button_off:hover, {id} a.cke_button_off:focus, {id} a.cke_button_off:active [{mediumGradient}] {id} .cke_button_on [{ckeButtonOn}] {id} .cke_toolbar_separator [background-color: {ckeToolbarSeparator};] {id} .cke_combo_button [border-color:{defaultBorder};{lightGradient}] {id} a.cke_combo_button:hover, {id} a.cke_combo_button:focus, {id} .cke_combo_on a.cke_combo_button [border-color:{defaultBorder};{mediumGradient}] {id} .cke_path_item [color:{elementsPathColor};] {id} a.cke_path_item:hover, {id} a.cke_path_item:focus, {id} a.cke_path_item:active [background-color:{elementsPathBg};] {id}.cke_panel [border-color:{defaultBorder};] "), panel:new CKEDITOR.template(".cke_panel_grouptitle [{lightGradient}border-color:{defaultBorder};] .cke_menubutton_icon [background-color:{menubuttonIcon};] .cke_menubutton:hover .cke_menubutton_icon, .cke_menubutton:focus .cke_menubutton_icon, .cke_menubutton:active .cke_menubutton_icon [background-color:{menubuttonIconHover};] .cke_menuseparator [background-color:{menubuttonIcon};] a:hover.cke_colorbox, a:focus.cke_colorbox, a:active.cke_colorbox [border-color:{defaultBorder};] a:hover.cke_colorauto, a:hover.cke_colormore, a:focus.cke_colorauto, a:focus.cke_colormore, a:active.cke_colorauto, a:active.cke_colormore [background-color:{ckeColorauto};border-color:{defaultBorder};] ")}; -return function(g,f){var m=g.uiColor,m={id:"."+g.id,defaultBorder:a(m,-.1),defaultGradient:e(a(m,.9),m),lightGradient:e(a(m,1),a(m,.7)),mediumGradient:e(a(m,.8),a(m,.5)),ckeButtonOn:e(a(m,.6),a(m,.7)),ckeResizer:a(m,-.4),ckeToolbarSeparator:a(m,.5),ckeColorauto:a(m,.8),dialogBody:a(m,.7),dialogTabSelected:e("#FFFFFF","#FFFFFF"),dialogTabSelectedBorder:"#FFF",elementsPathColor:a(m,-.6),elementsPathBg:m,menubuttonIcon:a(m,.5),menubuttonIconHover:a(m,.3)};return c[f].output(m).replace(/\[/g,"{").replace(/\]/g, -"}")}}(),CKEDITOR.plugins.add("dialogui",{onLoad:function(){var a=function(a){this._||(this._={});this._["default"]=this._.initValue=a["default"]||"";this._.required=a.required||!1;for(var d=[this._],c=1;carguments.length)){var e=a.call(this,d);e.labelId=CKEDITOR.tools.getNextId()+ -"_label";this._.children=[];var g={role:d.role||"presentation"};d.includeLabel&&(g["aria-labelledby"]=e.labelId);CKEDITOR.ui.dialog.uiElement.call(this,b,d,c,"div",null,g,function(){var a=[],c=d.required?" cke_required":"";"horizontal"!=d.labelLayout?a.push('\x3clabel class\x3d"cke_dialog_ui_labeled_label'+c+'" ',' id\x3d"'+e.labelId+'"',e.inputId?' for\x3d"'+e.inputId+'"':"",(d.labelStyle?' style\x3d"'+d.labelStyle+'"':"")+"\x3e",d.label,"\x3c/label\x3e",'\x3cdiv class\x3d"cke_dialog_ui_labeled_content"', -d.controlStyle?' style\x3d"'+d.controlStyle+'"':"",' role\x3d"presentation"\x3e',f.call(this,b,d),"\x3c/div\x3e"):(c={type:"hbox",widths:d.widths,padding:0,children:[{type:"html",html:'\x3clabel class\x3d"cke_dialog_ui_labeled_label'+c+'" id\x3d"'+e.labelId+'" for\x3d"'+e.inputId+'"'+(d.labelStyle?' style\x3d"'+d.labelStyle+'"':"")+"\x3e"+CKEDITOR.tools.htmlEncode(d.label)+"\x3c/label\x3e"},{type:"html",html:'\x3cspan class\x3d"cke_dialog_ui_labeled_content"'+(d.controlStyle?' style\x3d"'+d.controlStyle+ -'"':"")+"\x3e"+f.call(this,b,d)+"\x3c/span\x3e"}]},CKEDITOR.dialog._.uiElementBuilders.hbox.build(b,c,a));return a.join("")})}},textInput:function(b,d,c){if(!(3>arguments.length)){a.call(this,d);var f=this._.inputId=CKEDITOR.tools.getNextId()+"_textInput",e={"class":"cke_dialog_ui_input_"+d.type,id:f,type:d.type};d.validate&&(this.validate=d.validate);d.maxLength&&(e.maxlength=d.maxLength);d.size&&(e.size=d.size);d.inputStyle&&(e.style=d.inputStyle);var g=this,l=!1;b.on("load",function(){g.getInputElement().on("keydown", -function(a){13==a.data.getKeystroke()&&(l=!0)});g.getInputElement().on("keyup",function(a){13==a.data.getKeystroke()&&l&&(b.getButton("ok")&&setTimeout(function(){b.getButton("ok").click()},0),l=!1);g.bidi&&k.call(g,a)},null,null,1E3)});CKEDITOR.ui.dialog.labeledElement.call(this,b,d,c,function(){var a=['\x3cdiv class\x3d"cke_dialog_ui_input_',d.type,'" role\x3d"presentation"'];d.width&&a.push('style\x3d"width:'+d.width+'" ');a.push("\x3e\x3cinput ");e["aria-labelledby"]=this._.labelId;this._.required&& -(e["aria-required"]=this._.required);for(var b in e)a.push(b+'\x3d"'+e[b]+'" ');a.push(" /\x3e\x3c/div\x3e");return a.join("")})}},textarea:function(b,d,c){if(!(3>arguments.length)){a.call(this,d);var f=this,e=this._.inputId=CKEDITOR.tools.getNextId()+"_textarea",g={};d.validate&&(this.validate=d.validate);g.rows=d.rows||5;g.cols=d.cols||20;g["class"]="cke_dialog_ui_input_textarea "+(d["class"]||"");"undefined"!=typeof d.inputStyle&&(g.style=d.inputStyle);d.dir&&(g.dir=d.dir);if(f.bidi)b.on("load", -function(){f.getInputElement().on("keyup",k)},f);CKEDITOR.ui.dialog.labeledElement.call(this,b,d,c,function(){g["aria-labelledby"]=this._.labelId;this._.required&&(g["aria-required"]=this._.required);var a=['\x3cdiv class\x3d"cke_dialog_ui_input_textarea" role\x3d"presentation"\x3e\x3ctextarea id\x3d"',e,'" '],b;for(b in g)a.push(b+'\x3d"'+CKEDITOR.tools.htmlEncode(g[b])+'" ');a.push("\x3e",CKEDITOR.tools.htmlEncode(f._["default"]),"\x3c/textarea\x3e\x3c/div\x3e");return a.join("")})}},checkbox:function(b, -d,c){if(!(3>arguments.length)){var f=a.call(this,d,{"default":!!d["default"]});d.validate&&(this.validate=d.validate);CKEDITOR.ui.dialog.uiElement.call(this,b,d,c,"span",null,null,function(){var a=CKEDITOR.tools.extend({},d,{id:d.id?d.id+"_checkbox":CKEDITOR.tools.getNextId()+"_checkbox"},!0),c=[],h=CKEDITOR.tools.getNextId()+"_label",e={"class":"cke_dialog_ui_checkbox_input",type:"checkbox","aria-labelledby":h};l(a);d["default"]&&(e.checked="checked");"undefined"!=typeof a.inputStyle&&(a.style=a.inputStyle); -f.checkbox=new CKEDITOR.ui.dialog.uiElement(b,a,c,"input",null,e);c.push(' \x3clabel id\x3d"',h,'" for\x3d"',e.id,'"'+(d.labelStyle?' style\x3d"'+d.labelStyle+'"':"")+"\x3e",CKEDITOR.tools.htmlEncode(d.label),"\x3c/label\x3e");return c.join("")})}},radio:function(b,d,c){if(!(3>arguments.length)){a.call(this,d);this._["default"]||(this._["default"]=this._.initValue=d.items[0][1]);d.validate&&(this.validate=d.validate);var f=[],e=this;d.role="radiogroup";d.includeLabel=!0;CKEDITOR.ui.dialog.labeledElement.call(this, -b,d,c,function(){for(var a=[],c=[],h=(d.id?d.id:CKEDITOR.tools.getNextId())+"_radio",g=0;garguments.length)){var f=a.call(this,d);d.validate&&(this.validate=d.validate);f.inputId=CKEDITOR.tools.getNextId()+"_select";CKEDITOR.ui.dialog.labeledElement.call(this,b,d,c,function(){var a=CKEDITOR.tools.extend({},d,{id:d.id?d.id+"_select":CKEDITOR.tools.getNextId()+"_select"},!0),c=[],h=[],e={id:f.inputId,"class":"cke_dialog_ui_input_select","aria-labelledby":this._.labelId};c.push('\x3cdiv class\x3d"cke_dialog_ui_input_', -d.type,'" role\x3d"presentation"');d.width&&c.push('style\x3d"width:'+d.width+'" ');c.push("\x3e");void 0!==d.size&&(e.size=d.size);void 0!==d.multiple&&(e.multiple=d.multiple);l(a);for(var g=0,k;garguments.length)){void 0===d["default"]&&(d["default"]="");var f=CKEDITOR.tools.extend(a.call(this,d),{definition:d,buttons:[]});d.validate&&(this.validate=d.validate);b.on("load",function(){CKEDITOR.document.getById(f.frameId).getParent().addClass("cke_dialog_ui_input_file")});CKEDITOR.ui.dialog.labeledElement.call(this,b,d,c,function(){f.frameId=CKEDITOR.tools.getNextId()+"_fileInput";var a=['\x3ciframe frameborder\x3d"0" allowtransparency\x3d"0" class\x3d"cke_dialog_ui_input_file" role\x3d"presentation" id\x3d"', -f.frameId,'" title\x3d"',d.label,'" src\x3d"javascript:void('];a.push(CKEDITOR.env.ie?"(function(){"+encodeURIComponent("document.open();("+CKEDITOR.tools.fixDomain+")();document.close();")+"})()":"0");a.push(')"\x3e\x3c/iframe\x3e');return a.join("")})}},fileButton:function(b,d,c){var f=this;if(!(3>arguments.length)){a.call(this,d);d.validate&&(this.validate=d.validate);var e=CKEDITOR.tools.extend({},d),g=e.onClick;e.className=(e.className?e.className+" ":"")+"cke_dialog_ui_button";e.onClick=function(a){var c= -d["for"];g&&!1===g.call(this,a)||(b.getContentElement(c[0],c[1]).submit(),this.disable())};b.on("load",function(){b.getContentElement(d["for"][0],d["for"][1])._.buttons.push(f)});CKEDITOR.ui.dialog.button.call(this,b,e,c)}},html:function(){var a=/^\s*<[\w:]+\s+([^>]*)?>/,d=/^(\s*<[\w:]+(?:\s+[^>]*)?)((?:.|\r|\n)+)$/,c=/\/$/;return function(f,e,g){if(!(3>arguments.length)){var k=[],n=e.html;"\x3c"!=n.charAt(0)&&(n="\x3cspan\x3e"+n+"\x3c/span\x3e");var l=e.focus;if(l){var m=this.focus;this.focus=function(){("function"== -typeof l?l:m).call(this);this.fire("focus")};e.isFocusable&&(this.isFocusable=this.isFocusable);this.keyboardFocusable=!0}CKEDITOR.ui.dialog.uiElement.call(this,f,e,k,"span",null,null,"");k=k.join("").match(a);n=n.match(d)||["","",""];c.test(n[1])&&(n[1]=n[1].slice(0,-1),n[2]="/"+n[2]);g.push([n[1]," ",k[1]||"",n[2]].join(""))}}}(),fieldset:function(a,d,c,f,e){var g=e.label;this._={children:d};CKEDITOR.ui.dialog.uiElement.call(this,a,e,f,"fieldset",null,null,function(){var a=[];g&&a.push("\x3clegend"+ -(e.labelStyle?' style\x3d"'+e.labelStyle+'"':"")+"\x3e"+g+"\x3c/legend\x3e");for(var b=0;bd.getChildCount()?(new CKEDITOR.dom.text(a,CKEDITOR.document)).appendTo(d):d.getChild(0).$.nodeValue=a;return this},getLabel:function(){var a= -CKEDITOR.document.getById(this._.labelId);return!a||1>a.getChildCount()?"":a.getChild(0).getText()},eventProcessors:f},!0);CKEDITOR.ui.dialog.button.prototype=CKEDITOR.tools.extend(new CKEDITOR.ui.dialog.uiElement,{click:function(){return this._.disabled?!1:this.fire("click",{dialog:this._.dialog})},enable:function(){this._.disabled=!1;var a=this.getElement();a&&a.removeClass("cke_disabled")},disable:function(){this._.disabled=!0;this.getElement().addClass("cke_disabled")},isVisible:function(){return this.getElement().getFirst().isVisible()}, +return function(e,h){var m=e.uiColor,m={id:"."+e.id,defaultBorder:a(m,-.1),defaultGradient:f(a(m,.9),m),lightGradient:f(a(m,1),a(m,.7)),mediumGradient:f(a(m,.8),a(m,.5)),ckeButtonOn:f(a(m,.6),a(m,.7)),ckeResizer:a(m,-.4),ckeToolbarSeparator:a(m,.5),ckeColorauto:a(m,.8),dialogBody:a(m,.7),dialogTabSelected:f("#FFFFFF","#FFFFFF"),dialogTabSelectedBorder:"#FFF",elementsPathColor:a(m,-.6),elementsPathBg:m,menubuttonIcon:a(m,.5),menubuttonIconHover:a(m,.3)};return c[h].output(m).replace(/\[/g,"{").replace(/\]/g, +"}")}}(),CKEDITOR.plugins.add("dialogui",{onLoad:function(){var a=function(a){this._||(this._={});this._["default"]=this._.initValue=a["default"]||"";this._.required=a.required||!1;for(var d=[this._],c=1;carguments.length)){var h=a.call(this,d);h.labelId=CKEDITOR.tools.getNextId()+ +"_label";this._.children=[];var f={role:d.role||"presentation"};d.includeLabel&&(f["aria-labelledby"]=h.labelId);CKEDITOR.ui.dialog.uiElement.call(this,b,d,c,"div",null,f,function(){var a=[],c=d.required?" cke_required":"";"horizontal"!=d.labelLayout?a.push('\x3clabel class\x3d"cke_dialog_ui_labeled_label'+c+'" ',' id\x3d"'+h.labelId+'"',h.inputId?' for\x3d"'+h.inputId+'"':"",(d.labelStyle?' style\x3d"'+d.labelStyle+'"':"")+"\x3e",d.label,"\x3c/label\x3e",'\x3cdiv class\x3d"cke_dialog_ui_labeled_content"', +d.controlStyle?' style\x3d"'+d.controlStyle+'"':"",' role\x3d"presentation"\x3e',e.call(this,b,d),"\x3c/div\x3e"):(c={type:"hbox",widths:d.widths,padding:0,children:[{type:"html",html:'\x3clabel class\x3d"cke_dialog_ui_labeled_label'+c+'" id\x3d"'+h.labelId+'" for\x3d"'+h.inputId+'"'+(d.labelStyle?' style\x3d"'+d.labelStyle+'"':"")+"\x3e"+CKEDITOR.tools.htmlEncode(d.label)+"\x3c/label\x3e"},{type:"html",html:'\x3cspan class\x3d"cke_dialog_ui_labeled_content"'+(d.controlStyle?' style\x3d"'+d.controlStyle+ +'"':"")+"\x3e"+e.call(this,b,d)+"\x3c/span\x3e"}]},CKEDITOR.dialog._.uiElementBuilders.hbox.build(b,c,a));return a.join("")})}},textInput:function(b,d,c){if(!(3>arguments.length)){a.call(this,d);var e=this._.inputId=CKEDITOR.tools.getNextId()+"_textInput",h={"class":"cke_dialog_ui_input_"+d.type,id:e,type:d.type};d.validate&&(this.validate=d.validate);d.maxLength&&(h.maxlength=d.maxLength);d.size&&(h.size=d.size);d.inputStyle&&(h.style=d.inputStyle);var f=this,l=!1;b.on("load",function(){f.getInputElement().on("keydown", +function(a){13==a.data.getKeystroke()&&(l=!0)});f.getInputElement().on("keyup",function(a){13==a.data.getKeystroke()&&l&&(b.getButton("ok")&&setTimeout(function(){b.getButton("ok").click()},0),l=!1);f.bidi&&k.call(f,a)},null,null,1E3)});CKEDITOR.ui.dialog.labeledElement.call(this,b,d,c,function(){var a=['\x3cdiv class\x3d"cke_dialog_ui_input_',d.type,'" role\x3d"presentation"'];d.width&&a.push('style\x3d"width:'+d.width+'" ');a.push("\x3e\x3cinput ");h["aria-labelledby"]=this._.labelId;this._.required&& +(h["aria-required"]=this._.required);for(var b in h)a.push(b+'\x3d"'+h[b]+'" ');a.push(" /\x3e\x3c/div\x3e");return a.join("")})}},textarea:function(b,d,c){if(!(3>arguments.length)){a.call(this,d);var e=this,h=this._.inputId=CKEDITOR.tools.getNextId()+"_textarea",f={};d.validate&&(this.validate=d.validate);f.rows=d.rows||5;f.cols=d.cols||20;f["class"]="cke_dialog_ui_input_textarea "+(d["class"]||"");"undefined"!=typeof d.inputStyle&&(f.style=d.inputStyle);d.dir&&(f.dir=d.dir);if(e.bidi)b.on("load", +function(){e.getInputElement().on("keyup",k)},e);CKEDITOR.ui.dialog.labeledElement.call(this,b,d,c,function(){f["aria-labelledby"]=this._.labelId;this._.required&&(f["aria-required"]=this._.required);var a=['\x3cdiv class\x3d"cke_dialog_ui_input_textarea" role\x3d"presentation"\x3e\x3ctextarea id\x3d"',h,'" '],b;for(b in f)a.push(b+'\x3d"'+CKEDITOR.tools.htmlEncode(f[b])+'" ');a.push("\x3e",CKEDITOR.tools.htmlEncode(e._["default"]),"\x3c/textarea\x3e\x3c/div\x3e");return a.join("")})}},checkbox:function(b, +d,c){if(!(3>arguments.length)){var e=a.call(this,d,{"default":!!d["default"]});d.validate&&(this.validate=d.validate);CKEDITOR.ui.dialog.uiElement.call(this,b,d,c,"span",null,null,function(){var a=CKEDITOR.tools.extend({},d,{id:d.id?d.id+"_checkbox":CKEDITOR.tools.getNextId()+"_checkbox"},!0),c=[],g=CKEDITOR.tools.getNextId()+"_label",h={"class":"cke_dialog_ui_checkbox_input",type:"checkbox","aria-labelledby":g};l(a);d["default"]&&(h.checked="checked");"undefined"!=typeof a.inputStyle&&(a.style=a.inputStyle); +e.checkbox=new CKEDITOR.ui.dialog.uiElement(b,a,c,"input",null,h);c.push(' \x3clabel id\x3d"',g,'" for\x3d"',h.id,'"'+(d.labelStyle?' style\x3d"'+d.labelStyle+'"':"")+"\x3e",CKEDITOR.tools.htmlEncode(d.label),"\x3c/label\x3e");return c.join("")})}},radio:function(b,d,c){if(!(3>arguments.length)){a.call(this,d);this._["default"]||(this._["default"]=this._.initValue=d.items[0][1]);d.validate&&(this.validate=d.validate);var e=[],h=this;d.role="radiogroup";d.includeLabel=!0;CKEDITOR.ui.dialog.labeledElement.call(this, +b,d,c,function(){for(var a=[],c=[],g=(d.id?d.id:CKEDITOR.tools.getNextId())+"_radio",f=0;farguments.length)){var e=a.call(this,d);d.validate&&(this.validate=d.validate);e.inputId=CKEDITOR.tools.getNextId()+"_select";CKEDITOR.ui.dialog.labeledElement.call(this,b,d,c,function(){var a=CKEDITOR.tools.extend({},d,{id:d.id?d.id+"_select":CKEDITOR.tools.getNextId()+"_select"},!0),c=[],g=[],h={id:e.inputId,"class":"cke_dialog_ui_input_select","aria-labelledby":this._.labelId};c.push('\x3cdiv class\x3d"cke_dialog_ui_input_', +d.type,'" role\x3d"presentation"');d.width&&c.push('style\x3d"width:'+d.width+'" ');c.push("\x3e");void 0!==d.size&&(h.size=d.size);void 0!==d.multiple&&(h.multiple=d.multiple);l(a);for(var f=0,k;farguments.length)){void 0===d["default"]&&(d["default"]="");var e=CKEDITOR.tools.extend(a.call(this,d),{definition:d,buttons:[]});d.validate&&(this.validate=d.validate);b.on("load",function(){CKEDITOR.document.getById(e.frameId).getParent().addClass("cke_dialog_ui_input_file")});CKEDITOR.ui.dialog.labeledElement.call(this,b,d,c,function(){e.frameId=CKEDITOR.tools.getNextId()+"_fileInput";var a=['\x3ciframe frameborder\x3d"0" allowtransparency\x3d"0" class\x3d"cke_dialog_ui_input_file" role\x3d"presentation" id\x3d"', +e.frameId,'" title\x3d"',d.label,'" src\x3d"javascript:void('];a.push(CKEDITOR.env.ie?"(function(){"+encodeURIComponent("document.open();("+CKEDITOR.tools.fixDomain+")();document.close();")+"})()":"0");a.push(')"\x3e\x3c/iframe\x3e');return a.join("")})}},fileButton:function(b,d,c){var e=this;if(!(3>arguments.length)){a.call(this,d);d.validate&&(this.validate=d.validate);var h=CKEDITOR.tools.extend({},d),f=h.onClick;h.className=(h.className?h.className+" ":"")+"cke_dialog_ui_button";h.onClick=function(a){var c= +d["for"];f&&!1===f.call(this,a)||(b.getContentElement(c[0],c[1]).submit(),this.disable())};b.on("load",function(){b.getContentElement(d["for"][0],d["for"][1])._.buttons.push(e)});CKEDITOR.ui.dialog.button.call(this,b,h,c)}},html:function(){var a=/^\s*<[\w:]+\s+([^>]*)?>/,d=/^(\s*<[\w:]+(?:\s+[^>]*)?)((?:.|\r|\n)+)$/,c=/\/$/;return function(e,h,f){if(!(3>arguments.length)){var k=[],n=h.html;"\x3c"!=n.charAt(0)&&(n="\x3cspan\x3e"+n+"\x3c/span\x3e");var l=h.focus;if(l){var m=this.focus;this.focus=function(){("function"== +typeof l?l:m).call(this);this.fire("focus")};h.isFocusable&&(this.isFocusable=this.isFocusable);this.keyboardFocusable=!0}CKEDITOR.ui.dialog.uiElement.call(this,e,h,k,"span",null,null,"");k=k.join("").match(a);n=n.match(d)||["","",""];c.test(n[1])&&(n[1]=n[1].slice(0,-1),n[2]="/"+n[2]);f.push([n[1]," ",k[1]||"",n[2]].join(""))}}}(),fieldset:function(a,d,c,e,h){var f=h.label;this._={children:d};CKEDITOR.ui.dialog.uiElement.call(this,a,h,e,"fieldset",null,null,function(){var a=[];f&&a.push("\x3clegend"+ +(h.labelStyle?' style\x3d"'+h.labelStyle+'"':"")+"\x3e"+f+"\x3c/legend\x3e");for(var b=0;bd.getChildCount()?(new CKEDITOR.dom.text(a,CKEDITOR.document)).appendTo(d):d.getChild(0).$.nodeValue=a;return this},getLabel:function(){var a= +CKEDITOR.document.getById(this._.labelId);return!a||1>a.getChildCount()?"":a.getChild(0).getText()},eventProcessors:h},!0);CKEDITOR.ui.dialog.button.prototype=CKEDITOR.tools.extend(new CKEDITOR.ui.dialog.uiElement,{click:function(){return this._.disabled?!1:this.fire("click",{dialog:this._.dialog})},enable:function(){this._.disabled=!1;var a=this.getElement();a&&a.removeClass("cke_disabled")},disable:function(){this._.disabled=!0;this.getElement().addClass("cke_disabled")},isVisible:function(){return this.getElement().getFirst().isVisible()}, isEnabled:function(){return!this._.disabled},eventProcessors:CKEDITOR.tools.extend({},CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors,{onClick:function(a,d){this.on("click",function(){d.apply(this,arguments)})}},!0),accessKeyUp:function(){this.click()},accessKeyDown:function(){this.focus()},keyboardFocusable:!0},!0);CKEDITOR.ui.dialog.textInput.prototype=CKEDITOR.tools.extend(new CKEDITOR.ui.dialog.labeledElement,{getInputElement:function(){return CKEDITOR.document.getById(this._.inputId)}, focus:function(){var a=this.selectParentTab();setTimeout(function(){var d=a.getInputElement();d&&d.$.focus()},0)},select:function(){var a=this.selectParentTab();setTimeout(function(){var d=a.getInputElement();d&&(d.$.focus(),d.$.select())},0)},accessKeyUp:function(){this.select()},setValue:function(a){if(this.bidi){var d=a&&a.charAt(0);(d="‪"==d?"ltr":"‫"==d?"rtl":null)&&(a=a.slice(1));this.setDirectionMarker(d)}a||(a="");return CKEDITOR.ui.dialog.uiElement.prototype.setValue.apply(this,arguments)}, -getValue:function(){var a=CKEDITOR.ui.dialog.uiElement.prototype.getValue.call(this);if(this.bidi&&a){var d=this.getDirectionMarker();d&&(a=("ltr"==d?"‪":"‫")+a)}return a},setDirectionMarker:function(a){var d=this.getInputElement();a?d.setAttributes({dir:a,"data-cke-dir-marker":a}):this.getDirectionMarker()&&d.removeAttributes(["dir","data-cke-dir-marker"])},getDirectionMarker:function(){return this.getInputElement().data("cke-dir-marker")},keyboardFocusable:!0},g,!0);CKEDITOR.ui.dialog.textarea.prototype= -new CKEDITOR.ui.dialog.textInput;CKEDITOR.ui.dialog.select.prototype=CKEDITOR.tools.extend(new CKEDITOR.ui.dialog.labeledElement,{getInputElement:function(){return this._.select.getElement()},add:function(a,d,c){var f=new CKEDITOR.dom.element("option",this.getDialog().getParentEditor().document),e=this.getInputElement().$;f.$.text=a;f.$.value=void 0===d||null===d?a:d;void 0===c||null===c?CKEDITOR.env.ie?e.add(f.$):e.add(f.$,null):e.add(f.$,c);return this},remove:function(a){this.getInputElement().$.remove(a); -return this},clear:function(){for(var a=this.getInputElement().$;0b-a;d--)if(this._.tabs[this._.tabIdList[d%a]][0].$.offsetHeight)return this._.tabIdList[d%a];return null}function e(){for(var a=this._.tabIdList.length,b=CKEDITOR.tools.indexOf(this._.tabIdList,this._.currentTabId),d=b+1;db-a;d--)if(this._.tabs[this._.tabIdList[d%a]][0].$.offsetHeight)return this._.tabIdList[d%a];return null}function f(){for(var a=this._.tabIdList.length,b=CKEDITOR.tools.indexOf(this._.tabIdList,this._.currentTabId),d=b+1;dp.width-k.width-e?p.width-k.width+("rtl"==f.lang.dir?0:g[1]):h.x,h.y+g[0]p.height-k.height-e?p.height-k.height+g[2]:h.y,1);d.data.preventDefault()}function d(){CKEDITOR.document.removeListener("mousemove", -b);CKEDITOR.document.removeListener("mouseup",d);if(CKEDITOR.env.ie6Compat){var a=A.getChild(0).getFrameDocument();a.removeListener("mousemove",b);a.removeListener("mouseup",d)}}var c=null,h=null,f=a.getParentEditor(),e=f.config.dialog_magnetDistance,g=CKEDITOR.skin.margins||[0,0,0,0];"undefined"==typeof e&&(e=20);a.parts.title.on("mousedown",function(f){c={x:f.data.$.screenX,y:f.data.$.screenY};CKEDITOR.document.on("mousemove",b);CKEDITOR.document.on("mouseup",d);h=a.getPosition();if(CKEDITOR.env.ie6Compat){var e= -A.getChild(0).getFrameDocument();e.on("mousemove",b);e.on("mouseup",d)}f.data.preventDefault()},a)}function h(a){function b(d){var l="rtl"==f.lang.dir,m=n.width,q=n.height,t=m+(d.data.$.screenX-p.x)*(l?-1:1)*(a._.moved?1:2),w=q+(d.data.$.screenY-p.y)*(a._.moved?1:2),r=a._.element.getFirst(),r=l&&r.getComputedStyle("right"),u=a.getPosition();u.y+w>k.height&&(w=k.height-u.y);(l?r:u.x)+t>k.width&&(t=k.width-(l?r:u.x));if(h==CKEDITOR.DIALOG_RESIZE_WIDTH||h==CKEDITOR.DIALOG_RESIZE_BOTH)m=Math.max(c.minWidth|| -0,t-e);if(h==CKEDITOR.DIALOG_RESIZE_HEIGHT||h==CKEDITOR.DIALOG_RESIZE_BOTH)q=Math.max(c.minHeight||0,w-g);a.resize(m,q);a._.moved||a.layout();d.data.preventDefault()}function d(){CKEDITOR.document.removeListener("mouseup",d);CKEDITOR.document.removeListener("mousemove",b);l&&(l.remove(),l=null);if(CKEDITOR.env.ie6Compat){var a=A.getChild(0).getFrameDocument();a.removeListener("mouseup",d);a.removeListener("mousemove",b)}}var c=a.definition,h=c.resizable;if(h!=CKEDITOR.DIALOG_RESIZE_NONE){var f=a.getParentEditor(), -e,g,k,p,n,l,m=CKEDITOR.tools.addFunction(function(c){n=a.getSize();var h=a.parts.contents;h.$.getElementsByTagName("iframe").length&&(l=CKEDITOR.dom.element.createFromHtml('\x3cdiv class\x3d"cke_dialog_resize_cover" style\x3d"height: 100%; position: absolute; width: 100%;"\x3e\x3c/div\x3e'),h.append(l));g=n.height-a.parts.contents.getSize("height",!(CKEDITOR.env.gecko||CKEDITOR.env.ie&&CKEDITOR.env.quirks));e=n.width-a.parts.contents.getSize("width",1);p={x:c.screenX,y:c.screenY};k=CKEDITOR.document.getWindow().getViewPaneSize(); -CKEDITOR.document.on("mousemove",b);CKEDITOR.document.on("mouseup",d);CKEDITOR.env.ie6Compat&&(h=A.getChild(0).getFrameDocument(),h.on("mousemove",b),h.on("mouseup",d));c.preventDefault&&c.preventDefault()});a.on("load",function(){var b="";h==CKEDITOR.DIALOG_RESIZE_WIDTH?b=" cke_resizer_horizontal":h==CKEDITOR.DIALOG_RESIZE_HEIGHT&&(b=" cke_resizer_vertical");b=CKEDITOR.dom.element.createFromHtml('\x3cdiv class\x3d"cke_resizer'+b+" cke_resizer_"+f.lang.dir+'" title\x3d"'+CKEDITOR.tools.htmlEncode(f.lang.common.resize)+ -'" onmousedown\x3d"CKEDITOR.tools.callFunction('+m+', event )"\x3e'+("ltr"==f.lang.dir?"◢":"◣")+"\x3c/div\x3e");a.parts.footer.append(b,1)});f.on("destroy",function(){CKEDITOR.tools.removeFunction(m)})}}function p(a){a.data.preventDefault(1)}function v(a){var b=CKEDITOR.document.getWindow(),d=a.config,c=d.dialog_backgroundCoverColor||"white",h=d.dialog_backgroundCoverOpacity,f=d.baseFloatZIndex,d=CKEDITOR.tools.genKey(c,h,f),e=B[d];e?e.show():(f=['\x3cdiv tabIndex\x3d"-1" style\x3d"position: ',CKEDITOR.env.ie6Compat? -"absolute":"fixed","; z-index: ",f,"; top: 0px; left: 0px; ",CKEDITOR.env.ie6Compat?"":"background-color: "+c,'" class\x3d"cke_dialog_background_cover"\x3e'],CKEDITOR.env.ie6Compat&&(c="\x3chtml\x3e\x3cbody style\x3d\\'background-color:"+c+";\\'\x3e\x3c/body\x3e\x3c/html\x3e",f.push('\x3ciframe hidefocus\x3d"true" frameborder\x3d"0" id\x3d"cke_dialog_background_iframe" src\x3d"javascript:'),f.push("void((function(){"+encodeURIComponent("document.open();("+CKEDITOR.tools.fixDomain+")();document.write( '"+ -c+"' );document.close();")+"})())"),f.push('" style\x3d"position:absolute;left:0;top:0;width:100%;height: 100%;filter: progid:DXImageTransform.Microsoft.Alpha(opacity\x3d0)"\x3e\x3c/iframe\x3e')),f.push("\x3c/div\x3e"),e=CKEDITOR.dom.element.createFromHtml(f.join("")),e.setOpacity(void 0!==h?h:.5),e.on("keydown",p),e.on("keypress",p),e.on("keyup",p),e.appendTo(CKEDITOR.document.getBody()),B[d]=e);a.focusManager.add(e);A=e;a=function(){var a=b.getViewPaneSize();e.setStyles({width:a.width+"px",height:a.height+ -"px"})};var g=function(){var a=b.getScrollPosition(),d=CKEDITOR.dialog._.currentTop;e.setStyles({left:a.x+"px",top:a.y+"px"});if(d){do a=d.getPosition(),d.move(a.x,a.y);while(d=d._.parentDialog)}};x=a;b.on("resize",a);a();CKEDITOR.env.mac&&CKEDITOR.env.webkit||e.focus();if(CKEDITOR.env.ie6Compat){var k=function(){g();arguments.callee.prevScrollHandler.apply(this,arguments)};b.$.setTimeout(function(){k.prevScrollHandler=window.onscroll||function(){};window.onscroll=k},0);g()}}function u(a){A&&(a.focusManager.remove(A), -a=CKEDITOR.document.getWindow(),A.hide(),a.removeListener("resize",x),CKEDITOR.env.ie6Compat&&a.$.setTimeout(function(){window.onscroll=window.onscroll&&window.onscroll.prevScrollHandler||null},0),x=null)}var t=CKEDITOR.tools.cssLength,n='\x3cdiv class\x3d"cke_reset_all {editorId} {editorDialogClass} {hidpi}" dir\x3d"{langDir}" lang\x3d"{langCode}" role\x3d"dialog" aria-labelledby\x3d"cke_dialog_title_{id}"\x3e\x3ctable class\x3d"cke_dialog '+CKEDITOR.env.cssClass+' cke_{langDir}" style\x3d"position:absolute" role\x3d"presentation"\x3e\x3ctr\x3e\x3ctd role\x3d"presentation"\x3e\x3cdiv class\x3d"cke_dialog_body" role\x3d"presentation"\x3e\x3cdiv id\x3d"cke_dialog_title_{id}" class\x3d"cke_dialog_title" role\x3d"presentation"\x3e\x3c/div\x3e\x3ca id\x3d"cke_dialog_close_button_{id}" class\x3d"cke_dialog_close_button" href\x3d"javascript:void(0)" title\x3d"{closeTitle}" role\x3d"button"\x3e\x3cspan class\x3d"cke_label"\x3eX\x3c/span\x3e\x3c/a\x3e\x3cdiv id\x3d"cke_dialog_tabs_{id}" class\x3d"cke_dialog_tabs" role\x3d"tablist"\x3e\x3c/div\x3e\x3ctable class\x3d"cke_dialog_contents" role\x3d"presentation"\x3e\x3ctr\x3e\x3ctd id\x3d"cke_dialog_contents_{id}" class\x3d"cke_dialog_contents_body" role\x3d"presentation"\x3e\x3c/td\x3e\x3c/tr\x3e\x3ctr\x3e\x3ctd id\x3d"cke_dialog_footer_{id}" class\x3d"cke_dialog_footer" role\x3d"presentation"\x3e\x3c/td\x3e\x3c/tr\x3e\x3c/table\x3e\x3c/div\x3e\x3c/td\x3e\x3c/tr\x3e\x3c/table\x3e\x3c/div\x3e'; -CKEDITOR.dialog=function(b,c){function k(){var a=B._.focusList;a.sort(function(a,b){return a.tabIndex!=b.tabIndex?b.tabIndex-a.tabIndex:a.focusIndex-b.focusIndex});for(var b=a.length,d=0;db.length)){var d=B._.currentFocusIndex;B._.tabBarMode&&0>a&&(d=0);try{b[d].getInputElement().$.blur()}catch(c){}var h=d,f=1p.width-k.width-h?p.width-k.width+("rtl"==e.lang.dir?0:f[1]):g.x,g.y+f[0]p.height-k.height-h?p.height-k.height+f[2]:g.y,1);d.data.preventDefault()}function d(){CKEDITOR.document.removeListener("mousemove", +b);CKEDITOR.document.removeListener("mouseup",d);if(CKEDITOR.env.ie6Compat){var a=y.getChild(0).getFrameDocument();a.removeListener("mousemove",b);a.removeListener("mouseup",d)}}var c=null,g=null,e=a.getParentEditor(),h=e.config.dialog_magnetDistance,f=CKEDITOR.skin.margins||[0,0,0,0];"undefined"==typeof h&&(h=20);a.parts.title.on("mousedown",function(e){c={x:e.data.$.screenX,y:e.data.$.screenY};CKEDITOR.document.on("mousemove",b);CKEDITOR.document.on("mouseup",d);g=a.getPosition();if(CKEDITOR.env.ie6Compat){var h= +y.getChild(0).getFrameDocument();h.on("mousemove",b);h.on("mouseup",d)}e.data.preventDefault()},a)}function g(a){function b(d){var n="rtl"==e.lang.dir,m=l.width,q=l.height,r=m+(d.data.$.screenX-p.x)*(n?-1:1)*(a._.moved?1:2),w=q+(d.data.$.screenY-p.y)*(a._.moved?1:2),t=a._.element.getFirst(),t=n&&t.getComputedStyle("right"),v=a.getPosition();v.y+w>k.height&&(w=k.height-v.y);(n?t:v.x)+r>k.width&&(r=k.width-(n?t:v.x));if(g==CKEDITOR.DIALOG_RESIZE_WIDTH||g==CKEDITOR.DIALOG_RESIZE_BOTH)m=Math.max(c.minWidth|| +0,r-h);if(g==CKEDITOR.DIALOG_RESIZE_HEIGHT||g==CKEDITOR.DIALOG_RESIZE_BOTH)q=Math.max(c.minHeight||0,w-f);a.resize(m,q);a._.moved||a.layout();d.data.preventDefault()}function d(){CKEDITOR.document.removeListener("mouseup",d);CKEDITOR.document.removeListener("mousemove",b);n&&(n.remove(),n=null);if(CKEDITOR.env.ie6Compat){var a=y.getChild(0).getFrameDocument();a.removeListener("mouseup",d);a.removeListener("mousemove",b)}}var c=a.definition,g=c.resizable;if(g!=CKEDITOR.DIALOG_RESIZE_NONE){var e=a.getParentEditor(), +h,f,k,p,l,n,m=CKEDITOR.tools.addFunction(function(c){l=a.getSize();var g=a.parts.contents;g.$.getElementsByTagName("iframe").length&&(n=CKEDITOR.dom.element.createFromHtml('\x3cdiv class\x3d"cke_dialog_resize_cover" style\x3d"height: 100%; position: absolute; width: 100%;"\x3e\x3c/div\x3e'),g.append(n));f=l.height-a.parts.contents.getSize("height",!(CKEDITOR.env.gecko||CKEDITOR.env.ie&&CKEDITOR.env.quirks));h=l.width-a.parts.contents.getSize("width",1);p={x:c.screenX,y:c.screenY};k=CKEDITOR.document.getWindow().getViewPaneSize(); +CKEDITOR.document.on("mousemove",b);CKEDITOR.document.on("mouseup",d);CKEDITOR.env.ie6Compat&&(g=y.getChild(0).getFrameDocument(),g.on("mousemove",b),g.on("mouseup",d));c.preventDefault&&c.preventDefault()});a.on("load",function(){var b="";g==CKEDITOR.DIALOG_RESIZE_WIDTH?b=" cke_resizer_horizontal":g==CKEDITOR.DIALOG_RESIZE_HEIGHT&&(b=" cke_resizer_vertical");b=CKEDITOR.dom.element.createFromHtml('\x3cdiv class\x3d"cke_resizer'+b+" cke_resizer_"+e.lang.dir+'" title\x3d"'+CKEDITOR.tools.htmlEncode(e.lang.common.resize)+ +'" onmousedown\x3d"CKEDITOR.tools.callFunction('+m+', event )"\x3e'+("ltr"==e.lang.dir?"◢":"◣")+"\x3c/div\x3e");a.parts.footer.append(b,1)});e.on("destroy",function(){CKEDITOR.tools.removeFunction(m)})}}function p(a){a.data.preventDefault(1)}function u(a){var b=CKEDITOR.document.getWindow(),d=a.config,c=d.dialog_backgroundCoverColor||"white",g=d.dialog_backgroundCoverOpacity,e=d.baseFloatZIndex,d=CKEDITOR.tools.genKey(c,g,e),h=D[d];h?h.show():(e=['\x3cdiv tabIndex\x3d"-1" style\x3d"position: ',CKEDITOR.env.ie6Compat? +"absolute":"fixed","; z-index: ",e,"; top: 0px; left: 0px; ",CKEDITOR.env.ie6Compat?"":"background-color: "+c,'" class\x3d"cke_dialog_background_cover"\x3e'],CKEDITOR.env.ie6Compat&&(c="\x3chtml\x3e\x3cbody style\x3d\\'background-color:"+c+";\\'\x3e\x3c/body\x3e\x3c/html\x3e",e.push('\x3ciframe hidefocus\x3d"true" frameborder\x3d"0" id\x3d"cke_dialog_background_iframe" src\x3d"javascript:'),e.push("void((function(){"+encodeURIComponent("document.open();("+CKEDITOR.tools.fixDomain+")();document.write( '"+ +c+"' );document.close();")+"})())"),e.push('" style\x3d"position:absolute;left:0;top:0;width:100%;height: 100%;filter: progid:DXImageTransform.Microsoft.Alpha(opacity\x3d0)"\x3e\x3c/iframe\x3e')),e.push("\x3c/div\x3e"),h=CKEDITOR.dom.element.createFromHtml(e.join("")),h.setOpacity(void 0!==g?g:.5),h.on("keydown",p),h.on("keypress",p),h.on("keyup",p),h.appendTo(CKEDITOR.document.getBody()),D[d]=h);a.focusManager.add(h);y=h;a=function(){var a=b.getViewPaneSize();h.setStyles({width:a.width+"px",height:a.height+ +"px"})};var f=function(){var a=b.getScrollPosition(),d=CKEDITOR.dialog._.currentTop;h.setStyles({left:a.x+"px",top:a.y+"px"});if(d){do a=d.getPosition(),d.move(a.x,a.y);while(d=d._.parentDialog)}};x=a;b.on("resize",a);a();CKEDITOR.env.mac&&CKEDITOR.env.webkit||h.focus();if(CKEDITOR.env.ie6Compat){var k=function(){f();arguments.callee.prevScrollHandler.apply(this,arguments)};b.$.setTimeout(function(){k.prevScrollHandler=window.onscroll||function(){};window.onscroll=k},0);f()}}function v(a){y&&(a.focusManager.remove(y), +a=CKEDITOR.document.getWindow(),y.hide(),a.removeListener("resize",x),CKEDITOR.env.ie6Compat&&a.$.setTimeout(function(){window.onscroll=window.onscroll&&window.onscroll.prevScrollHandler||null},0),x=null)}var r=CKEDITOR.tools.cssLength,n='\x3cdiv class\x3d"cke_reset_all {editorId} {editorDialogClass} {hidpi}" dir\x3d"{langDir}" lang\x3d"{langCode}" role\x3d"dialog" aria-labelledby\x3d"cke_dialog_title_{id}"\x3e\x3ctable class\x3d"cke_dialog '+CKEDITOR.env.cssClass+' cke_{langDir}" style\x3d"position:absolute" role\x3d"presentation"\x3e\x3ctr\x3e\x3ctd role\x3d"presentation"\x3e\x3cdiv class\x3d"cke_dialog_body" role\x3d"presentation"\x3e\x3cdiv id\x3d"cke_dialog_title_{id}" class\x3d"cke_dialog_title" role\x3d"presentation"\x3e\x3c/div\x3e\x3ca id\x3d"cke_dialog_close_button_{id}" class\x3d"cke_dialog_close_button" href\x3d"javascript:void(0)" title\x3d"{closeTitle}" role\x3d"button"\x3e\x3cspan class\x3d"cke_label"\x3eX\x3c/span\x3e\x3c/a\x3e\x3cdiv id\x3d"cke_dialog_tabs_{id}" class\x3d"cke_dialog_tabs" role\x3d"tablist"\x3e\x3c/div\x3e\x3ctable class\x3d"cke_dialog_contents" role\x3d"presentation"\x3e\x3ctr\x3e\x3ctd id\x3d"cke_dialog_contents_{id}" class\x3d"cke_dialog_contents_body" role\x3d"presentation"\x3e\x3c/td\x3e\x3c/tr\x3e\x3ctr\x3e\x3ctd id\x3d"cke_dialog_footer_{id}" class\x3d"cke_dialog_footer" role\x3d"presentation"\x3e\x3c/td\x3e\x3c/tr\x3e\x3c/table\x3e\x3c/div\x3e\x3c/td\x3e\x3c/tr\x3e\x3c/table\x3e\x3c/div\x3e'; +CKEDITOR.dialog=function(b,c){function k(){var a=D._.focusList;a.sort(function(a,b){return a.tabIndex!=b.tabIndex?b.tabIndex-a.tabIndex:a.focusIndex-b.focusIndex});for(var b=a.length,d=0;db.length)){var d=D._.currentFocusIndex;D._.tabBarMode&&0>a&&(d=0);try{b[d].getInputElement().$.blur()}catch(c){}var g=d,e=1d.height|| -b.width+(0d.width?a.setStyle("position","absolute"):a.setStyle("position","fixed"));this.move(this._.moved?this._.position.x:c,this._.moved?this._.position.y:h)},foreach:function(a){for(var b in this._.contents)for(var d in this._.contents[b])a.call(this,this._.contents[b][d]);return this},reset:function(){var a=function(a){a.reset&&a.reset(1)};return function(){this.foreach(a);return this}}(),setupContent:function(){var a=arguments;this.foreach(function(b){b.setup&&b.setup.apply(b,a)})}, +return{width:a.$.offsetWidth||0,height:a.$.offsetHeight||0}},move:function(a,b,d){var c=this._.element.getFirst(),g="rtl"==this._.editor.lang.dir,e="fixed"==c.getComputedStyle("position");CKEDITOR.env.ie&&c.setStyle("zoom","100%");e&&this._.position&&this._.position.x==a&&this._.position.y==b||(this._.position={x:a,y:b},e||(e=CKEDITOR.document.getWindow().getScrollPosition(),a+=e.x,b+=e.y),g&&(e=this.getSize(),a=CKEDITOR.document.getWindow().getViewPaneSize().width-e.width-a),b={top:(0d.height|| +b.width+(0d.width?a.setStyle("position","absolute"):a.setStyle("position","fixed"));this.move(this._.moved?this._.position.x:c,this._.moved?this._.position.y:g)},foreach:function(a){for(var b in this._.contents)for(var d in this._.contents[b])a.call(this,this._.contents[b][d]);return this},reset:function(){var a=function(a){a.reset&&a.reset(1)};return function(){this.foreach(a);return this}}(),setupContent:function(){var a=arguments;this.foreach(function(b){b.setup&&b.setup.apply(b,a)})}, commitContent:function(){var a=arguments;this.foreach(function(b){CKEDITOR.env.ie&&this._.currentFocusIndex==b.focusIndex&&b.getInputElement().$.blur();b.commit&&b.commit.apply(b,a)})},hide:function(){if(this.parts.dialog.isVisible()){this.fire("hide",{});this._.editor.fire("dialogHide",this);this.selectPage(this._.tabIdList[0]);var a=this._.element;a.setStyle("display","none");this.parts.dialog.setStyle("visibility","hidden");for(J(this);CKEDITOR.dialog._.currentTop!=this;)CKEDITOR.dialog._.currentTop.hide(); -if(this._.parentDialog){var b=this._.parentDialog.getElement().getFirst();b.setStyle("z-index",parseInt(b.$.style.zIndex,10)+Math.floor(this._.editor.config.baseFloatZIndex/2))}else u(this._.editor);if(CKEDITOR.dialog._.currentTop=this._.parentDialog)CKEDITOR.dialog._.currentZIndex-=10;else{CKEDITOR.dialog._.currentZIndex=null;a.removeListener("keydown",D);a.removeListener("keyup",G);var d=this._.editor;d.focus();setTimeout(function(){d.focusManager.unlock();CKEDITOR.env.iOS&&d.window.focus()},0)}delete this._.parentDialog; -this.foreach(function(a){a.resetInitValue&&a.resetInitValue()});this.setState(CKEDITOR.DIALOG_STATE_IDLE)}},addPage:function(a){if(!a.requiredContent||this._.editor.filter.check(a.requiredContent)){for(var b=[],d=a.label?' title\x3d"'+CKEDITOR.tools.htmlEncode(a.label)+'"':"",c=CKEDITOR.dialog._.uiElementBuilders.vbox.build(this,{type:"vbox",className:"cke_dialog_page_contents",children:a.elements,expand:!!a.expand,padding:a.padding,style:a.style||"width: 100%;"},b),h=this._.contents[a.id]={},f=c.getChild(), -e=0;c=f.shift();)c.notAllowed||"hbox"==c.type||"vbox"==c.type||e++,h[c.id]=c,"function"==typeof c.getChild&&f.push.apply(f,c.getChild());e||(a.hidden=!0);b=CKEDITOR.dom.element.createFromHtml(b.join(""));b.setAttribute("role","tabpanel");c=CKEDITOR.env;h="cke_"+a.id+"_"+CKEDITOR.tools.getNextNumber();d=CKEDITOR.dom.element.createFromHtml(['\x3ca class\x3d"cke_dialog_tab"',0arguments.length)){var g=(c.call?c(b):c)||"div", -k=["\x3c",g," "],p=(h&&h.call?h(b):h)||{},n=(f&&f.call?f(b):f)||{},l=(e&&e.call?e.call(this,a,b):e)||"",m=this.domId=n.id||CKEDITOR.tools.getNextId()+"_uiElement";b.requiredContent&&!a.getParentEditor().filter.check(b.requiredContent)&&(p.display="none",this.notAllowed=!0);n.id=m;var t={};b.type&&(t["cke_dialog_ui_"+b.type]=1);b.className&&(t[b.className]=1);b.disabled&&(t.cke_disabled=1);for(var q=n["class"]&&n["class"].split?n["class"].split(" "):[],m=0;marguments.length)){var f=(c.call?c(b):c)||"div", +k=["\x3c",f," "],p=(g&&g.call?g(b):g)||{},l=(e&&e.call?e(b):e)||{},n=(h&&h.call?h.call(this,a,b):h)||"",m=this.domId=l.id||CKEDITOR.tools.getNextId()+"_uiElement";b.requiredContent&&!a.getParentEditor().filter.check(b.requiredContent)&&(p.display="none",this.notAllowed=!0);l.id=m;var q={};b.type&&(q["cke_dialog_ui_"+b.type]=1);b.className&&(q[b.className]=1);b.disabled&&(q.cke_disabled=1);for(var r=l["class"]&&l["class"].split?l["class"].split(" "):[],m=0;mCKEDITOR.env.version?"cke_dialog_ui_focused":"";b.on("focus",function(){a._.tabBarMode=!1;a._.hasFocus=!0;w.fire("focus");d&&this.addClass(d)});b.on("blur",function(){w.fire("blur");d&&this.removeClass(d)})}});CKEDITOR.tools.extend(this,b);this.keyboardFocusable&&(this.tabIndex=b.tabIndex||0,this.focusIndex= -a._.focusList.push(this)-1,this.on("focus",function(){a._.currentFocusIndex=w.focusIndex}))}},hbox:function(a,b,d,c,h){if(!(4>arguments.length)){this._||(this._={});var f=this._.children=b,e=h&&h.widths||null,g=h&&h.height||null,k,n={role:"presentation"};h&&h.align&&(n.align=h.align);CKEDITOR.ui.dialog.uiElement.call(this,a,h||{type:"hbox"},c,"table",{},n,function(){var a=['\x3ctbody\x3e\x3ctr class\x3d"cke_dialog_ui_hbox"\x3e'];for(k=0;karguments.length)){this._||(this._={});var f=this._.children=b,e=h&&h.width||null,g=h&&h.heights||null;CKEDITOR.ui.dialog.uiElement.call(this,a,h||{type:"vbox"},c,"div",null,{role:"presentation"},function(){var b=['\x3ctable role\x3d"presentation" cellspacing\x3d"0" border\x3d"0" '];b.push('style\x3d"');h&&h.expand&&b.push("height:100%;");b.push("width:"+t(e||"100%"),";");CKEDITOR.env.webkit&&b.push("float:none;");b.push('"');b.push('align\x3d"',CKEDITOR.tools.htmlEncode(h&& -h.align||("ltr"==a.getParentEditor().lang.dir?"left":"right")),'" ');b.push("\x3e\x3ctbody\x3e");for(var c=0;cCKEDITOR.env.version?"cke_dialog_ui_focused":"";b.on("focus",function(){a._.tabBarMode=!1;a._.hasFocus=!0;w.fire("focus");d&&this.addClass(d)});b.on("blur",function(){w.fire("blur");d&&this.removeClass(d)})}});CKEDITOR.tools.extend(this,b);this.keyboardFocusable&&(this.tabIndex=b.tabIndex||0,this.focusIndex= +a._.focusList.push(this)-1,this.on("focus",function(){a._.currentFocusIndex=w.focusIndex}))}},hbox:function(a,b,d,c,g){if(!(4>arguments.length)){this._||(this._={});var e=this._.children=b,h=g&&g.widths||null,f=g&&g.height||null,k,p={role:"presentation"};g&&g.align&&(p.align=g.align);CKEDITOR.ui.dialog.uiElement.call(this,a,g||{type:"hbox"},c,"table",{},p,function(){var a=['\x3ctbody\x3e\x3ctr class\x3d"cke_dialog_ui_hbox"\x3e'];for(k=0;karguments.length)){this._||(this._={});var e=this._.children=b,h=g&&g.width||null,f=g&&g.heights||null;CKEDITOR.ui.dialog.uiElement.call(this,a,g||{type:"vbox"},c,"div",null,{role:"presentation"},function(){var b=['\x3ctable role\x3d"presentation" cellspacing\x3d"0" border\x3d"0" '];b.push('style\x3d"');g&&g.expand&&b.push("height:100%;");b.push("width:"+r(h||"100%"),";");CKEDITOR.env.webkit&&b.push("float:none;");b.push('"');b.push('align\x3d"',CKEDITOR.tools.htmlEncode(g&& +g.align||("ltr"==a.getParentEditor().lang.dir?"left":"right")),'" ');b.push("\x3e\x3ctbody\x3e");for(var c=0;carguments.length)return this._.children.concat();a.splice||(a=[a]);return 2>a.length?this._.children[a[0]]:this._.children[a[0]]&&this._.children[a[0]].getChild? -this._.children[a[0]].getChild(a.slice(1,a.length)):null}},!0);CKEDITOR.ui.dialog.vbox.prototype=new CKEDITOR.ui.dialog.hbox;(function(){var a={build:function(a,b,d){for(var c=b.children,h,f=[],e=[],g=0;gl.length&&(c=a.document.createElement(a.config.enterMode==CKEDITOR.ENTER_P?"p":"div"),k=m.shift(),f.insertNode(c),c.append(new CKEDITOR.dom.text("",a.document)),f.moveToBookmark(k),f.selectNodeContents(c),f.collapse(!0),k=f.createBookmark(),l.push(c),m.unshift(k));b=l[0].getParent();f=[];for(k=0;k]+data-cke-bookmark[^<]*?<\/span>/ig, -"");f&&a(b,c)})}function B(){if("wysiwyg"==b.mode){var a=A("paste");b.getCommand("cut").setState(A("cut"));b.getCommand("copy").setState(A("copy"));b.getCommand("paste").setState(a);b.fire("pasteState",a)}}function A(a){if(F&&a in{paste:1,cut:1})return CKEDITOR.TRISTATE_DISABLED;if("paste"==a)return CKEDITOR.TRISTATE_OFF;a=b.getSelection();var d=a.getRanges();return a.getType()==CKEDITOR.SELECTION_NONE||1==d.length&&d[0].collapsed?CKEDITOR.TRISTATE_DISABLED:CKEDITOR.TRISTATE_OFF}var z=CKEDITOR.plugins.clipboard, -D=0,G=0,F=0;(function(){b.on("key",r);b.on("contentDom",d);b.on("selectionChange",function(a){F=a.data.selection.getRanges()[0].checkReadOnly();B()});b.contextMenu&&b.contextMenu.addListener(function(a,b){F=b.getRanges()[0].checkReadOnly();return{cut:A("cut"),copy:A("copy"),paste:A("paste")}})})();(function(){function a(d,c,f,e,g){var k=b.lang.clipboard[c];b.addCommand(c,f);b.ui.addButton&&b.ui.addButton(d,{label:k,command:c,toolbar:"clipboard,"+e});b.addMenuItems&&b.addMenuItem(c,{label:k,command:c, -group:"clipboard",order:g})}a("Cut","cut",c("cut"),10,1);a("Copy","copy",c("copy"),20,4);a("Paste","paste",f(),30,8)})();b.getClipboardData=function(a,d){function c(a){a.removeListener();a.cancel();d(a.data)}function f(a){a.removeListener();a.cancel();n=!0;d({type:k,dataValue:a.data.dataValue,dataTransfer:a.data.dataTransfer,method:"paste"})}function e(){this.customTitle=a&&a.title}var g=!1,k="auto",n=!1;d||(d=a,a=null);b.on("paste",c,null,null,0);b.on("beforePaste",function(a){a.removeListener(); -g=!0;k=a.data.type},null,null,1E3);!1===C()&&(b.removeListener("paste",c),g&&b.fire("pasteDialog",e)?(b.on("pasteDialogCommit",f),b.on("dialogHide",function(a){a.removeListener();a.data.removeListener("pasteDialogCommit",f);setTimeout(function(){n||d(null)},10)})):d(null))}}function c(a){if(CKEDITOR.env.webkit){if(!a.match(/^[^<]*$/g)&&!a.match(/^(
<\/div>|
[^<]*<\/div>)*$/gi))return"html"}else if(CKEDITOR.env.ie){if(!a.match(/^([^<]|)*$/gi)&&!a.match(/^(

([^<]|)*<\/p>|(\r\n))*$/gi))return"html"}else if(CKEDITOR.env.gecko){if(!a.match(/^([^<]|)*$/gi))return"html"}else return"html"; -return"htmlifiedtext"}function g(a,b){function d(a){return CKEDITOR.tools.repeat("\x3c/p\x3e\x3cp\x3e",~~(a/2))+(1==a%2?"\x3cbr\x3e":"")}b=b.replace(/\s+/g," ").replace(/> +/gi,"\x3cbr\x3e");b=b.replace(/<\/?[A-Z]+>/g,function(a){return a.toLowerCase()});if(b.match(/^[^<]$/))return b;CKEDITOR.env.webkit&&-1(
|)<\/div>)(?!$|(

(
|)<\/div>))/g,"\x3cbr\x3e").replace(/^(
(
|)<\/div>){2}(?!$)/g,"\x3cdiv\x3e\x3c/div\x3e"), -b.match(/
(
|)<\/div>/)&&(b="\x3cp\x3e"+b.replace(/(
(
|)<\/div>)+/g,function(a){return d(a.split("\x3c/div\x3e\x3cdiv\x3e").length+1)})+"\x3c/p\x3e"),b=b.replace(/<\/div>
/g,"\x3cbr\x3e"),b=b.replace(/<\/?div>/g,""));CKEDITOR.env.gecko&&a.enterMode!=CKEDITOR.ENTER_BR&&(CKEDITOR.env.gecko&&(b=b.replace(/^

$/,"\x3cbr\x3e")),-1){2,}/g,function(a){return d(a.length/4)})+"\x3c/p\x3e"));return l(a,b)}function f(){function a(){var b= +this._.children[a[0]].getChild(a.slice(1,a.length)):null}},!0);CKEDITOR.ui.dialog.vbox.prototype=new CKEDITOR.ui.dialog.hbox;(function(){var a={build:function(a,b,d){for(var c=b.children,g,e=[],h=[],f=0;fl.length&&(c=a.document.createElement(a.config.enterMode==CKEDITOR.ENTER_P?"p":"div"),k=m.shift(),h.insertNode(c),c.append(new CKEDITOR.dom.text("",a.document)),h.moveToBookmark(k),h.selectNodeContents(c),h.collapse(!0),k=h.createBookmark(),l.push(c),m.unshift(k));b=l[0].getParent();h=[];for(k=0;k]+data-cke-bookmark[^<]*?<\/span>/ig, +"");e&&a(b,c)})}function D(){if("wysiwyg"==b.mode){var a=y("paste");b.getCommand("cut").setState(y("cut"));b.getCommand("copy").setState(y("copy"));b.getCommand("paste").setState(a);b.fire("pasteState",a)}}function y(a){if(E&&a in{paste:1,cut:1})return CKEDITOR.TRISTATE_DISABLED;if("paste"==a)return CKEDITOR.TRISTATE_OFF;a=b.getSelection();var d=a.getRanges();return a.getType()==CKEDITOR.SELECTION_NONE||1==d.length&&d[0].collapsed?CKEDITOR.TRISTATE_DISABLED:CKEDITOR.TRISTATE_OFF}var z=CKEDITOR.plugins.clipboard, +C=0,G=0,E=0;(function(){b.on("key",t);b.on("contentDom",d);b.on("selectionChange",function(a){E=a.data.selection.getRanges()[0].checkReadOnly();D()});b.contextMenu&&b.contextMenu.addListener(function(a,b){E=b.getRanges()[0].checkReadOnly();return{cut:y("cut"),copy:y("copy"),paste:y("paste")}})})();(function(){function a(d,c,e,h,f){var k=b.lang.clipboard[c];b.addCommand(c,e);b.ui.addButton&&b.ui.addButton(d,{label:k,command:c,toolbar:"clipboard,"+h});b.addMenuItems&&b.addMenuItem(c,{label:k,command:c, +group:"clipboard",order:f})}a("Cut","cut",c("cut"),10,1);a("Copy","copy",c("copy"),20,4);a("Paste","paste",e(),30,8)})();b.getClipboardData=function(a,d){function c(a){a.removeListener();a.cancel();d(a.data)}function e(a){a.removeListener();a.cancel();l=!0;d({type:k,dataValue:a.data.dataValue,dataTransfer:a.data.dataTransfer,method:"paste"})}function h(){this.customTitle=a&&a.title}var f=!1,k="auto",l=!1;d||(d=a,a=null);b.on("paste",c,null,null,0);b.on("beforePaste",function(a){a.removeListener(); +f=!0;k=a.data.type},null,null,1E3);!1===B()&&(b.removeListener("paste",c),f&&b.fire("pasteDialog",h)?(b.on("pasteDialogCommit",e),b.on("dialogHide",function(a){a.removeListener();a.data.removeListener("pasteDialogCommit",e);setTimeout(function(){l||d(null)},10)})):d(null))}}function c(a){if(CKEDITOR.env.webkit){if(!a.match(/^[^<]*$/g)&&!a.match(/^(
<\/div>|
[^<]*<\/div>)*$/gi))return"html"}else if(CKEDITOR.env.ie){if(!a.match(/^([^<]|)*$/gi)&&!a.match(/^(

([^<]|)*<\/p>|(\r\n))*$/gi))return"html"}else if(CKEDITOR.env.gecko){if(!a.match(/^([^<]|)*$/gi))return"html"}else return"html"; +return"htmlifiedtext"}function e(a,b){function d(a){return CKEDITOR.tools.repeat("\x3c/p\x3e\x3cp\x3e",~~(a/2))+(1==a%2?"\x3cbr\x3e":"")}b=b.replace(/\s+/g," ").replace(/> +/gi,"\x3cbr\x3e");b=b.replace(/<\/?[A-Z]+>/g,function(a){return a.toLowerCase()});if(b.match(/^[^<]$/))return b;CKEDITOR.env.webkit&&-1(
|)<\/div>)(?!$|(

(
|)<\/div>))/g,"\x3cbr\x3e").replace(/^(
(
|)<\/div>){2}(?!$)/g,"\x3cdiv\x3e\x3c/div\x3e"), +b.match(/
(
|)<\/div>/)&&(b="\x3cp\x3e"+b.replace(/(
(
|)<\/div>)+/g,function(a){return d(a.split("\x3c/div\x3e\x3cdiv\x3e").length+1)})+"\x3c/p\x3e"),b=b.replace(/<\/div>
/g,"\x3cbr\x3e"),b=b.replace(/<\/?div>/g,""));CKEDITOR.env.gecko&&a.enterMode!=CKEDITOR.ENTER_BR&&(CKEDITOR.env.gecko&&(b=b.replace(/^

$/,"\x3cbr\x3e")),-1){2,}/g,function(a){return d(a.length/4)})+"\x3c/p\x3e"));return l(a,b)}function h(){function a(){var b= {},d;for(d in CKEDITOR.dtd)"$"!=d.charAt(0)&&"div"!=d&&"span"!=d&&(b[d]=1);return b}var b={};return{get:function(d){return"plain-text"==d?b.plainText||(b.plainText=new CKEDITOR.filter("br")):"semantic-content"==d?((d=b.semanticContent)||(d=new CKEDITOR.filter,d.allow({$1:{elements:a(),attributes:!0,styles:!1,classes:!1}}),d=b.semanticContent=d),d):d?new CKEDITOR.filter(d):null}}}function m(a,b,d){b=CKEDITOR.htmlParser.fragment.fromHtml(b);var c=new CKEDITOR.htmlParser.basicWriter;d.applyTo(b,!0,!1, -a.activeEnterMode);b.writeHtml(c);return c.getHtml()}function l(a,b){a.enterMode==CKEDITOR.ENTER_BR?b=b.replace(/(<\/p>

)+/g,function(a){return CKEDITOR.tools.repeat("\x3cbr\x3e",a.length/7*2)}).replace(/<\/?p>/g,""):a.enterMode==CKEDITOR.ENTER_DIV&&(b=b.replace(/<(\/)?p>/g,"\x3c$1div\x3e"));return b}function k(a){a.data.preventDefault();a.data.$.dataTransfer.dropEffect="none"}function b(b){var d=CKEDITOR.plugins.clipboard;b.on("contentDom",function(){function c(d,f,e){f.select();a(b,{dataTransfer:e, -method:"drop"},1);e.sourceEditor.fire("saveSnapshot");e.sourceEditor.editable().extractHtmlFromRange(d);e.sourceEditor.getSelection().selectRanges([d]);e.sourceEditor.fire("saveSnapshot")}function f(c,e){c.select();a(b,{dataTransfer:e,method:"drop"},1);d.resetDragDataTransfer()}function e(a,d,c){var f={$:a.data.$,target:a.data.getTarget()};d&&(f.dragRange=d);c&&(f.dropRange=c);!1===b.fire(a.name,f)&&a.data.preventDefault()}function g(a){a.type!=CKEDITOR.NODE_ELEMENT&&(a=a.getParent());return a.getChildCount()} -var k=b.editable(),l=CKEDITOR.plugins.clipboard.getDropTarget(b),m=b.ui.space("top"),C=b.ui.space("bottom");d.preventDefaultDropOnElement(m);d.preventDefaultDropOnElement(C);k.attachListener(l,"dragstart",e);k.attachListener(b,"dragstart",d.resetDragDataTransfer,d,null,1);k.attachListener(b,"dragstart",function(a){d.initDragDataTransfer(a,b)},null,null,2);k.attachListener(b,"dragstart",function(){var a=d.dragRange=b.getSelection().getRanges()[0];CKEDITOR.env.ie&&10>CKEDITOR.env.version&&(d.dragStartContainerChildCount= -a?g(a.startContainer):null,d.dragEndContainerChildCount=a?g(a.endContainer):null)},null,null,100);k.attachListener(l,"dragend",e);k.attachListener(b,"dragend",d.initDragDataTransfer,d,null,1);k.attachListener(b,"dragend",d.resetDragDataTransfer,d,null,100);k.attachListener(l,"dragover",function(a){var b=a.data.getTarget();b&&b.is&&b.is("html")?a.data.preventDefault():CKEDITOR.env.ie&&CKEDITOR.plugins.clipboard.isFileApiSupported&&a.data.$.dataTransfer.types.contains("Files")&&a.data.preventDefault()}); -k.attachListener(l,"drop",function(a){if(!a.data.$.defaultPrevented){a.data.preventDefault();var c=a.data.getTarget();if(!c.isReadOnly()||c.type==CKEDITOR.NODE_ELEMENT&&c.is("html")){var c=d.getRangeAtDropPosition(a,b),f=d.dragRange;c&&e(a,f,c)}}},null,null,9999);k.attachListener(b,"drop",d.initDragDataTransfer,d,null,1);k.attachListener(b,"drop",function(a){if(a=a.data){var e=a.dropRange,g=a.dragRange,k=a.dataTransfer;k.getTransferType(b)==CKEDITOR.DATA_TRANSFER_INTERNAL?setTimeout(function(){d.internalDrop(g, -e,k,b)},0):k.getTransferType(b)==CKEDITOR.DATA_TRANSFER_CROSS_EDITORS?c(g,e,k):f(e,k)}},null,null,9999)})}CKEDITOR.plugins.add("clipboard",{requires:"dialog",init:function(a){var d,k=f();a.config.forcePasteAsPlainText?d="plain-text":a.config.pasteFilter?d=a.config.pasteFilter:!CKEDITOR.env.webkit||"pasteFilter"in a.config||(d="semantic-content");a.pasteFilter=k.get(d);e(a);b(a);CKEDITOR.dialog.add("paste",CKEDITOR.getUrl(this.path+"dialogs/paste.js"));a.on("paste",function(b){b.data.dataTransfer|| +a.activeEnterMode);b.writeHtml(c);return c.getHtml()}function l(a,b){a.enterMode==CKEDITOR.ENTER_BR?b=b.replace(/(<\/p>

)+/g,function(a){return CKEDITOR.tools.repeat("\x3cbr\x3e",a.length/7*2)}).replace(/<\/?p>/g,""):a.enterMode==CKEDITOR.ENTER_DIV&&(b=b.replace(/<(\/)?p>/g,"\x3c$1div\x3e"));return b}function k(a){a.data.preventDefault();a.data.$.dataTransfer.dropEffect="none"}function b(b){var d=CKEDITOR.plugins.clipboard;b.on("contentDom",function(){function c(d,e,h){e.select();a(b,{dataTransfer:h, +method:"drop"},1);h.sourceEditor.fire("saveSnapshot");h.sourceEditor.editable().extractHtmlFromRange(d);h.sourceEditor.getSelection().selectRanges([d]);h.sourceEditor.fire("saveSnapshot")}function e(c,h){c.select();a(b,{dataTransfer:h,method:"drop"},1);d.resetDragDataTransfer()}function h(a,d,c){var e={$:a.data.$,target:a.data.getTarget()};d&&(e.dragRange=d);c&&(e.dropRange=c);!1===b.fire(a.name,e)&&a.data.preventDefault()}function f(a){a.type!=CKEDITOR.NODE_ELEMENT&&(a=a.getParent());return a.getChildCount()} +var k=b.editable(),l=CKEDITOR.plugins.clipboard.getDropTarget(b),m=b.ui.space("top"),B=b.ui.space("bottom");d.preventDefaultDropOnElement(m);d.preventDefaultDropOnElement(B);k.attachListener(l,"dragstart",h);k.attachListener(b,"dragstart",d.resetDragDataTransfer,d,null,1);k.attachListener(b,"dragstart",function(a){d.initDragDataTransfer(a,b)},null,null,2);k.attachListener(b,"dragstart",function(){var a=d.dragRange=b.getSelection().getRanges()[0];CKEDITOR.env.ie&&10>CKEDITOR.env.version&&(d.dragStartContainerChildCount= +a?f(a.startContainer):null,d.dragEndContainerChildCount=a?f(a.endContainer):null)},null,null,100);k.attachListener(l,"dragend",h);k.attachListener(b,"dragend",d.initDragDataTransfer,d,null,1);k.attachListener(b,"dragend",d.resetDragDataTransfer,d,null,100);k.attachListener(l,"dragover",function(a){var b=a.data.getTarget();b&&b.is&&b.is("html")?a.data.preventDefault():CKEDITOR.env.ie&&CKEDITOR.plugins.clipboard.isFileApiSupported&&a.data.$.dataTransfer.types.contains("Files")&&a.data.preventDefault()}); +k.attachListener(l,"drop",function(a){if(!a.data.$.defaultPrevented){a.data.preventDefault();var c=a.data.getTarget();if(!c.isReadOnly()||c.type==CKEDITOR.NODE_ELEMENT&&c.is("html")){var c=d.getRangeAtDropPosition(a,b),e=d.dragRange;c&&h(a,e,c)}}},null,null,9999);k.attachListener(b,"drop",d.initDragDataTransfer,d,null,1);k.attachListener(b,"drop",function(a){if(a=a.data){var h=a.dropRange,f=a.dragRange,k=a.dataTransfer;k.getTransferType(b)==CKEDITOR.DATA_TRANSFER_INTERNAL?setTimeout(function(){d.internalDrop(f, +h,k,b)},0):k.getTransferType(b)==CKEDITOR.DATA_TRANSFER_CROSS_EDITORS?c(f,h,k):e(h,k)}},null,null,9999)})}CKEDITOR.plugins.add("clipboard",{requires:"dialog",init:function(a){var d,k=h();a.config.forcePasteAsPlainText?d="plain-text":a.config.pasteFilter?d=a.config.pasteFilter:!CKEDITOR.env.webkit||"pasteFilter"in a.config||(d="semantic-content");a.pasteFilter=k.get(d);f(a);b(a);CKEDITOR.dialog.add("paste",CKEDITOR.getUrl(this.path+"dialogs/paste.js"));a.on("paste",function(b){b.data.dataTransfer|| (b.data.dataTransfer=new CKEDITOR.plugins.clipboard.dataTransfer);if(!b.data.dataValue){var d=b.data.dataTransfer,c=d.getData("text/html");if(c)b.data.dataValue=c,b.data.type="html";else if(c=d.getData("text/plain"))b.data.dataValue=a.editable().transformPlainTextToHtml(c),b.data.type="text"}},null,null,1);a.on("paste",function(a){var b=a.data.dataValue,d=CKEDITOR.dtd.$block;-1 <\/span>/gi," "),"html"!=a.data.type&&(b=b.replace(/]*>([^<]*)<\/span>/gi, -function(a,b){return b.replace(/\t/g,"\x26nbsp;\x26nbsp; \x26nbsp;")})),-1/,"")),b=b.replace(/(<[^>]+) class="Apple-[^"]*"/gi,"$1"));if(b.match(/^<[^<]+cke_(editable|contents)/i)){var c,f,h=new CKEDITOR.dom.element("div");for(h.setHtml(b);1==h.getChildCount()&&(c=h.getFirst())&&c.type==CKEDITOR.NODE_ELEMENT&&(c.hasClass("cke_editable")|| -c.hasClass("cke_contents"));)h=f=c;f&&(b=f.getHtml().replace(/
$/i,""))}CKEDITOR.env.ie?b=b.replace(/^ (?: |\r\n)?<(\w+)/g,function(b,c){return c.toLowerCase()in d?(a.data.preSniffing="html","\x3c"+c):b}):CKEDITOR.env.webkit?b=b.replace(/<\/(\w+)>


<\/div>$/,function(b,c){return c in d?(a.data.endsWithEOL=1,"\x3c/"+c+"\x3e"):b}):CKEDITOR.env.gecko&&(b=b.replace(/(\s)
$/,"$1"));a.data.dataValue=b},null,null,3);a.on("paste",function(b){b=b.data;var d=b.type,f=b.dataValue,e,l=a.config.clipboard_defaultContentType|| -"html",p=b.dataTransfer.getTransferType(a);e="html"==d||"html"==b.preSniffing?"html":c(f);"htmlifiedtext"==e&&(f=g(a.config,f));"text"==d&&"html"==e?f=m(a,f,k.get("plain-text")):p==CKEDITOR.DATA_TRANSFER_EXTERNAL&&a.pasteFilter&&!b.dontFilter&&(f=m(a,f,a.pasteFilter));b.startsWithEOL&&(f='\x3cbr data-cke-eol\x3d"1"\x3e'+f);b.endsWithEOL&&(f+='\x3cbr data-cke-eol\x3d"1"\x3e');"auto"==d&&(d="html"==e||"html"==l?"html":"text");b.type=d;b.dataValue=f;delete b.preSniffing;delete b.startsWithEOL;delete b.endsWithEOL}, +function(a,b){return b.replace(/\t/g,"\x26nbsp;\x26nbsp; \x26nbsp;")})),-1/,"")),b=b.replace(/(<[^>]+) class="Apple-[^"]*"/gi,"$1"));if(b.match(/^<[^<]+cke_(editable|contents)/i)){var c,g,e=new CKEDITOR.dom.element("div");for(e.setHtml(b);1==e.getChildCount()&&(c=e.getFirst())&&c.type==CKEDITOR.NODE_ELEMENT&&(c.hasClass("cke_editable")|| +c.hasClass("cke_contents"));)e=g=c;g&&(b=g.getHtml().replace(/
$/i,""))}CKEDITOR.env.ie?b=b.replace(/^ (?: |\r\n)?<(\w+)/g,function(b,c){return c.toLowerCase()in d?(a.data.preSniffing="html","\x3c"+c):b}):CKEDITOR.env.webkit?b=b.replace(/<\/(\w+)>

<\/div>$/,function(b,c){return c in d?(a.data.endsWithEOL=1,"\x3c/"+c+"\x3e"):b}):CKEDITOR.env.gecko&&(b=b.replace(/(\s)
$/,"$1"));a.data.dataValue=b},null,null,3);a.on("paste",function(b){b=b.data;var d=b.type,h=b.dataValue,f,l=a.config.clipboard_defaultContentType|| +"html",p=b.dataTransfer.getTransferType(a);f="html"==d||"html"==b.preSniffing?"html":c(h);"htmlifiedtext"==f&&(h=e(a.config,h));"text"==d&&"html"==f?h=m(a,h,k.get("plain-text")):p==CKEDITOR.DATA_TRANSFER_EXTERNAL&&a.pasteFilter&&!b.dontFilter&&(h=m(a,h,a.pasteFilter));b.startsWithEOL&&(h='\x3cbr data-cke-eol\x3d"1"\x3e'+h);b.endsWithEOL&&(h+='\x3cbr data-cke-eol\x3d"1"\x3e');"auto"==d&&(d="html"==f||"html"==l?"html":"text");b.type=d;b.dataValue=h;delete b.preSniffing;delete b.startsWithEOL;delete b.endsWithEOL}, null,null,6);a.on("paste",function(b){b=b.data;b.dataValue&&(a.insertHtml(b.dataValue,b.type,b.range),setTimeout(function(){a.fire("afterPaste")},0))},null,null,1E3);a.on("pasteDialog",function(b){setTimeout(function(){a.openDialog("paste",b.data)},0)})}});CKEDITOR.plugins.clipboard={isCustomCopyCutSupported:!CKEDITOR.env.ie&&!CKEDITOR.env.iOS,isCustomDataTypesSupported:!CKEDITOR.env.ie,isFileApiSupported:!CKEDITOR.env.ie||9CKEDITOR.env.version||b.isInline()?b:a.document},fixSplitNodesAfterDrop:function(a,b,d,c){function f(a,d,c){var h=a;h.type==CKEDITOR.NODE_TEXT&&(h=a.getParent());if(h.equals(d)&&c!=d.getChildCount())return a= -b.startContainer.getChild(b.startOffset-1),d=b.startContainer.getChild(b.startOffset),a&&a.type==CKEDITOR.NODE_TEXT&&d&&d.type==CKEDITOR.NODE_TEXT&&(c=a.getLength(),a.setText(a.getText()+d.getText()),d.remove(),b.setStart(a,c),b.collapse(!0)),!0}var e=b.startContainer;"number"==typeof c&&"number"==typeof d&&e.type==CKEDITOR.NODE_ELEMENT&&(f(a.startContainer,e,d)||f(a.endContainer,e,c))},isDropRangeAffectedByDragRange:function(a,b){var d=b.startContainer,c=b.endOffset;return a.endContainer.equals(d)&& -a.endOffset<=c||a.startContainer.getParent().equals(d)&&a.startContainer.getIndex()CKEDITOR.env.version&&this.fixSplitNodesAfterDrop(b,d,e.dragStartContainerChildCount,e.dragEndContainerChildCount);(l=this.isDropRangeAffectedByDragRange(b,d))||(k=b.createBookmark(!1)); -e=d.clone().createBookmark(!1);l&&(k=b.createBookmark(!1));b=k.startNode;d=k.endNode;l=e.startNode;d&&b.getPosition(l)&CKEDITOR.POSITION_PRECEDING&&d.getPosition(l)&CKEDITOR.POSITION_FOLLOWING&&l.insertBefore(b);b=f.createRange();b.moveToBookmark(k);g.extractHtmlFromRange(b,1);d=f.createRange();d.moveToBookmark(e);a(f,{dataTransfer:c,method:"drop",range:d},1);f.fire("unlockSnapshot")},getRangeAtDropPosition:function(a,b){var d=a.data.$,c=d.clientX,f=d.clientY,e=b.getSelection(!0).getRanges()[0],g= -b.createRange();if(a.data.testRange)return a.data.testRange;if(document.caretRangeFromPoint)d=b.document.$.caretRangeFromPoint(c,f),g.setStart(CKEDITOR.dom.node(d.startContainer),d.startOffset),g.collapse(!0);else if(d.rangeParent)g.setStart(CKEDITOR.dom.node(d.rangeParent),d.rangeOffset),g.collapse(!0);else{if(CKEDITOR.env.ie&&8l&& -!k;l++){if(!k)try{d.moveToPoint(c,f-l),k=!0}catch(m){}if(!k)try{d.moveToPoint(c,f+l),k=!0}catch(r){}}if(k){var x="cke-temp-"+(new Date).getTime();d.pasteHTML('\x3cspan id\x3d"'+x+'"\x3e​\x3c/span\x3e');var B=b.document.getById(x);g.moveToPosition(B,CKEDITOR.POSITION_BEFORE_START);B.remove()}else{var A=b.document.$.elementFromPoint(c,f),z=new CKEDITOR.dom.element(A),D;if(z.equals(b.editable())||"html"==z.getName())return e&&e.startContainer&&!e.startContainer.equals(b.editable())?e:null;D=z.getClientRect(); -cCKEDITOR.env.version||b.isInline()?b:a.document},fixSplitNodesAfterDrop:function(a,b,d,c){function e(a,d,c){var g=a;g.type==CKEDITOR.NODE_TEXT&&(g=a.getParent());if(g.equals(d)&&c!=d.getChildCount())return a= +b.startContainer.getChild(b.startOffset-1),d=b.startContainer.getChild(b.startOffset),a&&a.type==CKEDITOR.NODE_TEXT&&d&&d.type==CKEDITOR.NODE_TEXT&&(c=a.getLength(),a.setText(a.getText()+d.getText()),d.remove(),b.setStart(a,c),b.collapse(!0)),!0}var h=b.startContainer;"number"==typeof c&&"number"==typeof d&&h.type==CKEDITOR.NODE_ELEMENT&&(e(a.startContainer,h,d)||e(a.endContainer,h,c))},isDropRangeAffectedByDragRange:function(a,b){var d=b.startContainer,c=b.endOffset;return a.endContainer.equals(d)&& +a.endOffset<=c||a.startContainer.getParent().equals(d)&&a.startContainer.getIndex()CKEDITOR.env.version&&this.fixSplitNodesAfterDrop(b,d,h.dragStartContainerChildCount,h.dragEndContainerChildCount);(l=this.isDropRangeAffectedByDragRange(b,d))||(k=b.createBookmark(!1)); +h=d.clone().createBookmark(!1);l&&(k=b.createBookmark(!1));b=k.startNode;d=k.endNode;l=h.startNode;d&&b.getPosition(l)&CKEDITOR.POSITION_PRECEDING&&d.getPosition(l)&CKEDITOR.POSITION_FOLLOWING&&l.insertBefore(b);b=e.createRange();b.moveToBookmark(k);f.extractHtmlFromRange(b,1);d=e.createRange();d.moveToBookmark(h);a(e,{dataTransfer:c,method:"drop",range:d},1);e.fire("unlockSnapshot")},getRangeAtDropPosition:function(a,b){var d=a.data.$,c=d.clientX,e=d.clientY,h=b.getSelection(!0).getRanges()[0],f= +b.createRange();if(a.data.testRange)return a.data.testRange;if(document.caretRangeFromPoint)d=b.document.$.caretRangeFromPoint(c,e),f.setStart(CKEDITOR.dom.node(d.startContainer),d.startOffset),f.collapse(!0);else if(d.rangeParent)f.setStart(CKEDITOR.dom.node(d.rangeParent),d.rangeOffset),f.collapse(!0);else{if(CKEDITOR.env.ie&&8l&& +!k;l++){if(!k)try{d.moveToPoint(c,e-l),k=!0}catch(m){}if(!k)try{d.moveToPoint(c,e+l),k=!0}catch(t){}}if(k){var x="cke-temp-"+(new Date).getTime();d.pasteHTML('\x3cspan id\x3d"'+x+'"\x3e​\x3c/span\x3e');var D=b.document.getById(x);f.moveToPosition(D,CKEDITOR.POSITION_BEFORE_START);D.remove()}else{var y=b.document.$.elementFromPoint(c,e),z=new CKEDITOR.dom.element(y),C;if(z.equals(b.editable())||"html"==z.getName())return h&&h.startContainer&&!h.startContainer.equals(b.editable())?h:null;C=z.getClientRect(); +c/i,bodyRegExp:/([\s\S]*)<\/body>/i, fragmentRegExp:/\x3c!--(?:Start|End)Fragment--\x3e/g,data:{},files:[],normalizeType:function(a){a=a.toLowerCase();return"text"==a||"text/plain"==a?"Text":"url"==a?"URL":a}};this.id=this.getData(d);this.id||(this.id="Text"==d?"":"cke-"+CKEDITOR.tools.getUniqueId());if("Text"!=d)try{this.$.setData(d,this.id)}catch(c){}b&&(this.sourceEditor=b,this.setData("text/html",b.getSelectedHtml(1)),"Text"==d||this.getData("text/plain")||this.setData("text/plain",b.getSelection().getSelectedText()))};CKEDITOR.DATA_TRANSFER_INTERNAL= 1;CKEDITOR.DATA_TRANSFER_CROSS_EDITORS=2;CKEDITOR.DATA_TRANSFER_EXTERNAL=3;CKEDITOR.plugins.clipboard.dataTransfer.prototype={getData:function(a){a=this._.normalizeType(a);var b=this._.data[a];if(void 0===b||null===b||""===b)try{b=this.$.getData(a)}catch(d){}if(void 0===b||null===b||""===b)b="";"text/html"==a?(b=b.replace(this._.metaRegExp,""),(a=this._.bodyRegExp.exec(b))&&a.length&&(b=a[1],b=b.replace(this._.fragmentRegExp,""))):"Text"==a&&CKEDITOR.env.gecko&&this.getFilesCount()&&"file://"==b.substring(0, @@ -620,418 +624,424 @@ fragmentRegExp:/\x3c!--(?:Start|End)Fragment--\x3e/g,data:{},files:[],normalizeT c)}if(this.$){var b=this,d,c;if(CKEDITOR.plugins.clipboard.isCustomDataTypesSupported){if(this.$.types)for(d=0;df?q+f:c.width>f?q-a.left:q-a.right+c.width):hf?q-f:c.width>f?q-a.right+c.width:q-a.left);f=a.top;c.height-a.tope?w-e:c.height>e?w-a.bottom+c.height:w-a.top);CKEDITOR.env.ie&&(c=a=new CKEDITOR.dom.element(p.$.offsetParent),"html"==c.getName()&& +n&&(q-=p.$.offsetWidth);p.setStyle("left",q+"px");var c=b.element.getWindow(),a=p.$.getBoundingClientRect(),c=c.getViewPaneSize(),e=a.width||a.right-a.left,g=a.height||a.bottom-a.top,h=n?a.right:c.width-a.left,f=n?c.width-a.right:a.left;n?he?q+e:c.width>e?q-a.left:q-a.right+c.width):he?q-e:c.width>e?q-a.right+c.width:q-a.left);e=a.top;c.height-a.topg?w-g:c.height>g?w-a.bottom+c.height:w-a.top);CKEDITOR.env.ie&&(c=a=new CKEDITOR.dom.element(p.$.offsetParent),"html"==c.getName()&& (c=c.getDocument().getBody()),"rtl"==c.getComputedStyle("direction")&&(q=CKEDITOR.env.ie8Compat?q-2*p.getDocument().getDocumentElement().$.scrollLeft:q-(a.$.scrollWidth-a.$.clientWidth)));var a=p.getFirst(),l;(l=a.getCustomData("activePanel"))&&l.onHide&&l.onHide.call(this,1);a.setCustomData("activePanel",this);p.setStyles({top:w+"px",left:q+"px"});p.setOpacity(1);k&&k()},this);b.isLoaded?a():b.onLoad=a;CKEDITOR.tools.setTimeout(function(){var a=CKEDITOR.env.webkit&&CKEDITOR.document.getWindow().getScrollPosition().y; this.focus();d.element.focus();CKEDITOR.env.webkit&&(CKEDITOR.document.getBody().$.scrollTop=a);this.allowBlur(!0);this._.editor.fire("panelShow",this)},0,this)},CKEDITOR.env.air?200:0,this);this.visible=1;this.onShow&&this.onShow.call(this)},reposition:function(){var a=this._.showBlockParams;this.visible&&this._.showBlockParams&&(this.hide(),this.showBlock.apply(this,a))},focus:function(){if(CKEDITOR.env.webkit){var a=CKEDITOR.document.getActive();a&&!a.equals(this._.iframe)&&a.$.blur()}(this._.lastFocused|| this._.iframe.getFrameDocument().getWindow()).focus()},blur:function(){var a=this._.iframe.getFrameDocument().getActive();a&&a.is("a")&&(this._.lastFocused=a)},hide:function(a){if(this.visible&&(!this.onHide||!0!==this.onHide.call(this))){this.hideChild();CKEDITOR.env.gecko&&this._.iframe.getFrameDocument().$.activeElement.blur();this.element.setStyle("display","none");this.visible=0;this.element.getFirst().removeCustomData("activePanel");if(a=a&&this._.returnFocus)CKEDITOR.env.webkit&&a.type&&a.getWindow().$.focus(), -a.focus();delete this._.lastFocused;this._.showBlockParams=null;this._.editor.fire("panelHide",this)}},allowBlur:function(a){var e=this._.panel;void 0!==a&&(e.allowBlur=a);return e.allowBlur},showAsChild:function(a,e,f,m,l,k){if(this._.activeChild!=a||a._.panel._.offsetParentId!=f.getId())this.hideChild(),a.onHide=CKEDITOR.tools.bind(function(){CKEDITOR.tools.setTimeout(function(){this._.focused||this.hide()},0,this)},this),this._.activeChild=a,this._.focused=!1,a.showBlock(e,f,m,l,k),this.blur(), -(CKEDITOR.env.ie7Compat||CKEDITOR.env.ie6Compat)&&setTimeout(function(){a.element.getChild(0).$.style.cssText+=""},100)},hideChild:function(a){var e=this._.activeChild;e&&(delete e.onHide,delete this._.activeChild,e.hide(),a&&this.focus())}}});CKEDITOR.on("instanceDestroyed",function(){var a=CKEDITOR.tools.isEmpty(CKEDITOR.instances),g;for(g in e){var f=e[g];a?f.destroy():f.element.hide()}a&&(e={})})}(),CKEDITOR.plugins.add("menu",{requires:"floatpanel",beforeInit:function(a){for(var e=a.config.menu_groups.split(","), -c=a._.menuGroups={},g=a._.menuItems={},f=0;fc.group?1:a.orderc.order?1:0})}var e='\x3cspan class\x3d"cke_menuitem"\x3e\x3ca id\x3d"{id}" class\x3d"cke_menubutton cke_menubutton__{name} cke_menubutton_{state} {cls}" href\x3d"{href}" title\x3d"{title}" tabindex\x3d"-1"_cke_focus\x3d1 hidefocus\x3d"true" role\x3d"{role}" aria-haspopup\x3d"{hasPopup}" aria-disabled\x3d"{disabled}" {ariaChecked}'; -CKEDITOR.env.gecko&&CKEDITOR.env.mac&&(e+=' onkeypress\x3d"return false;"');CKEDITOR.env.gecko&&(e+=' onblur\x3d"this.style.cssText \x3d this.style.cssText;"');var e=e+(' onmouseover\x3d"CKEDITOR.tools.callFunction({hoverFn},{index});" onmouseout\x3d"CKEDITOR.tools.callFunction({moveOutFn},{index});" '+(CKEDITOR.env.ie?'onclick\x3d"return false;" onmouseup':"onclick")+'\x3d"CKEDITOR.tools.callFunction({clickFn},{index}); return false;"\x3e'),c=CKEDITOR.addTemplate("menuItem",e+'\x3cspan class\x3d"cke_menubutton_inner"\x3e\x3cspan class\x3d"cke_menubutton_icon"\x3e\x3cspan class\x3d"cke_button_icon cke_button__{iconName}_icon" style\x3d"{iconStyle}"\x3e\x3c/span\x3e\x3c/span\x3e\x3cspan class\x3d"cke_menubutton_label"\x3e{label}\x3c/span\x3e{arrowHtml}\x3c/span\x3e\x3c/a\x3e\x3c/span\x3e'), -g=CKEDITOR.addTemplate("menuArrow",'\x3cspan class\x3d"cke_menuarrow"\x3e\x3cspan\x3e{label}\x3c/span\x3e\x3c/span\x3e');CKEDITOR.menu=CKEDITOR.tools.createClass({$:function(a,c){c=this._.definition=c||{};this.id=CKEDITOR.tools.getNextId();this.editor=a;this.items=[];this._.listeners=[];this._.level=c.level||1;var e=CKEDITOR.tools.extend({},c.panel,{css:[CKEDITOR.skin.getPath("editor")],level:this._.level-1,block:{}}),g=e.block.attributes=e.attributes||{};!g.role&&(g.role="menu");this._.panelDefinition= -e},_:{onShow:function(){var a=this.editor.getSelection(),c=a&&a.getStartElement(),e=this.editor.elementPath(),g=this._.listeners;this.removeAll();for(var b=0;bc.group?1:a.orderc.order?1:0})}var f='\x3cspan class\x3d"cke_menuitem"\x3e\x3ca id\x3d"{id}" class\x3d"cke_menubutton cke_menubutton__{name} cke_menubutton_{state} {cls}" href\x3d"{href}" title\x3d"{title}" tabindex\x3d"-1"_cke_focus\x3d1 hidefocus\x3d"true" role\x3d"{role}" aria-haspopup\x3d"{hasPopup}" aria-disabled\x3d"{disabled}" {ariaChecked}'; +CKEDITOR.env.gecko&&CKEDITOR.env.mac&&(f+=' onkeypress\x3d"return false;"');CKEDITOR.env.gecko&&(f+=' onblur\x3d"this.style.cssText \x3d this.style.cssText;"');var f=f+(' onmouseover\x3d"CKEDITOR.tools.callFunction({hoverFn},{index});" onmouseout\x3d"CKEDITOR.tools.callFunction({moveOutFn},{index});" '+(CKEDITOR.env.ie?'onclick\x3d"return false;" onmouseup':"onclick")+'\x3d"CKEDITOR.tools.callFunction({clickFn},{index}); return false;"\x3e'),c=CKEDITOR.addTemplate("menuItem",f+'\x3cspan class\x3d"cke_menubutton_inner"\x3e\x3cspan class\x3d"cke_menubutton_icon"\x3e\x3cspan class\x3d"cke_button_icon cke_button__{iconName}_icon" style\x3d"{iconStyle}"\x3e\x3c/span\x3e\x3c/span\x3e\x3cspan class\x3d"cke_menubutton_label"\x3e{label}\x3c/span\x3e{arrowHtml}\x3c/span\x3e\x3c/a\x3e\x3c/span\x3e'), +e=CKEDITOR.addTemplate("menuArrow",'\x3cspan class\x3d"cke_menuarrow"\x3e\x3cspan\x3e{label}\x3c/span\x3e\x3c/span\x3e');CKEDITOR.menu=CKEDITOR.tools.createClass({$:function(a,c){c=this._.definition=c||{};this.id=CKEDITOR.tools.getNextId();this.editor=a;this.items=[];this._.listeners=[];this._.level=c.level||1;var e=CKEDITOR.tools.extend({},c.panel,{css:[CKEDITOR.skin.getPath("editor")],level:this._.level-1,block:{}}),f=e.block.attributes=e.attributes||{};!f.role&&(f.role="menu");this._.panelDefinition= +e},_:{onShow:function(){var a=this.editor.getSelection(),c=a&&a.getStartElement(),e=this.editor.elementPath(),f=this._.listeners;this.removeAll();for(var b=0;bb.width&&(g.resize_minWidth=b.width);g.resize_minHeight>b.height&&(g.resize_minHeight=b.height);CKEDITOR.document.on("mousemove",e);CKEDITOR.document.on("mouseup",c);a.document&&(a.document.on("mousemove",e),a.document.on("mouseup",c));d.preventDefault&&d.preventDefault()});a.on("destroy",function(){CKEDITOR.tools.removeFunction(p)});a.on("uiSpace",function(b){if("bottom"==b.data.space){var c= -"";d&&!h&&(c=" cke_resizer_horizontal");!d&&h&&(c=" cke_resizer_vertical");var e='\x3cspan id\x3d"'+f+'" class\x3d"cke_resizer'+c+" cke_resizer_"+m+'" title\x3d"'+CKEDITOR.tools.htmlEncode(a.lang.common.resize)+'" onmousedown\x3d"CKEDITOR.tools.callFunction('+p+', event)"\x3e'+("ltr"==m?"◢":"◣")+"\x3c/span\x3e";"ltr"==m&&"ltr"==c?b.data.html+=e:b.data.html=e+b.data.html}},a,null,100);a.on("maximize",function(b){a.ui.space("resizer")[b.data==CKEDITOR.TRISTATE_ON?"hide":"show"]()})}}}),function(){var a= +f){a.on("contextmenu",function(a){a=a.data;var e=CKEDITOR.env.webkit?c:CKEDITOR.env.mac?a.$.metaKey:a.$.ctrlKey;if(!f||!e){a.preventDefault();if(CKEDITOR.env.mac&&CKEDITOR.env.webkit){var e=this.editor,l=(new CKEDITOR.dom.elementPath(a.getTarget(),e.editable())).contains(function(a){return a.hasAttribute("contenteditable")},!0);l&&"false"==l.getAttribute("contenteditable")&&e.getSelection().fake(l)}var l=a.getTarget().getDocument(),k=a.getTarget().getDocument().getDocumentElement(),e=!l.equals(CKEDITOR.document), +l=l.getWindow().getScrollPosition(),b=e?a.$.clientX:a.$.pageX||l.x+a.$.clientX,d=e?a.$.clientY:a.$.pageY||l.y+a.$.clientY;CKEDITOR.tools.setTimeout(function(){this.open(k,null,b,d)},CKEDITOR.env.ie?200:0,this)}},this);if(CKEDITOR.env.webkit){var c,e=function(){c=0};a.on("keydown",function(a){c=CKEDITOR.env.mac?a.data.$.metaKey:a.data.$.ctrlKey});a.on("keyup",e);a.on("contextmenu",e)}},open:function(a,f,c,e){this.editor.focus();a=a||CKEDITOR.document.getDocumentElement();this.editor.selectionChange(1); +this.show(a,f,c,e)}}})},beforeInit:function(a){var f=a.contextMenu=new CKEDITOR.plugins.contextMenu(a);a.on("contentDom",function(){f.addTarget(a.editable(),!1!==a.config.browserContextMenuOnCtrl)});a.addCommand("contextMenu",{exec:function(){a.contextMenu.open(a.document.getBody())}});a.setKeystroke(CKEDITOR.SHIFT+121,"contextMenu");a.setKeystroke(CKEDITOR.CTRL+CKEDITOR.SHIFT+121,"contextMenu")}}),CKEDITOR.plugins.add("resize",{init:function(a){function f(c){var h=b.width,f=b.height,l=h+(c.data.$.screenX- +k.x)*("rtl"==m?-1:1);c=f+(c.data.$.screenY-k.y);d&&(h=Math.max(e.resize_minWidth,Math.min(l,e.resize_maxWidth)));g&&(f=Math.max(e.resize_minHeight,Math.min(c,e.resize_maxHeight)));a.resize(d?h:null,f)}function c(){CKEDITOR.document.removeListener("mousemove",f);CKEDITOR.document.removeListener("mouseup",c);a.document&&(a.document.removeListener("mousemove",f),a.document.removeListener("mouseup",c))}var e=a.config,h=a.ui.spaceId("resizer"),m=a.element?a.element.getDirection(1):"ltr";!e.resize_dir&& +(e.resize_dir="vertical");void 0===e.resize_maxWidth&&(e.resize_maxWidth=3E3);void 0===e.resize_maxHeight&&(e.resize_maxHeight=3E3);void 0===e.resize_minWidth&&(e.resize_minWidth=750);void 0===e.resize_minHeight&&(e.resize_minHeight=250);if(!1!==e.resize_enabled){var l=null,k,b,d=("both"==e.resize_dir||"horizontal"==e.resize_dir)&&e.resize_minWidth!=e.resize_maxWidth,g=("both"==e.resize_dir||"vertical"==e.resize_dir)&&e.resize_minHeight!=e.resize_maxHeight,p=CKEDITOR.tools.addFunction(function(d){l|| +(l=a.getResizable());b={width:l.$.offsetWidth||0,height:l.$.offsetHeight||0};k={x:d.screenX,y:d.screenY};e.resize_minWidth>b.width&&(e.resize_minWidth=b.width);e.resize_minHeight>b.height&&(e.resize_minHeight=b.height);CKEDITOR.document.on("mousemove",f);CKEDITOR.document.on("mouseup",c);a.document&&(a.document.on("mousemove",f),a.document.on("mouseup",c));d.preventDefault&&d.preventDefault()});a.on("destroy",function(){CKEDITOR.tools.removeFunction(p)});a.on("uiSpace",function(b){if("bottom"==b.data.space){var c= +"";d&&!g&&(c=" cke_resizer_horizontal");!d&&g&&(c=" cke_resizer_vertical");var e='\x3cspan id\x3d"'+h+'" class\x3d"cke_resizer'+c+" cke_resizer_"+m+'" title\x3d"'+CKEDITOR.tools.htmlEncode(a.lang.common.resize)+'" onmousedown\x3d"CKEDITOR.tools.callFunction('+p+', event)"\x3e'+("ltr"==m?"◢":"◣")+"\x3c/span\x3e";"ltr"==m&&"ltr"==c?b.data.html+=e:b.data.html=e+b.data.html}},a,null,100);a.on("maximize",function(b){a.ui.space("resizer")[b.data==CKEDITOR.TRISTATE_ON?"hide":"show"]()})}}}),function(){var a= '\x3ca id\x3d"{id}" class\x3d"cke_button cke_button__{name} cke_button_{state} {cls}"'+(CKEDITOR.env.gecko&&!CKEDITOR.env.hc?"":" href\x3d\"javascript:void('{titleJs}')\"")+' title\x3d"{title}" tabindex\x3d"-1" hidefocus\x3d"true" role\x3d"button" aria-labelledby\x3d"{id}_label" aria-haspopup\x3d"{hasArrow}" aria-disabled\x3d"{ariaDisabled}"';CKEDITOR.env.gecko&&CKEDITOR.env.mac&&(a+=' onkeypress\x3d"return false;"');CKEDITOR.env.gecko&&(a+=' onblur\x3d"this.style.cssText \x3d this.style.cssText;"'); var a=a+(' onkeydown\x3d"return CKEDITOR.tools.callFunction({keydownFn},event);" onfocus\x3d"return CKEDITOR.tools.callFunction({focusFn},event);" '+(CKEDITOR.env.ie?'onclick\x3d"return false;" onmouseup':"onclick")+'\x3d"CKEDITOR.tools.callFunction({clickFn},this);return false;"\x3e\x3cspan class\x3d"cke_button_icon cke_button__{iconName}_icon" style\x3d"{style}"'),a=a+'\x3e\x26nbsp;\x3c/span\x3e\x3cspan id\x3d"{id}_label" class\x3d"cke_button_label cke_button__{name}_label" aria-hidden\x3d"false"\x3e{label}\x3c/span\x3e{arrowHtml}\x3c/a\x3e', -e=CKEDITOR.addTemplate("buttonArrow",'\x3cspan class\x3d"cke_button_arrow"\x3e'+(CKEDITOR.env.hc?"\x26#9660;":"")+"\x3c/span\x3e"),c=CKEDITOR.addTemplate("button",a);CKEDITOR.plugins.add("button",{beforeInit:function(a){a.ui.addHandler(CKEDITOR.UI_BUTTON,CKEDITOR.ui.button.handler)}});CKEDITOR.UI_BUTTON="button";CKEDITOR.ui.button=function(a){CKEDITOR.tools.extend(this,a,{title:a.label,click:a.click||function(c){c.execCommand(a.command)}});this._={}};CKEDITOR.ui.button.handler={create:function(a){return new CKEDITOR.ui.button(a)}}; -CKEDITOR.ui.button.prototype={render:function(a,f){function m(){var b=a.mode;b&&(b=this.modes[b]?void 0!==n[b]?n[b]:CKEDITOR.TRISTATE_OFF:CKEDITOR.TRISTATE_DISABLED,b=a.readOnly&&!this.readOnly?CKEDITOR.TRISTATE_DISABLED:b,this.setState(b),this.refresh&&this.refresh())}var l=CKEDITOR.env,k=this._.id=CKEDITOR.tools.getNextId(),b="",d=this.command,h;this._.editor=a;var p={id:k,button:this,editor:a,focus:function(){CKEDITOR.document.getById(k).focus()},execute:function(){this.button.click(a)},attach:function(a){this.button.attach(a)}}, -v=CKEDITOR.tools.addFunction(function(a){if(p.onkey)return a=new CKEDITOR.dom.event(a),!1!==p.onkey(p,a.getKeystroke())}),u=CKEDITOR.tools.addFunction(function(a){var b;p.onfocus&&(b=!1!==p.onfocus(p,new CKEDITOR.dom.event(a)));return b}),t=0;p.clickFn=h=CKEDITOR.tools.addFunction(function(){t&&(a.unlockSelection(1),t=0);p.execute();l.iOS&&a.focus()});if(this.modes){var n={};a.on("beforeModeUnload",function(){a.mode&&this._.state!=CKEDITOR.TRISTATE_DISABLED&&(n[a.mode]=this._.state)},this);a.on("activeFilterChange", +f=CKEDITOR.addTemplate("buttonArrow",'\x3cspan class\x3d"cke_button_arrow"\x3e'+(CKEDITOR.env.hc?"\x26#9660;":"")+"\x3c/span\x3e"),c=CKEDITOR.addTemplate("button",a);CKEDITOR.plugins.add("button",{beforeInit:function(a){a.ui.addHandler(CKEDITOR.UI_BUTTON,CKEDITOR.ui.button.handler)}});CKEDITOR.UI_BUTTON="button";CKEDITOR.ui.button=function(a){CKEDITOR.tools.extend(this,a,{title:a.label,click:a.click||function(c){c.execCommand(a.command)}});this._={}};CKEDITOR.ui.button.handler={create:function(a){return new CKEDITOR.ui.button(a)}}; +CKEDITOR.ui.button.prototype={render:function(a,h){function m(){var b=a.mode;b&&(b=this.modes[b]?void 0!==n[b]?n[b]:CKEDITOR.TRISTATE_OFF:CKEDITOR.TRISTATE_DISABLED,b=a.readOnly&&!this.readOnly?CKEDITOR.TRISTATE_DISABLED:b,this.setState(b),this.refresh&&this.refresh())}var l=CKEDITOR.env,k=this._.id=CKEDITOR.tools.getNextId(),b="",d=this.command,g;this._.editor=a;var p={id:k,button:this,editor:a,focus:function(){CKEDITOR.document.getById(k).focus()},execute:function(){this.button.click(a)},attach:function(a){this.button.attach(a)}}, +u=CKEDITOR.tools.addFunction(function(a){if(p.onkey)return a=new CKEDITOR.dom.event(a),!1!==p.onkey(p,a.getKeystroke())}),v=CKEDITOR.tools.addFunction(function(a){var b;p.onfocus&&(b=!1!==p.onfocus(p,new CKEDITOR.dom.event(a)));return b}),r=0;p.clickFn=g=CKEDITOR.tools.addFunction(function(){r&&(a.unlockSelection(1),r=0);p.execute();l.iOS&&a.focus()});if(this.modes){var n={};a.on("beforeModeUnload",function(){a.mode&&this._.state!=CKEDITOR.TRISTATE_DISABLED&&(n[a.mode]=this._.state)},this);a.on("activeFilterChange", m,this);a.on("mode",m,this);!this.readOnly&&a.on("readOnly",m,this)}else d&&(d=a.getCommand(d))&&(d.on("state",function(){this.setState(d.state)},this),b+=d.state==CKEDITOR.TRISTATE_ON?"on":d.state==CKEDITOR.TRISTATE_DISABLED?"disabled":"off");if(this.directional)a.on("contentDirChanged",function(b){var d=CKEDITOR.document.getById(this._.id),c=d.getFirst();b=b.data;b!=a.lang.dir?d.addClass("cke_"+b):d.removeClass("cke_ltr").removeClass("cke_rtl");c.setAttribute("style",CKEDITOR.skin.getIconStyle(w, -"rtl"==b,this.icon,this.iconOffset))},this);d||(b+="off");var q=this.name||this.command,w=q;this.icon&&!/\./.test(this.icon)&&(w=this.icon,this.icon=null);b={id:k,name:q,iconName:w,label:this.label,cls:this.className||"",state:b,ariaDisabled:"disabled"==b?"true":"false",title:this.title,titleJs:l.gecko&&!l.hc?"":(this.title||"").replace("'",""),hasArrow:this.hasArrow?"true":"false",keydownFn:v,focusFn:u,clickFn:h,style:CKEDITOR.skin.getIconStyle(w,"rtl"==a.lang.dir,this.icon,this.iconOffset),arrowHtml:this.hasArrow? -e.output():""};c.output(b,f);if(this.onRender)this.onRender();return p},setState:function(a){if(this._.state==a)return!1;this._.state=a;var c=CKEDITOR.document.getById(this._.id);return c?(c.setState(a,"cke_button"),a==CKEDITOR.TRISTATE_DISABLED?c.setAttribute("aria-disabled",!0):c.removeAttribute("aria-disabled"),this.hasArrow?(a=a==CKEDITOR.TRISTATE_ON?this._.editor.lang.button.selectedLabel.replace(/%1/g,this.label):this.label,CKEDITOR.document.getById(this._.id+"_label").setText(a)):a==CKEDITOR.TRISTATE_ON? -c.setAttribute("aria-pressed",!0):c.removeAttribute("aria-pressed"),!0):!1},getState:function(){return this._.state},toFeature:function(a){if(this._.feature)return this._.feature;var c=this;this.allowedContent||this.requiredContent||!this.command||(c=a.getCommand(this.command)||c);return this._.feature=c}};CKEDITOR.ui.prototype.addButton=function(a,c){this.add(a,CKEDITOR.UI_BUTTON,c)}}(),function(){function a(a){function c(){for(var b=g(),d=CKEDITOR.tools.clone(a.config.toolbarGroups)||e(a),h=0;h< -d.length;h++){var m=d[h];if("/"!=m){"string"==typeof m&&(m=d[h]={name:m});var n,q=m.groups;if(q)for(var w=0;wb.order?-1:0>a.order?1:a.orderb.order?-1:0>a.order?1:a.orderCKEDITOR.env.version? -l.createText("\r"):l.createElement("br"),f.deleteContents(),f.insertNode(a),CKEDITOR.env.needsBrFiller?(l.createText("").insertAfter(a),m&&(q||n.blockLimit).appendBogus(),a.getNext().$.nodeValue="",f.setStartAt(a.getNext(),CKEDITOR.POSITION_AFTER_START)):f.setStartAt(a,CKEDITOR.POSITION_AFTER_END)),f.collapse(!0),f.select(),f.scrollIntoView()):k(a,c,f,g)}}};var m=CKEDITOR.plugins.enterkey,l=m.enterBr,k=m.enterBlock,b=/^h[1-6]$/}(),function(){function a(a,c){var g={},f=[],m={nbsp:" ",shy:"­",gt:"\x3e", -lt:"\x3c",amp:"\x26",apos:"'",quot:'"'};a=a.replace(/\b(nbsp|shy|gt|lt|amp|apos|quot)(?:,|$)/g,function(a,b){var e=c?"\x26"+b+";":m[b];g[e]=c?m[b]:"\x26"+b+";";f.push(e);return""});if(!c&&a){a=a.split(",");var l=document.createElement("div"),k;l.innerHTML="\x26"+a.join(";\x26")+";";k=l.innerHTML;l=null;for(l=0;le&&(e=640);420>c&&(c=420);var f=parseInt((window.screen.height-c)/2,10),m=parseInt((window.screen.width- -e)/2,10);g=(g||"location\x3dno,menubar\x3dno,toolbar\x3dno,dependent\x3dyes,minimizable\x3dno,modal\x3dyes,alwaysRaised\x3dyes,resizable\x3dyes,scrollbars\x3dyes")+",width\x3d"+e+",height\x3d"+c+",top\x3d"+f+",left\x3d"+m;var l=window.open("",null,g,!0);if(!l)return!1;try{-1==navigator.userAgent.toLowerCase().indexOf(" chrome/")&&(l.moveTo(m,f),l.resizeTo(e,c)),l.focus(),l.location.href=a}catch(k){window.open(a,null,g,!0)}return!0}}),function(){function a(a,c){var f=[];if(c)for(var e in c)f.push(e+ -"\x3d"+encodeURIComponent(c[e]));else return a;return a+(-1!=a.indexOf("?")?"\x26":"?")+f.join("\x26")}function e(a){a+="";return a.charAt(0).toUpperCase()+a.substr(1)}function c(){var b=this.getDialog(),c=b.getParentEditor();c._.filebrowserSe=this;var f=c.config["filebrowser"+e(b.getName())+"WindowWidth"]||c.config.filebrowserWindowWidth||"80%",b=c.config["filebrowser"+e(b.getName())+"WindowHeight"]||c.config.filebrowserWindowHeight||"70%",g=this.filebrowser.params||{};g.CKEditor=c.name;g.CKEditorFuncNum= -c._.filebrowserFn;g.langCode||(g.langCode=c.langCode);g=a(this.filebrowser.url,g);c.popup(g,f,b,c.config.filebrowserWindowFeatures||c.config.fileBrowserWindowFeatures)}function g(){var a=this.getDialog();a.getParentEditor()._.filebrowserSe=this;return a.getContentElement(this["for"][0],this["for"][1]).getInputElement().$.value&&a.getContentElement(this["for"][0],this["for"][1]).getAction()?!0:!1}function f(b,c,f){var e=f.params||{};e.CKEditor=b.name;e.CKEditorFuncNum=b._.filebrowserFn;e.langCode|| -(e.langCode=b.langCode);c.action=a(f.url,e);c.filebrowser=f}function m(a,d,h,k){if(k&&k.length)for(var l,u=k.length;u--;)if(l=k[u],"hbox"!=l.type&&"vbox"!=l.type&&"fieldset"!=l.type||m(a,d,h,l.children),l.filebrowser)if("string"==typeof l.filebrowser&&(l.filebrowser={action:"fileButton"==l.type?"QuickUpload":"Browse",target:l.filebrowser}),"Browse"==l.filebrowser.action){var t=l.filebrowser.url;void 0===t&&(t=a.config["filebrowser"+e(d)+"BrowseUrl"],void 0===t&&(t=a.config.filebrowserBrowseUrl)); -t&&(l.onClick=c,l.filebrowser.url=t,l.hidden=!1)}else if("QuickUpload"==l.filebrowser.action&&l["for"]&&(t=l.filebrowser.url,void 0===t&&(t=a.config["filebrowser"+e(d)+"UploadUrl"],void 0===t&&(t=a.config.filebrowserUploadUrl)),t)){var n=l.onClick;l.onClick=function(a){var b=a.sender;return n&&!1===n.call(b,a)?!1:g.call(b,a)};l.filebrowser.url=t;l.hidden=!1;f(a,h.getContents(l["for"][0]).get(l["for"][1]),l.filebrowser)}}function l(a,c,f){if(-1!==f.indexOf(";")){f=f.split(";");for(var e=0;ew.height-q.bottom?h("pin"):h("bottom"),d=w.width/2,d=f.floatSpacePreferRight?"right":0n.width?"rtl"==f.contentsLangDirection?"right":"left":d-q.left> -q.right-d?"left":"right",n.width>w.width?(d="left",p=0):(p="left"==d?0w.width&&(d="left"==d?"right":"left",p=0)),b.setStyle(d,c(("pin"==k?B:r)+p+("pin"==k?0:"left"==d?C:-C)))):(k="pin",h("pin"),l(d))}}}();if(m){var k=new CKEDITOR.template('\x3cdiv id\x3d"cke_{name}" class\x3d"cke {id} cke_reset_all cke_chrome cke_editor_{name} cke_float cke_{langDir} '+CKEDITOR.env.cssClass+'" dir\x3d"{langDir}" title\x3d"'+(CKEDITOR.env.gecko?" ":"")+'" lang\x3d"{langCode}" role\x3d"application" style\x3d"{style}"'+ -(a.title?' aria-labelledby\x3d"cke_{name}_arialbl"':" ")+"\x3e"+(a.title?'\x3cspan id\x3d"cke_{name}_arialbl" class\x3d"cke_voice_label"\x3e{voiceLabel}\x3c/span\x3e':" ")+'\x3cdiv class\x3d"cke_inner"\x3e\x3cdiv id\x3d"{topId}" class\x3d"cke_top" role\x3d"presentation"\x3e{content}\x3c/div\x3e\x3c/div\x3e\x3c/div\x3e'),b=CKEDITOR.document.getBody().append(CKEDITOR.dom.element.createFromHtml(k.output({content:m,id:a.id,langDir:a.lang.dir,langCode:a.langCode,name:a.name,style:"display:none;z-index:"+ -(f.baseFloatZIndex-1),topId:a.ui.spaceId("top"),voiceLabel:a.title}))),d=CKEDITOR.tools.eventsBuffer(500,l),h=CKEDITOR.tools.eventsBuffer(100,l);b.unselectable();b.on("mousedown",function(a){a=a.data;a.getTarget().hasAscendant("a",1)||a.preventDefault()});a.on("focus",function(b){l(b);a.on("change",d.input);e.on("scroll",h.input);e.on("resize",h.input)});a.on("blur",function(){b.hide();a.removeListener("change",d.input);e.removeListener("scroll",h.input);e.removeListener("resize",h.input)});a.on("destroy", -function(){e.removeListener("scroll",h.input);e.removeListener("resize",h.input);b.clearCustomData();b.remove()});a.focusManager.hasFocus&&b.show();a.focusManager.add(b,1)}}var e=CKEDITOR.document.getWindow(),c=CKEDITOR.tools.cssLength;CKEDITOR.plugins.add("floatingspace",{init:function(c){c.on("loaded",function(){a(this)},null,null,20)}})}(),CKEDITOR.plugins.add("listblock",{requires:"panel",onLoad:function(){var a=CKEDITOR.addTemplate("panel-list",'\x3cul role\x3d"presentation" class\x3d"cke_panel_list"\x3e{items}\x3c/ul\x3e'), -e=CKEDITOR.addTemplate("panel-list-item",'\x3cli id\x3d"{id}" class\x3d"cke_panel_listItem" role\x3dpresentation\x3e\x3ca id\x3d"{id}_option" _cke_focus\x3d1 hidefocus\x3dtrue title\x3d"{title}" href\x3d"javascript:void(\'{val}\')" {onclick}\x3d"CKEDITOR.tools.callFunction({clickFn},\'{val}\'); return false;" role\x3d"option"\x3e{text}\x3c/a\x3e\x3c/li\x3e'),c=CKEDITOR.addTemplate("panel-list-group",'\x3ch1 id\x3d"{id}" class\x3d"cke_panel_grouptitle" role\x3d"presentation" \x3e{label}\x3c/h1\x3e'), -g=/\'/g;CKEDITOR.ui.panel.prototype.addListBlock=function(a,c){return this.addBlock(a,new CKEDITOR.ui.listBlock(this.getHolderElement(),c))};CKEDITOR.ui.listBlock=CKEDITOR.tools.createClass({base:CKEDITOR.ui.panel.block,$:function(a,c){c=c||{};var e=c.attributes||(c.attributes={});(this.multiSelect=!!c.multiSelect)&&(e["aria-multiselectable"]=!0);!e.role&&(e.role="listbox");this.base.apply(this,arguments);this.element.setAttribute("role",e.role);e=this.keys;e[40]="next";e[9]="next";e[38]="prev";e[CKEDITOR.SHIFT+ -9]="prev";e[32]=CKEDITOR.env.ie?"mouseup":"click";CKEDITOR.env.ie&&(e[13]="mouseup");this._.pendingHtml=[];this._.pendingList=[];this._.items={};this._.groups={}},_:{close:function(){if(this._.started){var c=a.output({items:this._.pendingList.join("")});this._.pendingList=[];this._.pendingHtml.push(c);delete this._.started}},getClick:function(){this._.click||(this._.click=CKEDITOR.tools.addFunction(function(a){var c=this.toggle(a);if(this.onClick)this.onClick(a,c)},this));return this._.click}},proto:{add:function(a, -c,l){var k=CKEDITOR.tools.getNextId();this._.started||(this._.started=1,this._.size=this._.size||0);this._.items[a]=k;var b;b=CKEDITOR.tools.htmlEncodeAttr(a).replace(g,"\\'");a={id:k,val:b,onclick:CKEDITOR.env.ie?'onclick\x3d"return false;" onmouseup':"onclick",clickFn:this._.getClick(),title:CKEDITOR.tools.htmlEncodeAttr(l||a),text:c||a};this._.pendingList.push(e.output(a))},startGroup:function(a){this._.close();var e=CKEDITOR.tools.getNextId();this._.groups[a]=e;this._.pendingHtml.push(c.output({id:e, -label:a}))},commit:function(){this._.close();this.element.appendHtml(this._.pendingHtml.join(""));delete this._.size;this._.pendingHtml=[]},toggle:function(a){var c=this.isMarked(a);c?this.unmark(a):this.mark(a);return!c},hideGroup:function(a){var c=(a=this.element.getDocument().getById(this._.groups[a]))&&a.getNext();a&&(a.setStyle("display","none"),c&&"ul"==c.getName()&&c.setStyle("display","none"))},hideItem:function(a){this.element.getDocument().getById(this._.items[a]).setStyle("display","none")}, -showAll:function(){var a=this._.items,c=this._.groups,e=this.element.getDocument(),g;for(g in a)e.getById(a[g]).setStyle("display","");for(var b in c)a=e.getById(c[b]),g=a.getNext(),a.setStyle("display",""),g&&"ul"==g.getName()&&g.setStyle("display","")},mark:function(a){this.multiSelect||this.unmarkAll();a=this._.items[a];var c=this.element.getDocument().getById(a);c.addClass("cke_selected");this.element.getDocument().getById(a+"_option").setAttribute("aria-selected",!0);this.onMark&&this.onMark(c)}, -unmark:function(a){var c=this.element.getDocument();a=this._.items[a];var e=c.getById(a);e.removeClass("cke_selected");c.getById(a+"_option").removeAttribute("aria-selected");this.onUnmark&&this.onUnmark(e)},unmarkAll:function(){var a=this._.items,c=this.element.getDocument(),e;for(e in a){var g=a[e];c.getById(g).removeClass("cke_selected");c.getById(g+"_option").removeAttribute("aria-selected")}this.onUnmark&&this.onUnmark()},isMarked:function(a){return this.element.getDocument().getById(this._.items[a]).hasClass("cke_selected")}, -focus:function(a){this._.focusIndex=-1;var c=this.element.getElementsByTag("a"),e,g=-1;if(a)for(e=this.element.getDocument().getById(this._.items[a]).getFirst();a=c.getItem(++g);){if(a.equals(e)){this._.focusIndex=g;break}}else this.element.focus();e&&setTimeout(function(){e.focus()},0)}}})}}),CKEDITOR.plugins.add("richcombo",{requires:"floatpanel,listblock,button",beforeInit:function(a){a.ui.addHandler(CKEDITOR.UI_RICHCOMBO,CKEDITOR.ui.richCombo.handler)}}),function(){var a='\x3cspan id\x3d"{id}" class\x3d"cke_combo cke_combo__{name} {cls}" role\x3d"presentation"\x3e\x3cspan id\x3d"{id}_label" class\x3d"cke_combo_label"\x3e{label}\x3c/span\x3e\x3ca class\x3d"cke_combo_button" title\x3d"{title}" tabindex\x3d"-1"'+ -(CKEDITOR.env.gecko&&!CKEDITOR.env.hc?"":" href\x3d\"javascript:void('{titleJs}')\"")+' hidefocus\x3d"true" role\x3d"button" aria-labelledby\x3d"{id}_label" aria-haspopup\x3d"true"';CKEDITOR.env.gecko&&CKEDITOR.env.mac&&(a+=' onkeypress\x3d"return false;"');CKEDITOR.env.gecko&&(a+=' onblur\x3d"this.style.cssText \x3d this.style.cssText;"');var a=a+(' onkeydown\x3d"return CKEDITOR.tools.callFunction({keydownFn},event,this);" onfocus\x3d"return CKEDITOR.tools.callFunction({focusFn},event);" '+(CKEDITOR.env.ie? -'onclick\x3d"return false;" onmouseup':"onclick")+'\x3d"CKEDITOR.tools.callFunction({clickFn},this);return false;"\x3e\x3cspan id\x3d"{id}_text" class\x3d"cke_combo_text cke_combo_inlinelabel"\x3e{label}\x3c/span\x3e\x3cspan class\x3d"cke_combo_open"\x3e\x3cspan class\x3d"cke_combo_arrow"\x3e'+(CKEDITOR.env.hc?"\x26#9660;":CKEDITOR.env.air?"\x26nbsp;":"")+"\x3c/span\x3e\x3c/span\x3e\x3c/a\x3e\x3c/span\x3e"),e=CKEDITOR.addTemplate("combo",a);CKEDITOR.UI_RICHCOMBO="richcombo";CKEDITOR.ui.richCombo= -CKEDITOR.tools.createClass({$:function(a){CKEDITOR.tools.extend(this,a,{canGroup:!1,title:a.label,modes:{wysiwyg:1},editorFocus:1});a=this.panel||{};delete this.panel;this.id=CKEDITOR.tools.getNextNumber();this.document=a.parent&&a.parent.getDocument()||CKEDITOR.document;a.className="cke_combopanel";a.block={multiSelect:a.multiSelect,attributes:a.attributes};a.toolbarRelated=!0;this._={panelDefinition:a,items:{}}},proto:{renderHtml:function(a){var e=[];this.render(a,e);return e.join("")},render:function(a, -g){function f(){if(this.getState()!=CKEDITOR.TRISTATE_ON){var b=this.modes[a.mode]?CKEDITOR.TRISTATE_OFF:CKEDITOR.TRISTATE_DISABLED;a.readOnly&&!this.readOnly&&(b=CKEDITOR.TRISTATE_DISABLED);this.setState(b);this.setValue("");b!=CKEDITOR.TRISTATE_DISABLED&&this.refresh&&this.refresh()}}var m=CKEDITOR.env,l="cke_"+this.id,k=CKEDITOR.tools.addFunction(function(b){v&&(a.unlockSelection(1),v=0);d.execute(b)},this),b=this,d={id:l,combo:this,focus:function(){CKEDITOR.document.getById(l).getChild(1).focus()}, -execute:function(d){var e=b._;if(e.state!=CKEDITOR.TRISTATE_DISABLED)if(b.createPanel(a),e.on)e.panel.hide();else{b.commit();var f=b.getValue();f?e.list.mark(f):e.list.unmarkAll();e.panel.showBlock(b.id,new CKEDITOR.dom.element(d),4)}},clickFn:k};a.on("activeFilterChange",f,this);a.on("mode",f,this);a.on("selectionChange",f,this);!this.readOnly&&a.on("readOnly",f,this);var h=CKEDITOR.tools.addFunction(function(b,e){b=new CKEDITOR.dom.event(b);var f=b.getKeystroke();if(40==f)a.once("panelShow",function(a){a.data._.panel._.currentBlock.onKeyDown(40)}); -switch(f){case 13:case 32:case 40:CKEDITOR.tools.callFunction(k,e);break;default:d.onkey(d,f)}b.preventDefault()}),p=CKEDITOR.tools.addFunction(function(){d.onfocus&&d.onfocus()}),v=0;d.keyDownFn=h;m={id:l,name:this.name||this.command,label:this.label,title:this.title,cls:this.className||"",titleJs:m.gecko&&!m.hc?"":(this.title||"").replace("'",""),keydownFn:h,focusFn:p,clickFn:k};e.output(m,g);if(this.onRender)this.onRender();return d},createPanel:function(a){if(!this._.panel){var e=this._.panelDefinition, -f=this._.panelDefinition.block,m=e.parent||CKEDITOR.document.getBody(),l="cke_combopanel__"+this.name,k=new CKEDITOR.ui.floatPanel(a,m,e),b=k.addListBlock(this.id,f),d=this;k.onShow=function(){this.element.addClass(l);d.setState(CKEDITOR.TRISTATE_ON);d._.on=1;d.editorFocus&&!a.focusManager.hasFocus&&a.focus();if(d.onOpen)d.onOpen();a.once("panelShow",function(){b.focus(!b.multiSelect&&d.getValue())})};k.onHide=function(b){this.element.removeClass(l);d.setState(d.modes&&d.modes[a.mode]?CKEDITOR.TRISTATE_OFF: -CKEDITOR.TRISTATE_DISABLED);d._.on=0;if(!b&&d.onClose)d.onClose()};k.onEscape=function(){k.hide(1)};b.onClick=function(a,b){d.onClick&&d.onClick.call(d,a,b);k.hide()};this._.panel=k;this._.list=b;k.getBlock(this.id).onHide=function(){d._.on=0;d.setState(CKEDITOR.TRISTATE_OFF)};this.init&&this.init()}},setValue:function(a,e){this._.value=a;var f=this.document.getById("cke_"+this.id+"_text");f&&(a||e?f.removeClass("cke_combo_inlinelabel"):(e=this.label,f.addClass("cke_combo_inlinelabel")),f.setText("undefined"!= -typeof e?e:a))},getValue:function(){return this._.value||""},unmarkAll:function(){this._.list.unmarkAll()},mark:function(a){this._.list.mark(a)},hideItem:function(a){this._.list.hideItem(a)},hideGroup:function(a){this._.list.hideGroup(a)},showAll:function(){this._.list.showAll()},add:function(a,e,f){this._.items[a]=f||a;this._.list.add(a,e,f)},startGroup:function(a){this._.list.startGroup(a)},commit:function(){this._.committed||(this._.list.commit(),this._.committed=1,CKEDITOR.ui.fire("ready",this)); -this._.committed=1},setState:function(a){if(this._.state!=a){var e=this.document.getById("cke_"+this.id);e.setState(a,"cke_combo");a==CKEDITOR.TRISTATE_DISABLED?e.setAttribute("aria-disabled",!0):e.removeAttribute("aria-disabled");this._.state=a}},getState:function(){return this._.state},enable:function(){this._.state==CKEDITOR.TRISTATE_DISABLED&&this.setState(this._.lastState)},disable:function(){this._.state!=CKEDITOR.TRISTATE_DISABLED&&(this._.lastState=this._.state,this.setState(CKEDITOR.TRISTATE_DISABLED))}}, -statics:{handler:{create:function(a){return new CKEDITOR.ui.richCombo(a)}}}});CKEDITOR.ui.prototype.addRichCombo=function(a,e){this.add(a,CKEDITOR.UI_RICHCOMBO,e)}}(),CKEDITOR.plugins.add("format",{requires:"richcombo",init:function(a){if(!a.blockless){for(var e=a.config,c=a.lang.format,g=e.format_tags.split(";"),f={},m=0,l=[],k=0;kthis.$.offsetHeight){var c=g.createRange();c[33==b?"moveToElementEditStart":"moveToElementEditEnd"](this);c.select();a.data.preventDefault()}});CKEDITOR.env.ie&&this.attachListener(k,"blur",function(){try{k.$.selection.empty()}catch(a){}});CKEDITOR.env.iOS&&this.attachListener(k,"touchend",function(){a.focus()});b=g.document.getElementsByTag("title").getItem(0);b.data("cke-title",b.getText());CKEDITOR.env.ie&&(g.document.$.title=this._.docTitle);CKEDITOR.tools.setTimeout(function(){"unloaded"== -this.status&&(this.status="ready");g.fire("contentDom");this._.isPendingFocus&&(g.focus(),this._.isPendingFocus=!1);setTimeout(function(){g.fire("dataReady")},0)},0,this)}function e(a){function c(){var b;a.editable().attachListener(a,"selectionChange",function(){var c=a.getSelection().getSelectedElement();c&&(b&&(b.detachEvent("onresizestart",e),b=null),c.$.attachEvent("onresizestart",e),b=c.$)})}function e(a){a.returnValue=!1}if(CKEDITOR.env.gecko)try{var g=a.document.$;g.execCommand("enableObjectResizing", -!1,!a.config.disableObjectResizing);g.execCommand("enableInlineTableEditing",!1,!a.config.disableNativeTableHandles)}catch(b){}else CKEDITOR.env.ie&&11>CKEDITOR.env.version&&a.config.disableObjectResizing&&c(a)}function c(){var a=[];if(8<=CKEDITOR.document.$.documentMode){a.push("html.CSS1Compat [contenteditable\x3dfalse]{min-height:0 !important}");var c=[],e;for(e in CKEDITOR.dtd.$removeEmpty)c.push("html.CSS1Compat "+e+"[contenteditable\x3dfalse]");a.push(c.join(",")+"{display:inline-block}")}else CKEDITOR.env.gecko&& -(a.push("html{height:100% !important}"),a.push("img:-moz-broken{-moz-force-broken-image-icon:1;min-width:24px;min-height:24px}"));a.push("html{cursor:text;*cursor:auto}");a.push("img,input,textarea{cursor:default}");return a.join("\n")}CKEDITOR.plugins.add("wysiwygarea",{init:function(a){a.config.fullPage&&a.addFeature({allowedContent:"html head title; style [media,type]; body (*)[id]; meta link [*]",requiredContent:"body"});a.addMode("wysiwyg",function(c){function e(d){d&&d.removeListener();a.editable(new g(a, -b.$.contentWindow.document.body));a.setData(a.getData(1),c)}var k="document.open();"+(CKEDITOR.env.ie?"("+CKEDITOR.tools.fixDomain+")();":"")+"document.close();",k=CKEDITOR.env.air?"javascript:void(0)":CKEDITOR.env.ie&&!CKEDITOR.env.edge?"javascript:void(function(){"+encodeURIComponent(k)+"}())":"",b=CKEDITOR.dom.element.createFromHtml('\x3ciframe src\x3d"'+k+'" frameBorder\x3d"0"\x3e\x3c/iframe\x3e');b.setStyles({width:"100%",height:"100%"});b.addClass("cke_wysiwyg_frame").addClass("cke_reset"); -k=a.ui.space("contents");k.append(b);var d=CKEDITOR.env.ie&&!CKEDITOR.env.edge||CKEDITOR.env.gecko;if(d)b.on("load",e);var h=a.title,p=a.fire("ariaEditorHelpLabel",{}).label;h&&(CKEDITOR.env.ie&&p&&(h+=", "+p),b.setAttribute("title",h));if(p){var h=CKEDITOR.tools.getNextId(),v=CKEDITOR.dom.element.createFromHtml('\x3cspan id\x3d"'+h+'" class\x3d"cke_voice_label"\x3e'+p+"\x3c/span\x3e");k.append(v,1);b.setAttribute("aria-describedby",h)}a.on("beforeModeUnload",function(a){a.removeListener();v&&v.remove()}); -b.setAttributes({tabIndex:a.tabIndex,allowTransparency:"true"});!d&&e();a.fire("ariaWidget",b)})}});CKEDITOR.editor.prototype.addContentsCss=function(a){var c=this.config,e=c.contentsCss;CKEDITOR.tools.isArray(e)||(c.contentsCss=e?[e]:[]);c.contentsCss.push(a)};var g=CKEDITOR.tools.createClass({$:function(){this.base.apply(this,arguments);this._.frameLoadedHandler=CKEDITOR.tools.addFunction(function(c){CKEDITOR.tools.setTimeout(a,0,this,c)},this);this._.docTitle=this.getWindow().getFrame().getAttribute("title")}, -base:CKEDITOR.editable,proto:{setData:function(a,e){var g=this.editor;if(e)this.setHtml(a),this.fixInitialSelection(),g.fire("dataReady");else{this._.isLoadingData=!0;g._.dataStore={id:1};var k=g.config,b=k.fullPage,d=k.docType,h=CKEDITOR.tools.buildStyleHtml(c()).replace(/ -
aaa\nbbb\n\nccc
-aaa\nbbb\n\nccc - -" => array( - "" => TRUE, - "" => TRUE, - "
aaa\nbbb\n\nccc
" => TRUE, - "aaa\nbbb\n\nccc" => TRUE, - "" => TRUE, - ), - // Skip comments entirely. - "One. Two.\n\n" => array( - '' => TRUE, - "" => TRUE, - ), - // Resulting HTML should produce matching paragraph tags. - '

' => array( - "

\n

\n

" => TRUE, - ), - '

' => array( - "
\n
" => TRUE, - ), - '
aaa
' => array( - "
aaa
" => TRUE, - ), - "
aaa\nbbb\nccc
\nddd\neee" => array( - "
aaa\nbbb\nccc
" => TRUE, - "

ddd
\neee

" => TRUE, - ), - // Comments remain unchanged and subsequent lines/paragraphs are - // transformed normally. - "aaa\n\nbbb\n\nccc\n\nddd\n\neee\n\nfff" => array( - "

aaa

\n

\nbbb

\n

ccc

\n

ddd

" => TRUE, - "

\neee

\n

fff

" => TRUE, - ), - // Check that a comment in a PRE will result that the text after - // the comment, but still in PRE, is not transformed. - "
aaa\nbbb\n\nccc
\nddd" => array( - "
aaa\nbbb\n\nccc
" => TRUE, - ), - // Bug 810824, paragraphs were appearing around iframe tags. - "\n\n" => array( - "

" => FALSE, - ), - ); - $this->assertFilteredString($filter, $tests); - - // Very long string hitting PCRE limits. - $limit = max(ini_get('pcre.backtrack_limit'), ini_get('pcre.recursion_limit')); - $source = $this->randomMachineName($limit); - $result = _filter_autop($source); - $success = $this->assertEqual($result, '

' . $source . "

\n", 'Line break filter can process very long strings.'); - if (!$success) { - $this->verbose("\n" . $source . "\n
\n" . $result); - } - } - - - /** - * Tests filter settings, defaults, access restrictions and similar. - * - * @todo This is for functions like filter_filter and check_markup, whose - * functionality is not completely focused on filtering. Some ideas: - * restricting formats according to user permissions, proper cache - * handling, defaults -- allowed tags/attributes/protocols. - * - * @todo It is possible to add script, iframe etc. to allowed tags, but this - * makes HTML filter completely ineffective. - * - * @todo Class, id, name and xmlns should be added to disallowed attributes, - * or better a whitelist approach should be used for that too. - */ - function testHtmlFilter() { - // Get FilterHtml object. - $filter = $this->filters['filter_html']; - $filter->setConfiguration(array( - 'settings' => array( - 'allowed_html' => '


    1. ', - 'filter_html_help' => 1, - 'filter_html_nofollow' => 0, - ) - )); - - // HTML filter is not able to secure some tags, these should never be - // allowed. - $f = (string) $filter->process(' -' => array( - 'href="http://www.example.com"' => FALSE, - 'href="http://example.net"' => FALSE, - ), - ' - -' => array( - 'href' => FALSE, - ), - ' - -' => array( - 'href' => FALSE, - ), - ' - -' => array( - 'href' => FALSE, - ), - ' - -' => array( - 'href' => FALSE, - ), - ' -
      -
      www.example.com
      -
      http://example.com
      -
      person@example.com
      -
      Check www.example.net
      -
      Some text around http://www.example.info by person@example.info?
      -
      -' => array( - 'href="http://www.example.com"' => TRUE, - 'href="http://example.com"' => TRUE, - 'href="mailto:person@example.com"' => TRUE, - 'href="http://www.example.net"' => TRUE, - 'href="http://www.example.info"' => TRUE, - 'href="mailto:person@example.info"' => TRUE, - ), - ' -
      www.div.com
      -
        -
      • http://listitem.com
      • -
      • www.class.listitem.com
      • -
      -' => array( - '
      ' => TRUE, - '
    2. http://listitem.com
    3. ' => TRUE, - '
    4. www.class.listitem.com
    5. ' => TRUE, - ), - ); - $this->assertFilteredString($filter, $tests); - - // URL trimming. - $filter->setConfiguration(array( - 'settings' => array( - 'filter_url_length' => 20, - ) - )); - $tests = array( - 'www.trimmed.com/d/ff.ext?a=1&b=2#a1' => array( - 'www.trimmed.com/d/f…' => TRUE, - ), - ); - $this->assertFilteredString($filter, $tests); - } - - /** - * Asserts multiple filter output expectations for multiple input strings. - * - * @param FilterInterface $filter - * A input filter object. - * @param array $tests - * An associative array, whereas each key is an arbitrary input string and - * each value is again an associative array whose keys are filter output - * strings and whose values are Booleans indicating whether the output is - * expected or not. - * - * For example: - * @code - * $tests = array( - * 'Input string' => array( - * '

      Input string

      ' => TRUE, - * 'Input string FALSE, - * ), - * ); - * @endcode - */ - function assertFilteredString($filter, $tests) { - foreach ($tests as $source => $tasks) { - $result = $filter->process($source, $filter)->getProcessedText(); - foreach ($tasks as $value => $is_expected) { - // Not using assertIdentical, since combination with strpos() is hard to grok. - if ($is_expected) { - $success = $this->assertTrue(strpos($result, $value) !== FALSE, format_string('@source: @value found. Filtered result: @result.', array( - '@source' => var_export($source, TRUE), - '@value' => var_export($value, TRUE), - '@result' => var_export($result, TRUE), - ))); - } - else { - $success = $this->assertTrue(strpos($result, $value) === FALSE, format_string('@source: @value not found. Filtered result: @result.', array( - '@source' => var_export($source, TRUE), - '@value' => var_export($value, TRUE), - '@result' => var_export($result, TRUE), - ))); - } - if (!$success) { - $this->verbose('Source:
      ' . Html::escape(var_export($source, TRUE)) . '
      ' - . '
      ' . 'Result:
      ' . Html::escape(var_export($result, TRUE)) . '
      ' - . '
      ' . ($is_expected ? 'Expected:' : 'Not expected:') - . '
      ' . Html::escape(var_export($value, TRUE)) . '
      ' - ); - } - } - } - } - - /** - * Tests URL filter on longer content. - * - * Filters based on regular expressions should also be tested with a more - * complex content than just isolated test lines. - * The most common errors are: - * - accidental '*' (greedy) match instead of '*?' (minimal) match. - * - only matching first occurrence instead of all. - * - newlines not matching '.*'. - * - * This test covers: - * - Document with multiple newlines and paragraphs (two newlines). - * - Mix of several HTML tags, invalid non-HTML tags, tags to ignore and HTML - * comments. - * - Empty HTML tags (BR, IMG). - * - Mix of absolute and partial URLs, and email addresses in one content. - */ - function testUrlFilterContent() { - // Get FilterUrl object. - $filter = $this->filters['filter_url']; - $filter->setConfiguration(array( - 'settings' => array( - 'filter_url_length' => 496, - ) - )); - $path = drupal_get_path('module', 'filter') . '/tests'; - - $input = file_get_contents($path . '/filter.url-input.txt'); - $expected = file_get_contents($path . '/filter.url-output.txt'); - $result = _filter_url($input, $filter); - $this->assertIdentical($result, $expected, 'Complex HTML document was correctly processed.'); - } - - /** - * Tests the HTML corrector filter. - * - * @todo This test could really use some validity checking function. - */ - function testHtmlCorrectorFilter() { - // Tag closing. - $f = Html::normalize('

      text'); - $this->assertEqual($f, '

      text

      ', 'HTML corrector -- tag closing at the end of input.'); - - $f = Html::normalize('

      text

      text'); - $this->assertEqual($f, '

      text

      text

      ', 'HTML corrector -- tag closing.'); - - $f = Html::normalize("
      • e1
      • e2"); - $this->assertEqual($f, "
        • e1
        • e2
        ", 'HTML corrector -- unclosed list tags.'); - - $f = Html::normalize('
        content'); - $this->assertEqual($f, '
        content
        ', 'HTML corrector -- unclosed tag with attribute.'); - - // XHTML slash for empty elements. - $f = Html::normalize('

        '); - $this->assertEqual($f, '

        ', 'HTML corrector -- XHTML closing slash.'); - - $f = Html::normalize('

        test

        '); - $this->assertEqual($f, '

        test

        ', 'HTML corrector -- Convert uppercased tags to proper lowercased ones.'); - - $f = Html::normalize('

        test

        '); - $this->assertEqual($f, '

        test

        ', 'HTML corrector -- Convert uppercased tags to proper lowercased ones.'); - - $f = Html::normalize('test
        '); - $this->assertEqual($f, 'test
        ', 'HTML corrector -- Let proper XHTML pass through.'); - - $f = Html::normalize('test
        '); - $this->assertEqual($f, 'test
        ', 'HTML corrector -- Let proper XHTML pass through, but ensure there is a single space before the closing slash.'); - - $f = Html::normalize('test
        '); - $this->assertEqual($f, 'test
        ', 'HTML corrector -- Let proper XHTML pass through, but ensure there are not too many spaces before the closing slash.'); - - $f = Html::normalize(''); - $this->assertEqual($f, '', 'HTML corrector -- Convert XHTML that is properly formed but that would not be compatible with typical HTML user agents.'); - - $f = Html::normalize('test1
        test2'); - $this->assertEqual($f, 'test1
        test2', 'HTML corrector -- Automatically close single tags.'); - - $f = Html::normalize('line1
        line2'); - $this->assertEqual($f, 'line1
        line2', 'HTML corrector -- Automatically close single tags.'); - - $f = Html::normalize('line1
        line2'); - $this->assertEqual($f, 'line1
        line2', 'HTML corrector -- Automatically close single tags.'); - - $f = Html::normalize('test'); - $this->assertEqual($f, 'test', 'HTML corrector -- Automatically close single tags.'); - - $f = Html::normalize('

        '); - $this->assertEqual($f, '
        ', "HTML corrector -- Transform empty tags to a single closed tag if the tag's content model is EMPTY."); - - $f = Html::normalize('
        '); - $this->assertEqual($f, '
        ', "HTML corrector -- Do not transform empty tags to a single closed tag if the tag's content model is not EMPTY."); - - $f = Html::normalize('

        line1


        line2

        '); - $this->assertEqual($f, '

        line1


        line2', 'HTML corrector -- Move non-inline elements outside of inline containers.'); - - $f = Html::normalize('

        line1

        line2

        '); - $this->assertEqual($f, '

        line1

        line2
        ', 'HTML corrector -- Move non-inline elements outside of inline containers.'); - - $f = Html::normalize('

        test

        test

        \n'); - $this->assertEqual($f, '

        test

        test

        \n', 'HTML corrector -- Auto-close improperly nested tags.'); - - $f = Html::normalize('

        Line1
        bold stuff'); - $this->assertEqual($f, '

        Line1
        bold stuff

        ', 'HTML corrector -- Properly close unclosed tags, and remove useless closing tags.'); - - $f = Html::normalize('test '); - $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.'); - - $f = Html::normalize('test '); - $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.'); - - $f = Html::normalize('test '); - $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.'); - - $f = Html::normalize('test '); - $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.'); - - $f = Html::normalize('test '); - $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.'); - - $f = Html::normalize('

        test\n

        \n'); - $this->assertEqual($f, '

        test\n

        \n', 'HTML corrector -- New-lines are accepted and kept as-is.'); - - $f = Html::normalize('

        دروبال'); - $this->assertEqual($f, '

        دروبال

        ', 'HTML corrector -- Encoding is correctly kept.'); - - $f = Html::normalize(''); - $this->assertEqual($f, '', 'HTML corrector -- CDATA added to script element'); - - $f = Html::normalize('

        '); - $this->assertEqual($f, '

        ', 'HTML corrector -- CDATA added to a nested script element'); - - $f = Html::normalize('

        '); - $this->assertEqual($f, '

        ', 'HTML corrector -- CDATA added to a style element.'); - - $filtered_data = Html::normalize('

        '); - $this->assertEqual($filtered_data, '

        ', - format_string('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => '/*

        '); - $this->assertEqual($filtered_data, '

        ', - format_string('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => ' -

        '); - $this->assertEqual($filtered_data, '

        ', - format_string('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => ' -

        ', - format_string('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => '// assertTrue(strpos(strtolower(Html::decodeEntities($haystack)), $needle) !== FALSE, $message, $group); - } - - /** - * Asserts that text transformed to lowercase with HTML entities decoded does not contain a given string. - * - * Otherwise fails the test with a given message, similar to all the - * SimpleTest assert* functions. - * - * Note that this does not remove nulls, new lines, and other character that - * could be used to obscure a tag or an attribute name. - * - * @param string $haystack - * Text to look in. - * @param string $needle - * Lowercase, plain text to look for. - * @param string $message - * (optional) Message to display if failed. Defaults to an empty string. - * @param string $group - * (optional) The group this message belongs to. Defaults to 'Other'. - * - * @return bool - * TRUE on pass, FALSE on fail. - */ - function assertNoNormalized($haystack, $needle, $message = '', $group = 'Other') { - return $this->assertTrue(strpos(strtolower(Html::decodeEntities($haystack)), $needle) === FALSE, $message, $group); - } -} diff --git a/core/modules/filter/src/Tests/Migrate/d6/MigrateFilterFormatTest.php b/core/modules/filter/src/Tests/Migrate/d6/MigrateFilterFormatTest.php deleted file mode 100644 index 2a3de4e..0000000 --- a/core/modules/filter/src/Tests/Migrate/d6/MigrateFilterFormatTest.php +++ /dev/null @@ -1,57 +0,0 @@ -executeMigration('d6_filter_format'); - } - - /** - * Tests the Drupal 6 filter format to Drupal 8 migration. - */ - public function testFilterFormat() { - $filter_format = FilterFormat::load('filtered_html'); - - // Check filter status. - $filters = $filter_format->get('filters'); - $this->assertTrue($filters['filter_autop']['status']); - $this->assertTrue($filters['filter_url']['status']); - $this->assertTrue($filters['filter_htmlcorrector']['status']); - $this->assertTrue($filters['filter_html']['status']); - - // These should be false by default. - $this->assertFalse(isset($filters['filter_html_escape'])); - $this->assertFalse(isset($filters['filter_caption'])); - $this->assertFalse(isset($filters['filter_html_image_secure'])); - - // Check variables migrated into filter. - $this->assertIdentical('
          1. ', $filters['filter_html']['settings']['allowed_html']); - $this->assertIdentical(TRUE, $filters['filter_html']['settings']['filter_html_help']); - $this->assertIdentical(FALSE, $filters['filter_html']['settings']['filter_html_nofollow']); - $this->assertIdentical(72, $filters['filter_url']['settings']['filter_url_length']); - - // Check that the PHP code filter is converted to filter_null. - $filters = FilterFormat::load('php_code')->get('filters'); - $this->assertTrue(isset($filters['filter_null'])); - } - -} diff --git a/core/modules/filter/src/Tests/Migrate/d7/MigrateFilterFormatTest.php b/core/modules/filter/src/Tests/Migrate/d7/MigrateFilterFormatTest.php deleted file mode 100644 index b96b99a..0000000 --- a/core/modules/filter/src/Tests/Migrate/d7/MigrateFilterFormatTest.php +++ /dev/null @@ -1,81 +0,0 @@ -installConfig(static::$modules); - $this->executeMigration('d7_filter_format'); - } - - /** - * Asserts various aspects of a filter format entity. - * - * @param string $id - * The format ID. - * @param string $label - * The expected label of the format. - * @param array $enabled_filters - * The expected filters in the format, keyed by ID. - */ - protected function assertEntity($id, $label, array $enabled_filters) { - /** @var \Drupal\filter\FilterFormatInterface $entity */ - $entity = FilterFormat::load($id); - $this->assertTrue($entity instanceof FilterFormatInterface); - $this->assertIdentical($label, $entity->label()); - // get('filters') will return enabled filters only, not all of them. - $this->assertIdentical($enabled_filters, array_keys($entity->get('filters'))); - } - - /** - * Tests the Drupal 7 filter format to Drupal 8 migration. - */ - public function testFilterFormat() { - $this->assertEntity('custom_text_format', 'Custom Text format', ['filter_autop', 'filter_html']); - $this->assertEntity('filtered_html', 'Filtered HTML', ['filter_autop', 'filter_html', 'filter_htmlcorrector', 'filter_url']); - $this->assertEntity('full_html', 'Full HTML', ['filter_autop', 'filter_htmlcorrector', 'filter_url']); - $this->assertEntity('plain_text', 'Plain text', ['filter_html_escape', 'filter_url', 'filter_autop']); - // This assertion covers issue #2555089. Drupal 7 formats are identified - // by machine names, so migrated formats should be merged into existing - // ones. - $this->assertNull(FilterFormat::load('plain_text1')); - - // Ensure that filter-specific settings were migrated. - /** @var \Drupal\filter\FilterFormatInterface $format */ - $format = FilterFormat::load('filtered_html'); - $config = $format->filters('filter_html')->getConfiguration(); - $this->assertIdentical('
              1. ', $config['settings']['allowed_html']); - $config = $format->filters('filter_url')->getConfiguration(); - $this->assertIdentical(128, $config['settings']['filter_url_length']); - - // The php_code format gets migrated, but the php_code filter is changed to - // filter_null. - $filters = FilterFormat::load('php_code')->get('filters'); - $this->assertTrue(isset($filters['filter_null'])); - } - -} diff --git a/core/modules/filter/src/Tests/TextFormatElementFormTest.php b/core/modules/filter/src/Tests/TextFormatElementFormTest.php deleted file mode 100644 index b889c4a..0000000 --- a/core/modules/filter/src/Tests/TextFormatElementFormTest.php +++ /dev/null @@ -1,135 +0,0 @@ -installEntitySchema('user'); - $this->installSchema('system', ['sequences', 'router']); - $this->installConfig(['filter', 'filter_test']); - // Filter tips link to the full-page. - \Drupal::service('router.builder')->rebuild(); - /* @var \Drupal\Core\Render\ElementInfoManager $manager */ - $manager = \Drupal::service('plugin.manager.element_info'); - $manager->clearCachedDefinitions(); - $manager->getDefinitions(); - /* @var \Drupal\filter\FilterFormatInterface $filter_test_format */ - $filter_test_format = FilterFormat::load('filter_test'); - - /* @var \Drupal\user\RoleInterface $role */ - $role = Role::create([ - 'id' => 'admin', - 'label' => 'admin', - ]); - $role->grantPermission($filter_test_format->getPermissionName()); - $role->save(); - $this->testUser = User::create([ - 'name' => 'foobar', - 'mail' => 'foobar@example.com', - ]); - $this->testUser->addRole($role->id()); - $this->testUser->save(); - \Drupal::service('current_user')->setAccount($this->testUser); - } - - /** - * {@inheritdoc} - */ - public function getFormId() { - return 'test_text_area_element'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state) { - // A textformat field. - $form['textformat'] = [ - '#type' => 'text_format', - '#required' => TRUE, - '#title' => 'Text', - '#base_type' => 'textfield', - '#format' => NULL, - '#default_value' => 'test value', - ]; - - return $form; - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) {} - - /** - * Form validation handler. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - */ - public function validateForm(array &$form, FormStateInterface $form_state) {} - - /** - * Tests that values are returned. - */ - public function testTextFormatElement() { - /* @var \Drupal\Core\Form\FormBuilder $form_builder */ - $form_builder = $this->container->get('form_builder'); - $form = $form_builder->getForm($this); - $output = $this->render($form); - $this->setRawContent($output); - $this->assertFieldByName('textformat[value]'); - $this->assertRaw('

                Full HTML

                '); - $this->assertRaw('

                Filtered HTML

                '); - $this->assertRaw('

                Test format

                '); - $this->assertNoPattern('|]*>|', 'No empty H4 element found.'); - } - - /** - * {@inheritdoc} - */ - protected function getUrl() { - // \Drupal\simpletest\AssertContentTrait needs this for ::assertFieldByName - // to work. - return 'Internal rendering'; - } - -} diff --git a/core/modules/filter/tests/filter_test_plugin/src/Plugin/Filter/FilterSparkles.php b/core/modules/filter/tests/filter_test_plugin/src/Plugin/Filter/FilterSparkles.php index 5edd607..338a531 100644 --- a/core/modules/filter/tests/filter_test_plugin/src/Plugin/Filter/FilterSparkles.php +++ b/core/modules/filter/tests/filter_test_plugin/src/Plugin/Filter/FilterSparkles.php @@ -3,11 +3,6 @@ /** * @file * Contains \Drupal\filter_test_plugin\Plugin\Filter\FilterSparkles. - * - * This filter does not do anything, but enabling of its module is done in a - * test. - * - * @see \Drupal\filter\Tests\FilterFormTest::testFilterForm() */ namespace Drupal\filter_test_plugin\Plugin\Filter; @@ -18,6 +13,11 @@ /** * Provides a filter to limit allowed HTML tags. * + * This filter does not do anything, but enabling of its module is done in a + * test. + * + * @see \Drupal\filter\Tests\FilterFormTest::testFilterForm() + * * @Filter( * id = "filter_sparkles", * title = @Translation("Sparkles filter"), diff --git a/core/modules/filter/tests/src/Kernel/FilterAPITest.php b/core/modules/filter/tests/src/Kernel/FilterAPITest.php new file mode 100644 index 0000000..1a8f709 --- /dev/null +++ b/core/modules/filter/tests/src/Kernel/FilterAPITest.php @@ -0,0 +1,514 @@ +installConfig(array('system', 'filter', 'filter_test')); + } + + /** + * Tests that the filter order is respected. + */ + function testCheckMarkupFilterOrder() { + // Create crazy HTML format. + $crazy_format = FilterFormat::create(array( + 'format' => 'crazy', + 'name' => 'Crazy', + 'weight' => 1, + 'filters' => array( + 'filter_html_escape' => array( + 'weight' => 10, + 'status' => 1, + ), + 'filter_html' => array( + 'weight' => -10, + 'status' => 1, + 'settings' => array( + 'allowed_html' => '

                ', + ), + ), + ) + )); + $crazy_format->save(); + + $text = "

                Llamas are awesome!

                "; + $expected_filtered_text = "<p>Llamas are awesome!</p>"; + + $this->assertEqual(check_markup($text, 'crazy'), $expected_filtered_text, 'Filters applied in correct order.'); + } + + /** + * Tests the ability to apply only a subset of filters. + */ + function testCheckMarkupFilterSubset() { + $text = "Text with evil content and a URL: https://www.drupal.org!"; + $expected_filtered_text = "Text with evil content and a URL:
                https://www.drupal.org!"; + $expected_filter_text_without_html_generators = "Text with evil content and a URL: https://www.drupal.org!"; + + $actual_filtered_text = check_markup($text, 'filtered_html', '', array()); + $this->verbose("Actual:
                $actual_filtered_text
                Expected:
                $expected_filtered_text
                "); + $this->assertEqual( + $actual_filtered_text, + $expected_filtered_text, + 'Expected filter result.' + ); + $actual_filtered_text_without_html_generators = check_markup($text, 'filtered_html', '', array(FilterInterface::TYPE_MARKUP_LANGUAGE)); + $this->verbose("Actual:
                $actual_filtered_text_without_html_generators
                Expected:
                $expected_filter_text_without_html_generators
                "); + $this->assertEqual( + $actual_filtered_text_without_html_generators, + $expected_filter_text_without_html_generators, + 'Expected filter result when skipping FilterInterface::TYPE_MARKUP_LANGUAGE filters.' + ); + // Related to @see FilterSecurityTest.php/testSkipSecurityFilters(), but + // this check focuses on the ability to filter multiple filter types at once. + // Drupal core only ships with these two types of filters, so this is the + // most extensive test possible. + $actual_filtered_text_without_html_generators = check_markup($text, 'filtered_html', '', array(FilterInterface::TYPE_HTML_RESTRICTOR, FilterInterface::TYPE_MARKUP_LANGUAGE)); + $this->verbose("Actual:
                $actual_filtered_text_without_html_generators
                Expected:
                $expected_filter_text_without_html_generators
                "); + $this->assertEqual( + $actual_filtered_text_without_html_generators, + $expected_filter_text_without_html_generators, + 'Expected filter result when skipping FilterInterface::TYPE_MARKUP_LANGUAGE filters, even when trying to disable filters of the FilterInterface::TYPE_HTML_RESTRICTOR type.' + ); + } + + /** + * Tests the following functions for a variety of formats: + * - \Drupal\filter\Entity\FilterFormatInterface::getHtmlRestrictions() + * - \Drupal\filter\Entity\FilterFormatInterface::getFilterTypes() + */ + function testFilterFormatAPI() { + // Test on filtered_html. + $filtered_html_format = entity_load('filter_format', 'filtered_html'); + $this->assertIdentical( + $filtered_html_format->getHtmlRestrictions(), + array( + 'allowed' => array( + 'p' => FALSE, + 'br' => FALSE, + 'strong' => FALSE, + 'a' => array('href' => TRUE, 'hreflang' => TRUE), + '*' => array('style' => FALSE, 'on*' => FALSE, 'lang' => TRUE, 'dir' => array('ltr' => TRUE, 'rtl' => TRUE)), + ), + ), + 'FilterFormatInterface::getHtmlRestrictions() works as expected for the filtered_html format.' + ); + $this->assertIdentical( + $filtered_html_format->getFilterTypes(), + array(FilterInterface::TYPE_HTML_RESTRICTOR, FilterInterface::TYPE_MARKUP_LANGUAGE), + 'FilterFormatInterface::getFilterTypes() works as expected for the filtered_html format.' + ); + + // Test on full_html. + $full_html_format = entity_load('filter_format', 'full_html'); + $this->assertIdentical( + $full_html_format->getHtmlRestrictions(), + FALSE, // Every tag is allowed. + 'FilterFormatInterface::getHtmlRestrictions() works as expected for the full_html format.' + ); + $this->assertIdentical( + $full_html_format->getFilterTypes(), + array(), + 'FilterFormatInterface::getFilterTypes() works as expected for the full_html format.' + ); + + // Test on stupid_filtered_html, where nothing is allowed. + $stupid_filtered_html_format = FilterFormat::create(array( + 'format' => 'stupid_filtered_html', + 'name' => 'Stupid Filtered HTML', + 'filters' => array( + 'filter_html' => array( + 'status' => 1, + 'settings' => array( + 'allowed_html' => '', // Nothing is allowed. + ), + ), + ), + )); + $stupid_filtered_html_format->save(); + $this->assertIdentical( + $stupid_filtered_html_format->getHtmlRestrictions(), + array('allowed' => array()), // No tag is allowed. + 'FilterFormatInterface::getHtmlRestrictions() works as expected for the stupid_filtered_html format.' + ); + $this->assertIdentical( + $stupid_filtered_html_format->getFilterTypes(), + array(FilterInterface::TYPE_HTML_RESTRICTOR), + 'FilterFormatInterface::getFilterTypes() works as expected for the stupid_filtered_html format.' + ); + + // Test on very_restricted_html, where there's two different filters of the + // FilterInterface::TYPE_HTML_RESTRICTOR type, each restricting in different ways. + $very_restricted_html_format = FilterFormat::create(array( + 'format' => 'very_restricted_html', + 'name' => 'Very Restricted HTML', + 'filters' => array( + 'filter_html' => array( + 'status' => 1, + 'settings' => array( + 'allowed_html' => '


                ', + ), + ), + 'filter_test_restrict_tags_and_attributes' => array( + 'status' => 1, + 'settings' => array( + 'restrictions' => array( + 'allowed' => array( + 'p' => TRUE, + 'br' => FALSE, + 'a' => array('href' => TRUE), + 'em' => TRUE, + ), + ) + ), + ), + ) + )); + $very_restricted_html_format->save(); + $this->assertIdentical( + $very_restricted_html_format->getHtmlRestrictions(), + array( + 'allowed' => array( + 'p' => FALSE, + 'br' => FALSE, + 'a' => array('href' => TRUE), + '*' => array('style' => FALSE, 'on*' => FALSE, 'lang' => TRUE, 'dir' => array('ltr' => TRUE, 'rtl' => TRUE)), + ), + ), + 'FilterFormatInterface::getHtmlRestrictions() works as expected for the very_restricted_html format.' + ); + $this->assertIdentical( + $very_restricted_html_format->getFilterTypes(), + array(FilterInterface::TYPE_HTML_RESTRICTOR), + 'FilterFormatInterface::getFilterTypes() works as expected for the very_restricted_html format.' + ); + + // Test on nonsensical_restricted_html, where the allowed attribute values + // contain asterisks, which do not have any meaning, but which we also + // cannot prevent because configuration can be modified outside of forms. + $nonsensical_restricted_html = \Drupal\filter\Entity\FilterFormat::create(array( + 'format' => 'nonsensical_restricted_html', + 'name' => 'Nonsensical Restricted HTML', + 'filters' => array( + 'filter_html' => array( + 'status' => 1, + 'settings' => array( + 'allowed_html' => ' ', + ), + ), + ) + )); + $nonsensical_restricted_html->save(); + $this->assertIdentical( + $nonsensical_restricted_html->getHtmlRestrictions(), + array( + 'allowed' => array( + 'a' => FALSE, + 'b' => array('class' => TRUE), + 'c' => array('class' => TRUE), + 'd' => array('class' => array('foo' => TRUE, 'bar-*' => TRUE)), + '*' => array('style' => FALSE, 'on*' => FALSE, 'lang' => TRUE, 'dir' => array('ltr' => TRUE, 'rtl' => TRUE)), + ), + ), + 'FilterFormatInterface::getHtmlRestrictions() works as expected for the nonsensical_restricted_html format.' + ); + $this->assertIdentical( + $very_restricted_html_format->getFilterTypes(), + array(FilterInterface::TYPE_HTML_RESTRICTOR), + 'FilterFormatInterface::getFilterTypes() works as expected for the very_restricted_html format.' + ); + } + + /** + * Tests the 'processed_text' element. + * + * check_markup() is a wrapper for the 'processed_text' element, for use in + * simple scenarios; the 'processed_text' element has more advanced features: + * it lets filters attach assets, associate cache tags and define + * #lazy_builder callbacks. + * This test focuses solely on those advanced features. + */ + function testProcessedTextElement() { + FilterFormat::create(array( + 'format' => 'element_test', + 'name' => 'processed_text element test format', + 'filters' => array( + 'filter_test_assets' => array( + 'weight' => -1, + 'status' => TRUE, + ), + 'filter_test_cache_tags' => array( + 'weight' => 0, + 'status' => TRUE, + ), + 'filter_test_cache_contexts' => array( + 'weight' => 0, + 'status' => TRUE, + ), + 'filter_test_cache_merge' => array( + 'weight' => 0, + 'status' => TRUE, + ), + 'filter_test_placeholders' => array( + 'weight' => 1, + 'status' => TRUE, + ), + // Run the HTML corrector filter last, because it has the potential to + // break the placeholders added by the filter_test_placeholders filter. + 'filter_htmlcorrector' => array( + 'weight' => 10, + 'status' => TRUE, + ), + ), + ))->save(); + + $build = array( + '#type' => 'processed_text', + '#text' => '

                Hello, world!

                ', + '#format' => 'element_test', + ); + drupal_render_root($build); + + // Verify the attachments and cacheability metadata. + $expected_attachments = array( + // The assets attached by the filter_test_assets filter. + 'library' => array( + 'filter/caption', + ), + // The placeholders attached that still need to be processed. + 'placeholders' => [], + ); + $this->assertEqual($expected_attachments, $build['#attached'], 'Expected attachments present'); + $expected_cache_tags = array( + // The cache tag set by the processed_text element itself. + 'config:filter.format.element_test', + // The cache tags set by the filter_test_cache_tags filter. + 'foo:bar', + 'foo:baz', + // The cache tags set by the filter_test_cache_merge filter. + 'merge:tag', + ); + $this->assertEqual($expected_cache_tags, $build['#cache']['tags'], 'Expected cache tags present.'); + $expected_cache_contexts = [ + // The cache context set by the filter_test_cache_contexts filter. + 'languages:' . LanguageInterface::TYPE_CONTENT, + // The default cache contexts for Renderer. + 'languages:' . LanguageInterface::TYPE_INTERFACE, + 'theme', + // The cache tags set by the filter_test_cache_merge filter. + 'user.permissions', + ]; + $this->assertEqual($expected_cache_contexts, $build['#cache']['contexts'], 'Expected cache contexts present.'); + $expected_markup = '

                Hello, world!

                This is a dynamic llama.

                '; + $this->assertEqual($expected_markup, $build['#markup'], 'Expected #lazy_builder callback has been applied.'); + } + + /** + * Tests the function of the typed data type. + */ + function testTypedDataAPI() { + $definition = DataDefinition::create('filter_format'); + $data = \Drupal::typedDataManager()->create($definition); + + $this->assertTrue($data instanceof OptionsProviderInterface, 'Typed data object implements \Drupal\Core\TypedData\OptionsProviderInterface'); + + $filtered_html_user = $this->createUser(array('uid' => 2), array( + entity_load('filter_format', 'filtered_html')->getPermissionName(), + )); + + // Test with anonymous user. + $user = new AnonymousUserSession(); + \Drupal::currentUser()->setAccount($user); + + $expected_available_options = array( + 'filtered_html' => 'Filtered HTML', + 'full_html' => 'Full HTML', + 'filter_test' => 'Test format', + 'plain_text' => 'Plain text', + ); + + $available_values = $data->getPossibleValues(); + $this->assertEqual($available_values, array_keys($expected_available_options)); + $available_options = $data->getPossibleOptions(); + $this->assertEqual($available_options, $expected_available_options); + + $allowed_values = $data->getSettableValues($user); + $this->assertEqual($allowed_values, array('plain_text')); + $allowed_options = $data->getSettableOptions($user); + $this->assertEqual($allowed_options, array('plain_text' => 'Plain text')); + + $data->setValue('foo'); + $violations = $data->validate(); + $this->assertFilterFormatViolation($violations, 'foo'); + + // Make sure the information provided by a violation is correct. + $violation = $violations[0]; + $this->assertEqual($violation->getRoot(), $data, 'Violation root is filter format.'); + $this->assertEqual($violation->getPropertyPath(), '', 'Violation property path is correct.'); + $this->assertEqual($violation->getInvalidValue(), 'foo', 'Violation contains invalid value.'); + + $data->setValue('plain_text'); + $violations = $data->validate(); + $this->assertEqual(count($violations), 0, "No validation violation for format 'plain_text' found"); + + // Anonymous doesn't have access to the 'filtered_html' format. + $data->setValue('filtered_html'); + $violations = $data->validate(); + $this->assertFilterFormatViolation($violations, 'filtered_html'); + + // Set user with access to 'filtered_html' format. + \Drupal::currentUser()->setAccount($filtered_html_user); + $violations = $data->validate(); + $this->assertEqual(count($violations), 0, "No validation violation for accessible format 'filtered_html' found."); + + $allowed_values = $data->getSettableValues($filtered_html_user); + $this->assertEqual($allowed_values, array('filtered_html', 'plain_text')); + $allowed_options = $data->getSettableOptions($filtered_html_user); + $expected_allowed_options = array( + 'filtered_html' => 'Filtered HTML', + 'plain_text' => 'Plain text', + ); + $this->assertEqual($allowed_options, $expected_allowed_options); + } + + /** + * Tests that FilterFormat::preSave() only saves customized plugins. + */ + public function testFilterFormatPreSave() { + /** @var \Drupal\filter\FilterFormatInterface $crazy_format */ + $crazy_format = FilterFormat::create(array( + 'format' => 'crazy', + 'name' => 'Crazy', + 'weight' => 1, + 'filters' => array( + 'filter_html_escape' => array( + 'weight' => 10, + 'status' => 1, + ), + 'filter_html' => array( + 'weight' => -10, + 'status' => 1, + 'settings' => array( + 'allowed_html' => '

                ', + ), + ), + ) + )); + $crazy_format->save(); + // Use config to directly load the configuration and check that only enabled + // or customized plugins are saved to configuration. + $filters = $this->config('filter.format.crazy')->get('filters'); + $this->assertEqual(array('filter_html_escape', 'filter_html'), array_keys($filters)); + + // Disable a plugin to ensure that disabled plugins with custom settings are + // stored in configuration. + $crazy_format->setFilterConfig('filter_html_escape', array('status' => FALSE)); + $crazy_format->save(); + $filters = $this->config('filter.format.crazy')->get('filters'); + $this->assertEqual(array('filter_html_escape', 'filter_html'), array_keys($filters)); + + // Set the settings as per default to ensure that disable plugins in this + // state are not stored in configuration. + $crazy_format->setFilterConfig('filter_html_escape', array('weight' => -10)); + $crazy_format->save(); + $filters = $this->config('filter.format.crazy')->get('filters'); + $this->assertEqual(array('filter_html'), array_keys($filters)); + } + + /** + * Checks if an expected violation exists in the given violations. + * + * @param \Symfony\Component\Validator\ConstraintViolationListInterface $violations + * The violations to assert. + * @param mixed $invalid_value + * The expected invalid value. + */ + public function assertFilterFormatViolation(ConstraintViolationListInterface $violations, $invalid_value) { + $filter_format_violation_found = FALSE; + foreach ($violations as $violation) { + if ($violation->getRoot() instanceof FilterFormatDataType && $violation->getInvalidValue() === $invalid_value) { + $filter_format_violation_found = TRUE; + break; + } + } + $this->assertTrue($filter_format_violation_found, format_string('Validation violation for invalid value "%invalid_value" found', array('%invalid_value' => $invalid_value))); + } + + /** + * Tests that filter format dependency removal works. + * + * Ensure that modules providing filter plugins are required when the plugin + * is in use, and that only disabled plugins are removed from format + * configuration entities rather than the configuration entities being + * deleted. + * + * @see \Drupal\filter\Entity\FilterFormat::onDependencyRemoval() + * @see filter_system_info_alter() + */ + public function testDependencyRemoval() { + $this->installSchema('user', array('users_data')); + $filter_format = \Drupal\filter\Entity\FilterFormat::load('filtered_html'); + + // Disable the filter_test_restrict_tags_and_attributes filter plugin but + // have custom configuration so that the filter plugin is still configured + // in filtered_html the filter format. + $filter_config = [ + 'weight' => 20, + 'status' => 0, + ]; + $filter_format->setFilterConfig('filter_test_restrict_tags_and_attributes', $filter_config)->save(); + // Use the get method to match the assert after the module has been + // uninstalled. + $filters = $filter_format->get('filters'); + $this->assertTrue(isset($filters['filter_test_restrict_tags_and_attributes']), 'The filter plugin filter_test_restrict_tags_and_attributes is configured by the filtered_html filter format.'); + + drupal_static_reset('filter_formats'); + \Drupal::entityManager()->getStorage('filter_format')->resetCache(); + $module_data = _system_rebuild_module_data(); + $this->assertFalse(isset($module_data['filter_test']->info['required']), 'The filter_test module is required.'); + + // Verify that a dependency exists on the module that provides the filter + // plugin since it has configuration for the disabled plugin. + $this->assertEqual(['module' => ['filter_test']], $filter_format->getDependencies()); + + // Uninstall the module. + \Drupal::service('module_installer')->uninstall(array('filter_test')); + + // Verify the filter format still exists but the dependency and filter is + // gone. + \Drupal::entityManager()->getStorage('filter_format')->resetCache(); + $filter_format = \Drupal\filter\Entity\FilterFormat::load('filtered_html'); + $this->assertEqual([], $filter_format->getDependencies()); + // Use the get method since the FilterFormat::filters() method only returns + // existing plugins. + $filters = $filter_format->get('filters'); + $this->assertFalse(isset($filters['filter_test_restrict_tags_and_attributes']), 'The filter plugin filter_test_restrict_tags_and_attributes is not configured by the filtered_html filter format.'); + } + +} diff --git a/core/modules/filter/tests/src/Kernel/FilterCrudTest.php b/core/modules/filter/tests/src/Kernel/FilterCrudTest.php new file mode 100644 index 0000000..1f54f65 --- /dev/null +++ b/core/modules/filter/tests/src/Kernel/FilterCrudTest.php @@ -0,0 +1,109 @@ + 'empty_format', + 'name' => 'Empty format', + )); + $format->save(); + $this->verifyTextFormat($format); + + // Add another text format specifying all possible properties. + $format = FilterFormat::create(array( + 'format' => 'custom_format', + 'name' => 'Custom format', + )); + $format->setFilterConfig('filter_url', array( + 'status' => 1, + 'settings' => array( + 'filter_url_length' => 30, + ), + )); + $format->save(); + $this->verifyTextFormat($format); + + // Alter some text format properties and save again. + $format->set('name', 'Altered format'); + $format->setFilterConfig('filter_url', array( + 'status' => 0, + )); + $format->setFilterConfig('filter_autop', array( + 'status' => 1, + )); + $format->save(); + $this->verifyTextFormat($format); + + // Add a filter_test_replace filter and save again. + $format->setFilterConfig('filter_test_replace', array( + 'status' => 1, + )); + $format->save(); + $this->verifyTextFormat($format); + + // Disable the text format. + $format->disable()->save(); + + $formats = filter_formats(); + $this->assertTrue(!isset($formats[$format->id()]), 'filter_formats: Disabled text format no longer exists.'); + } + + /** + * Tests disabling the fallback text format. + */ + public function testDisableFallbackFormat() { + $this->installConfig(['filter']); + $message = '\LogicException with message "The fallback text format \'plain_text\' cannot be disabled." was thrown.'; + try { + FilterFormat::load('plain_text')->disable(); + $this->fail($message); + } + catch (\LogicException $e) { + $this->assertIdentical($e->getMessage(), "The fallback text format 'plain_text' cannot be disabled.", $message); + } + } + + /** + * Verifies that a text format is properly stored. + */ + function verifyTextFormat($format) { + $t_args = array('%format' => $format->label()); + $default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId(); + + // Verify the loaded filter has all properties. + $filter_format = entity_load('filter_format', $format->id()); + $this->assertEqual($filter_format->id(), $format->id(), format_string('filter_format_load: Proper format id for text format %format.', $t_args)); + $this->assertEqual($filter_format->label(), $format->label(), format_string('filter_format_load: Proper title for text format %format.', $t_args)); + $this->assertEqual($filter_format->get('weight'), $format->get('weight'), format_string('filter_format_load: Proper weight for text format %format.', $t_args)); + // Check that the filter was created in site default language. + $this->assertEqual($format->language()->getId(), $default_langcode, format_string('filter_format_load: Proper language code for text format %format.', $t_args)); + } + +} diff --git a/core/modules/filter/tests/src/Kernel/FilterDefaultConfigTest.php b/core/modules/filter/tests/src/Kernel/FilterDefaultConfigTest.php new file mode 100644 index 0000000..c41a2b1 --- /dev/null +++ b/core/modules/filter/tests/src/Kernel/FilterDefaultConfigTest.php @@ -0,0 +1,99 @@ +installEntitySchema('user'); + + // Install filter_test module, which ships with custom default format. + $this->installConfig(array('user', 'filter_test')); + } + + /** + * Tests installation of default formats. + */ + function testInstallation() { + // Verify that the format was installed correctly. + $format = entity_load('filter_format', 'filter_test'); + $this->assertTrue((bool) $format); + $this->assertEqual($format->id(), 'filter_test'); + $this->assertEqual($format->label(), 'Test format'); + $this->assertEqual($format->get('weight'), 2); + + // Verify that format default property values have been added/injected. + $this->assertTrue($format->uuid()); + + // Verify that the loaded format does not contain any roles. + $this->assertEqual($format->get('roles'), NULL); + // Verify that the defined roles in the default config have been processed. + $this->assertEqual(array_keys(filter_get_roles_by_format($format)), array( + RoleInterface::ANONYMOUS_ID, + RoleInterface::AUTHENTICATED_ID, + )); + + // Verify enabled filters. + $filters = $format->get('filters'); + $this->assertEqual($filters['filter_html_escape']['status'], 1); + $this->assertEqual($filters['filter_html_escape']['weight'], -10); + $this->assertEqual($filters['filter_html_escape']['provider'], 'filter'); + $this->assertEqual($filters['filter_html_escape']['settings'], array()); + $this->assertEqual($filters['filter_autop']['status'], 1); + $this->assertEqual($filters['filter_autop']['weight'], 0); + $this->assertEqual($filters['filter_autop']['provider'], 'filter'); + $this->assertEqual($filters['filter_autop']['settings'], array()); + $this->assertEqual($filters['filter_url']['status'], 1); + $this->assertEqual($filters['filter_url']['weight'], 0); + $this->assertEqual($filters['filter_url']['provider'], 'filter'); + $this->assertEqual($filters['filter_url']['settings'], array( + 'filter_url_length' => 72, + )); + } + + /** + * Tests that changes to FilterFormat::$roles do not have an effect. + */ + function testUpdateRoles() { + // Verify role permissions declared in default config. + $format = entity_load('filter_format', 'filter_test'); + $this->assertEqual(array_keys(filter_get_roles_by_format($format)), array( + RoleInterface::ANONYMOUS_ID, + RoleInterface::AUTHENTICATED_ID, + )); + + // Attempt to change roles. + $format->set('roles', array( + RoleInterface::AUTHENTICATED_ID, + )); + $format->save(); + + // Verify that roles have not been updated. + $format = entity_load('filter_format', 'filter_test'); + $this->assertEqual(array_keys(filter_get_roles_by_format($format)), array( + RoleInterface::ANONYMOUS_ID, + RoleInterface::AUTHENTICATED_ID, + )); + } + +} diff --git a/core/modules/filter/tests/src/Kernel/FilterSettingsTest.php b/core/modules/filter/tests/src/Kernel/FilterSettingsTest.php new file mode 100644 index 0000000..2db4130 --- /dev/null +++ b/core/modules/filter/tests/src/Kernel/FilterSettingsTest.php @@ -0,0 +1,66 @@ +container->get('plugin.manager.filter')->getDefinitions(); + + // Create text format using filter default settings. + $filter_defaults_format = FilterFormat::create(array( + 'format' => 'filter_defaults', + 'name' => 'Filter defaults', + )); + $filter_defaults_format->save(); + + // Verify that default weights defined in hook_filter_info() were applied. + $saved_settings = array(); + foreach ($filter_defaults_format->filters() as $name => $filter) { + $expected_weight = $filter_info[$name]['weight']; + $this->assertEqual($filter->weight, $expected_weight, format_string('@name filter weight %saved equals %default', array( + '@name' => $name, + '%saved' => $filter->weight, + '%default' => $expected_weight, + ))); + $saved_settings[$name]['weight'] = $expected_weight; + } + + // Re-save the text format. + $filter_defaults_format->save(); + // Reload it from scratch. + filter_formats_reset(); + + // Verify that saved filter settings have not been changed. + foreach ($filter_defaults_format->filters() as $name => $filter) { + $this->assertEqual($filter->weight, $saved_settings[$name]['weight'], format_string('@name filter weight %saved equals %previous', array( + '@name' => $name, + '%saved' => $filter->weight, + '%previous' => $saved_settings[$name]['weight'], + ))); + } + } +} diff --git a/core/modules/filter/tests/src/Kernel/FilterUnitTest.php b/core/modules/filter/tests/src/Kernel/FilterUnitTest.php new file mode 100644 index 0000000..87988bd --- /dev/null +++ b/core/modules/filter/tests/src/Kernel/FilterUnitTest.php @@ -0,0 +1,1184 @@ +installConfig(array('system')); + + $manager = $this->container->get('plugin.manager.filter'); + $bag = new FilterPluginCollection($manager, array()); + $this->filters = $bag->getAll(); + } + + /** + * Tests the align filter. + */ + function testAlignFilter() { + $filter = $this->filters['filter_align']; + + $test = function($input) use ($filter) { + return $filter->process($input, 'und'); + }; + + // No data-align attribute. + $input = ''; + $expected = $input; + $this->assertIdentical($expected, $test($input)->getProcessedText()); + + // Data-align attribute: all 3 allowed values. + $input = ''; + $expected = ''; + $this->assertIdentical($expected, $test($input)->getProcessedText()); + $input = ''; + $expected = ''; + $this->assertIdentical($expected, $test($input)->getProcessedText()); + $input = ''; + $expected = ''; + $this->assertIdentical($expected, $test($input)->getProcessedText()); + + // Data-align attribute: a disallowed value. + $input = ''; + $expected = ''; + $this->assertIdentical($expected, $test($input)->getProcessedText()); + + // Empty data-align attribute. + $input = ''; + $expected = ''; + $this->assertIdentical($expected, $test($input)->getProcessedText()); + + // Ensure the filter also works with uncommon yet valid attribute quoting. + $input = ''; + $expected = ''; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + + // Security test: attempt to inject an additional class. + $input = ''; + $expected = ''; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + + // Security test: attempt an XSS. + $input = ''; + $expected = ''; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + } + + /** + * Tests the caption filter. + */ + function testCaptionFilter() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + $filter = $this->filters['filter_caption']; + + $test = function($input) use ($filter, $renderer) { + return $renderer->executeInRenderContext(new RenderContext(), function () use ($input, $filter) { + return $filter->process($input, 'und'); + }); + }; + + $attached_library = array( + 'library' => array( + 'filter/caption', + ), + ); + + // No data-caption attribute. + $input = ''; + $expected = $input; + $this->assertIdentical($expected, $test($input)->getProcessedText()); + + // Data-caption attribute. + $input = ''; + $expected = '

                Loquacious llama!
                '; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + $this->assertIdentical($attached_library, $output->getAttachments()); + + // Empty data-caption attribute. + $input = ''; + $expected = ''; + $this->assertIdentical($expected, $test($input)->getProcessedText()); + + // HTML entities in the caption. + $input = ''; + $expected = '
                “Loquacious llama!”
                '; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + $this->assertIdentical($attached_library, $output->getAttachments()); + + // HTML encoded as HTML entities in data-caption attribute. + $input = ''; + $expected = '
                Loquacious llama!
                '; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + $this->assertIdentical($attached_library, $output->getAttachments()); + + // HTML (not encoded as HTML entities) in data-caption attribute, which is + // not allowed by the HTML spec, but may happen when people manually write + // HTML, so we explicitly support it. + $input = ''; + $expected = '
                Loquacious llama!
                '; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + $this->assertIdentical($attached_library, $output->getAttachments()); + + // Security test: attempt an XSS. + $input = ''; + $expected = '
                alert(\'Loquacious llama!\')
                '; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + $this->assertIdentical($attached_library, $output->getAttachments()); + + // Ensure the filter also works with uncommon yet valid attribute quoting. + $input = ''; + $expected = '
                Loquacious llama!
                '; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + $this->assertIdentical($attached_library, $output->getAttachments()); + + // Finally, ensure that this also works on any other tag. + $input = '
                '; + $expected = '
                ' . "\n" . '
                Loquacious llama!
                '; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + $this->assertIdentical($attached_library, $output->getAttachments()); + + // So far we've tested that the caption filter works correctly. But we also + // want to make sure that it works well in tandem with the "Limit allowed + // HTML tags" filter, which it is typically used with. + $html_filter = $this->filters['filter_html']; + $html_filter->setConfiguration(array( + 'settings' => array( + 'allowed_html' => '', + 'filter_html_help' => 1, + 'filter_html_nofollow' => 0, + ) + )); + $test_with_html_filter = function ($input) use ($filter, $html_filter, $renderer) { + return $renderer->executeInRenderContext(new RenderContext(), function () use ($input, $filter, $html_filter) { + // 1. Apply HTML filter's processing step. + $output = $html_filter->process($input, 'und'); + // 2. Apply caption filter's processing step. + $output = $filter->process($output, 'und'); + return $output->getProcessedText(); + }); + }; + // Editor XSS filter. + $test_editor_xss_filter = function ($input) { + $dummy_filter_format = FilterFormat::create(); + return Standard::filterXss($input, $dummy_filter_format); + }; + + // All the tricky cases encountered at https://www.drupal.org/node/2105841. + // A plain URL preceded by text. + $input = ''; + $expected = '
                See https://www.drupal.org
                '; + $this->assertIdentical($expected, $test_with_html_filter($input)); + $this->assertIdentical($input, $test_editor_xss_filter($input)); + + // An anchor. + $input = ''; + $expected = '
                This is a quick test…
                '; + $this->assertIdentical($expected, $test_with_html_filter($input)); + $this->assertIdentical($input, $test_editor_xss_filter($input)); + + // A plain URL surrounded by parentheses. + $input = ''; + $expected = '
                (https://www.drupal.org)
                '; + $this->assertIdentical($expected, $test_with_html_filter($input)); + $this->assertIdentical($input, $test_editor_xss_filter($input)); + + // A source being credited. + $input = ''; + $expected = '
                Source: Wikipedia
                '; + $this->assertIdentical($expected, $test_with_html_filter($input)); + $this->assertIdentical($input, $test_editor_xss_filter($input)); + + // A source being credited, without a space after the colon. + $input = ''; + $expected = '
                Source:Wikipedia
                '; + $this->assertIdentical($expected, $test_with_html_filter($input)); + $this->assertIdentical($input, $test_editor_xss_filter($input)); + + // A pretty crazy edge case where we have two colons. + $input = ''; + $expected = '
                Interesting (Scope resolution operator ::)
                '; + $this->assertIdentical($expected, $test_with_html_filter($input)); + $this->assertIdentical($input, $test_editor_xss_filter($input)); + + // An evil anchor (to ensure XSS filtering is applied to the caption also). + $input = ''; + $expected = '
                This is an evil test…
                '; + $this->assertIdentical($expected, $test_with_html_filter($input)); + $expected_xss_filtered = ''; + $this->assertIdentical($expected_xss_filtered, $test_editor_xss_filter($input)); + } + + /** + * Tests the combination of the align and caption filters. + */ + function testAlignAndCaptionFilters() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + $align_filter = $this->filters['filter_align']; + $caption_filter = $this->filters['filter_caption']; + + $test = function($input) use ($align_filter, $caption_filter, $renderer) { + return $renderer->executeInRenderContext(new RenderContext(), function () use ($input, $align_filter, $caption_filter) { + return $caption_filter->process($align_filter->process($input, 'und'), 'und'); + }); + }; + + $attached_library = array( + 'library' => array( + 'filter/caption', + ), + ); + + // Both data-caption and data-align attributes: all 3 allowed values for the + // data-align attribute. + $input = ''; + $expected = '
                Loquacious llama!
                '; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + $this->assertIdentical($attached_library, $output->getAttachments()); + $input = ''; + $expected = '
                Loquacious llama!
                '; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + $this->assertIdentical($attached_library, $output->getAttachments()); + $input = ''; + $expected = '
                Loquacious llama!
                '; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + $this->assertIdentical($attached_library, $output->getAttachments()); + + // Both data-caption and data-align attributes, but a disallowed data-align + // attribute value. + $input = ''; + $expected = '
                Loquacious llama!
                '; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + $this->assertIdentical($attached_library, $output->getAttachments()); + + // Ensure both filters together work for linked images. + $input = ''; + $expected = '
                ' . "\n" . '
                Loquacious llama!
                '; + $output = $test($input); + $this->assertIdentical($expected, $output->getProcessedText()); + $this->assertIdentical($attached_library, $output->getAttachments()); + } + + /** + * Tests the line break filter. + */ + function testLineBreakFilter() { + // Get FilterAutoP object. + $filter = $this->filters['filter_autop']; + + // Since the line break filter naturally needs plenty of newlines in test + // strings and expectations, we're using "\n" instead of regular newlines + // here. + $tests = array( + // Single line breaks should be changed to
                tags, while paragraphs + // separated with double line breaks should be enclosed with

                tags. + "aaa\nbbb\n\nccc" => array( + "

                aaa
                \nbbb

                \n

                ccc

                " => TRUE, + ), + // Skip contents of certain block tags entirely. + " + +
                aaa\nbbb\n\nccc
                +aaa\nbbb\n\nccc + +" => array( + "" => TRUE, + "" => TRUE, + "
                aaa\nbbb\n\nccc
                " => TRUE, + "aaa\nbbb\n\nccc" => TRUE, + "" => TRUE, + ), + // Skip comments entirely. + "One. Two.\n\n" => array( + '' => TRUE, + "" => TRUE, + ), + // Resulting HTML should produce matching paragraph tags. + '

                ' => array( + "

                \n

                \n

                " => TRUE, + ), + '

                ' => array( + "
                \n
                " => TRUE, + ), + '
                aaa
                ' => array( + "
                aaa
                " => TRUE, + ), + "
                aaa\nbbb\nccc
                \nddd\neee" => array( + "
                aaa\nbbb\nccc
                " => TRUE, + "

                ddd
                \neee

                " => TRUE, + ), + // Comments remain unchanged and subsequent lines/paragraphs are + // transformed normally. + "aaa\n\nbbb\n\nccc\n\nddd\n\neee\n\nfff" => array( + "

                aaa

                \n

                \nbbb

                \n

                ccc

                \n

                ddd

                " => TRUE, + "

                \neee

                \n

                fff

                " => TRUE, + ), + // Check that a comment in a PRE will result that the text after + // the comment, but still in PRE, is not transformed. + "
                aaa\nbbb\n\nccc
                \nddd" => array( + "
                aaa\nbbb\n\nccc
                " => TRUE, + ), + // Bug 810824, paragraphs were appearing around iframe tags. + "\n\n" => array( + "

                " => FALSE, + ), + ); + $this->assertFilteredString($filter, $tests); + + // Very long string hitting PCRE limits. + $limit = max(ini_get('pcre.backtrack_limit'), ini_get('pcre.recursion_limit')); + $source = $this->randomMachineName($limit); + $result = _filter_autop($source); + $success = $this->assertEqual($result, '

                ' . $source . "

                \n", 'Line break filter can process very long strings.'); + if (!$success) { + $this->verbose("\n" . $source . "\n
                \n" . $result); + } + } + + + /** + * Tests filter settings, defaults, access restrictions and similar. + * + * @todo This is for functions like filter_filter and check_markup, whose + * functionality is not completely focused on filtering. Some ideas: + * restricting formats according to user permissions, proper cache + * handling, defaults -- allowed tags/attributes/protocols. + * + * @todo It is possible to add script, iframe etc. to allowed tags, but this + * makes HTML filter completely ineffective. + * + * @todo Class, id, name and xmlns should be added to disallowed attributes, + * or better a whitelist approach should be used for that too. + */ + function testHtmlFilter() { + // Get FilterHtml object. + $filter = $this->filters['filter_html']; + $filter->setConfiguration(array( + 'settings' => array( + 'allowed_html' => '


                  1. ', + 'filter_html_help' => 1, + 'filter_html_nofollow' => 0, + ) + )); + + // HTML filter is not able to secure some tags, these should never be + // allowed. + $f = (string) $filter->process(' +' => array( + 'href="http://www.example.com"' => FALSE, + 'href="http://example.net"' => FALSE, + ), + ' + +' => array( + 'href' => FALSE, + ), + ' + +' => array( + 'href' => FALSE, + ), + ' + +' => array( + 'href' => FALSE, + ), + ' + +' => array( + 'href' => FALSE, + ), + ' +
                    +
                    www.example.com
                    +
                    http://example.com
                    +
                    person@example.com
                    +
                    Check www.example.net
                    +
                    Some text around http://www.example.info by person@example.info?
                    +
                    +' => array( + 'href="http://www.example.com"' => TRUE, + 'href="http://example.com"' => TRUE, + 'href="mailto:person@example.com"' => TRUE, + 'href="http://www.example.net"' => TRUE, + 'href="http://www.example.info"' => TRUE, + 'href="mailto:person@example.info"' => TRUE, + ), + ' +
                    www.div.com
                    +
                      +
                    • http://listitem.com
                    • +
                    • www.class.listitem.com
                    • +
                    +' => array( + '
                    ' => TRUE, + '
                  2. http://listitem.com
                  3. ' => TRUE, + '
                  4. www.class.listitem.com
                  5. ' => TRUE, + ), + ); + $this->assertFilteredString($filter, $tests); + + // URL trimming. + $filter->setConfiguration(array( + 'settings' => array( + 'filter_url_length' => 20, + ) + )); + $tests = array( + 'www.trimmed.com/d/ff.ext?a=1&b=2#a1' => array( + 'www.trimmed.com/d/f…' => TRUE, + ), + ); + $this->assertFilteredString($filter, $tests); + } + + /** + * Asserts multiple filter output expectations for multiple input strings. + * + * @param FilterInterface $filter + * A input filter object. + * @param array $tests + * An associative array, whereas each key is an arbitrary input string and + * each value is again an associative array whose keys are filter output + * strings and whose values are Booleans indicating whether the output is + * expected or not. + * + * For example: + * @code + * $tests = array( + * 'Input string' => array( + * '

                    Input string

                    ' => TRUE, + * 'Input string FALSE, + * ), + * ); + * @endcode + */ + function assertFilteredString($filter, $tests) { + foreach ($tests as $source => $tasks) { + $result = $filter->process($source, $filter)->getProcessedText(); + foreach ($tasks as $value => $is_expected) { + // Not using assertIdentical, since combination with strpos() is hard to grok. + if ($is_expected) { + $success = $this->assertTrue(strpos($result, $value) !== FALSE, format_string('@source: @value found. Filtered result: @result.', array( + '@source' => var_export($source, TRUE), + '@value' => var_export($value, TRUE), + '@result' => var_export($result, TRUE), + ))); + } + else { + $success = $this->assertTrue(strpos($result, $value) === FALSE, format_string('@source: @value not found. Filtered result: @result.', array( + '@source' => var_export($source, TRUE), + '@value' => var_export($value, TRUE), + '@result' => var_export($result, TRUE), + ))); + } + if (!$success) { + $this->verbose('Source:
                    ' . Html::escape(var_export($source, TRUE)) . '
                    ' + . '
                    ' . 'Result:
                    ' . Html::escape(var_export($result, TRUE)) . '
                    ' + . '
                    ' . ($is_expected ? 'Expected:' : 'Not expected:') + . '
                    ' . Html::escape(var_export($value, TRUE)) . '
                    ' + ); + } + } + } + } + + /** + * Tests URL filter on longer content. + * + * Filters based on regular expressions should also be tested with a more + * complex content than just isolated test lines. + * The most common errors are: + * - accidental '*' (greedy) match instead of '*?' (minimal) match. + * - only matching first occurrence instead of all. + * - newlines not matching '.*'. + * + * This test covers: + * - Document with multiple newlines and paragraphs (two newlines). + * - Mix of several HTML tags, invalid non-HTML tags, tags to ignore and HTML + * comments. + * - Empty HTML tags (BR, IMG). + * - Mix of absolute and partial URLs, and email addresses in one content. + */ + function testUrlFilterContent() { + // Get FilterUrl object. + $filter = $this->filters['filter_url']; + $filter->setConfiguration(array( + 'settings' => array( + 'filter_url_length' => 496, + ) + )); + $path = __DIR__ . '/../..'; + + $input = file_get_contents($path . '/filter.url-input.txt'); + $expected = file_get_contents($path . '/filter.url-output.txt'); + $result = _filter_url($input, $filter); + $this->assertIdentical($result, $expected, 'Complex HTML document was correctly processed.'); + } + + /** + * Tests the HTML corrector filter. + * + * @todo This test could really use some validity checking function. + */ + function testHtmlCorrectorFilter() { + // Tag closing. + $f = Html::normalize('

                    text'); + $this->assertEqual($f, '

                    text

                    ', 'HTML corrector -- tag closing at the end of input.'); + + $f = Html::normalize('

                    text

                    text'); + $this->assertEqual($f, '

                    text

                    text

                    ', 'HTML corrector -- tag closing.'); + + $f = Html::normalize("
                    • e1
                    • e2"); + $this->assertEqual($f, "
                      • e1
                      • e2
                      ", 'HTML corrector -- unclosed list tags.'); + + $f = Html::normalize('
                      content'); + $this->assertEqual($f, '
                      content
                      ', 'HTML corrector -- unclosed tag with attribute.'); + + // XHTML slash for empty elements. + $f = Html::normalize('

                      '); + $this->assertEqual($f, '

                      ', 'HTML corrector -- XHTML closing slash.'); + + $f = Html::normalize('

                      test

                      '); + $this->assertEqual($f, '

                      test

                      ', 'HTML corrector -- Convert uppercased tags to proper lowercased ones.'); + + $f = Html::normalize('

                      test

                      '); + $this->assertEqual($f, '

                      test

                      ', 'HTML corrector -- Convert uppercased tags to proper lowercased ones.'); + + $f = Html::normalize('test
                      '); + $this->assertEqual($f, 'test
                      ', 'HTML corrector -- Let proper XHTML pass through.'); + + $f = Html::normalize('test
                      '); + $this->assertEqual($f, 'test
                      ', 'HTML corrector -- Let proper XHTML pass through, but ensure there is a single space before the closing slash.'); + + $f = Html::normalize('test
                      '); + $this->assertEqual($f, 'test
                      ', 'HTML corrector -- Let proper XHTML pass through, but ensure there are not too many spaces before the closing slash.'); + + $f = Html::normalize(''); + $this->assertEqual($f, '', 'HTML corrector -- Convert XHTML that is properly formed but that would not be compatible with typical HTML user agents.'); + + $f = Html::normalize('test1
                      test2'); + $this->assertEqual($f, 'test1
                      test2', 'HTML corrector -- Automatically close single tags.'); + + $f = Html::normalize('line1
                      line2'); + $this->assertEqual($f, 'line1
                      line2', 'HTML corrector -- Automatically close single tags.'); + + $f = Html::normalize('line1
                      line2'); + $this->assertEqual($f, 'line1
                      line2', 'HTML corrector -- Automatically close single tags.'); + + $f = Html::normalize('test'); + $this->assertEqual($f, 'test', 'HTML corrector -- Automatically close single tags.'); + + $f = Html::normalize('

                      '); + $this->assertEqual($f, '
                      ', "HTML corrector -- Transform empty tags to a single closed tag if the tag's content model is EMPTY."); + + $f = Html::normalize('
                      '); + $this->assertEqual($f, '
                      ', "HTML corrector -- Do not transform empty tags to a single closed tag if the tag's content model is not EMPTY."); + + $f = Html::normalize('

                      line1


                      line2

                      '); + $this->assertEqual($f, '

                      line1


                      line2', 'HTML corrector -- Move non-inline elements outside of inline containers.'); + + $f = Html::normalize('

                      line1

                      line2

                      '); + $this->assertEqual($f, '

                      line1

                      line2
                      ', 'HTML corrector -- Move non-inline elements outside of inline containers.'); + + $f = Html::normalize('

                      test

                      test

                      \n'); + $this->assertEqual($f, '

                      test

                      test

                      \n', 'HTML corrector -- Auto-close improperly nested tags.'); + + $f = Html::normalize('

                      Line1
                      bold stuff'); + $this->assertEqual($f, '

                      Line1
                      bold stuff

                      ', 'HTML corrector -- Properly close unclosed tags, and remove useless closing tags.'); + + $f = Html::normalize('test '); + $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.'); + + $f = Html::normalize('test '); + $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.'); + + $f = Html::normalize('test '); + $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.'); + + $f = Html::normalize('test '); + $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.'); + + $f = Html::normalize('test '); + $this->assertEqual($f, 'test ', 'HTML corrector -- Do not touch HTML comments.'); + + $f = Html::normalize('

                      test\n

                      \n'); + $this->assertEqual($f, '

                      test\n

                      \n', 'HTML corrector -- New-lines are accepted and kept as-is.'); + + $f = Html::normalize('

                      دروبال'); + $this->assertEqual($f, '

                      دروبال

                      ', 'HTML corrector -- Encoding is correctly kept.'); + + $f = Html::normalize(''); + $this->assertEqual($f, '', 'HTML corrector -- CDATA added to script element'); + + $f = Html::normalize('

                      '); + $this->assertEqual($f, '

                      ', 'HTML corrector -- CDATA added to a nested script element'); + + $f = Html::normalize('

                      '); + $this->assertEqual($f, '

                      ', 'HTML corrector -- CDATA added to a style element.'); + + $filtered_data = Html::normalize('

                      '); + $this->assertEqual($filtered_data, '

                      ', + format_string('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => '/*

                      '); + $this->assertEqual($filtered_data, '

                      ', + format_string('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => ' +

                      '); + $this->assertEqual($filtered_data, '

                      ', + format_string('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => ' +

                      ', + format_string('HTML corrector -- Existing cdata section @pattern_name properly escaped', array('@pattern_name' => '// assertTrue(strpos(strtolower(Html::decodeEntities($haystack)), $needle) !== FALSE, $message, $group); + } + + /** + * Asserts that text transformed to lowercase with HTML entities decoded does not contain a given string. + * + * Otherwise fails the test with a given message, similar to all the + * SimpleTest assert* functions. + * + * Note that this does not remove nulls, new lines, and other character that + * could be used to obscure a tag or an attribute name. + * + * @param string $haystack + * Text to look in. + * @param string $needle + * Lowercase, plain text to look for. + * @param string $message + * (optional) Message to display if failed. Defaults to an empty string. + * @param string $group + * (optional) The group this message belongs to. Defaults to 'Other'. + * + * @return bool + * TRUE on pass, FALSE on fail. + */ + function assertNoNormalized($haystack, $needle, $message = '', $group = 'Other') { + return $this->assertTrue(strpos(strtolower(Html::decodeEntities($haystack)), $needle) === FALSE, $message, $group); + } +} diff --git a/core/modules/filter/tests/src/Kernel/Migrate/d6/MigrateFilterFormatTest.php b/core/modules/filter/tests/src/Kernel/Migrate/d6/MigrateFilterFormatTest.php new file mode 100644 index 0000000..d4e5cd3 --- /dev/null +++ b/core/modules/filter/tests/src/Kernel/Migrate/d6/MigrateFilterFormatTest.php @@ -0,0 +1,57 @@ +executeMigration('d6_filter_format'); + } + + /** + * Tests the Drupal 6 filter format to Drupal 8 migration. + */ + public function testFilterFormat() { + $filter_format = FilterFormat::load('filtered_html'); + + // Check filter status. + $filters = $filter_format->get('filters'); + $this->assertTrue($filters['filter_autop']['status']); + $this->assertTrue($filters['filter_url']['status']); + $this->assertTrue($filters['filter_htmlcorrector']['status']); + $this->assertTrue($filters['filter_html']['status']); + + // These should be false by default. + $this->assertFalse(isset($filters['filter_html_escape'])); + $this->assertFalse(isset($filters['filter_caption'])); + $this->assertFalse(isset($filters['filter_html_image_secure'])); + + // Check variables migrated into filter. + $this->assertIdentical('
                        1. ', $filters['filter_html']['settings']['allowed_html']); + $this->assertIdentical(TRUE, $filters['filter_html']['settings']['filter_html_help']); + $this->assertIdentical(FALSE, $filters['filter_html']['settings']['filter_html_nofollow']); + $this->assertIdentical(72, $filters['filter_url']['settings']['filter_url_length']); + + // Check that the PHP code filter is converted to filter_null. + $filters = FilterFormat::load('php_code')->get('filters'); + $this->assertTrue(isset($filters['filter_null'])); + } + +} diff --git a/core/modules/filter/tests/src/Kernel/Migrate/d7/MigrateFilterFormatTest.php b/core/modules/filter/tests/src/Kernel/Migrate/d7/MigrateFilterFormatTest.php new file mode 100644 index 0000000..4bbacdb --- /dev/null +++ b/core/modules/filter/tests/src/Kernel/Migrate/d7/MigrateFilterFormatTest.php @@ -0,0 +1,81 @@ +installConfig(static::$modules); + $this->executeMigration('d7_filter_format'); + } + + /** + * Asserts various aspects of a filter format entity. + * + * @param string $id + * The format ID. + * @param string $label + * The expected label of the format. + * @param array $enabled_filters + * The expected filters in the format, keyed by ID. + */ + protected function assertEntity($id, $label, array $enabled_filters) { + /** @var \Drupal\filter\FilterFormatInterface $entity */ + $entity = FilterFormat::load($id); + $this->assertTrue($entity instanceof FilterFormatInterface); + $this->assertIdentical($label, $entity->label()); + // get('filters') will return enabled filters only, not all of them. + $this->assertIdentical($enabled_filters, array_keys($entity->get('filters'))); + } + + /** + * Tests the Drupal 7 filter format to Drupal 8 migration. + */ + public function testFilterFormat() { + $this->assertEntity('custom_text_format', 'Custom Text format', ['filter_autop', 'filter_html']); + $this->assertEntity('filtered_html', 'Filtered HTML', ['filter_autop', 'filter_html', 'filter_htmlcorrector', 'filter_url']); + $this->assertEntity('full_html', 'Full HTML', ['filter_autop', 'filter_htmlcorrector', 'filter_url']); + $this->assertEntity('plain_text', 'Plain text', ['filter_html_escape', 'filter_url', 'filter_autop']); + // This assertion covers issue #2555089. Drupal 7 formats are identified + // by machine names, so migrated formats should be merged into existing + // ones. + $this->assertNull(FilterFormat::load('plain_text1')); + + // Ensure that filter-specific settings were migrated. + /** @var \Drupal\filter\FilterFormatInterface $format */ + $format = FilterFormat::load('filtered_html'); + $config = $format->filters('filter_html')->getConfiguration(); + $this->assertIdentical('
                          '; - return $output; + return ['#markup' => $output]; } } /** + * Implements hook_theme(). + */ +function help_theme($existing, $type, $theme, $path) { + return [ + 'help_section' => [ + 'variables' => [ + 'title' => NULL, + 'description' => NULL, + 'links' => NULL, + 'empty' => NULL, + ], + ], + ]; +} + +/** * Implements hook_preprocess_HOOK() for block templates. */ function help_preprocess_block(&$variables) { diff --git a/core/modules/help/help.services.yml b/core/modules/help/help.services.yml new file mode 100644 index 0000000..26a2c33 --- /dev/null +++ b/core/modules/help/help.services.yml @@ -0,0 +1,4 @@ +services: + plugin.manager.help_section: + class: Drupal\help\HelpSectionManager + parent: default_plugin_manager diff --git a/core/modules/help/src/Annotation/HelpSection.php b/core/modules/help/src/Annotation/HelpSection.php new file mode 100644 index 0000000..d43156d --- /dev/null +++ b/core/modules/help/src/Annotation/HelpSection.php @@ -0,0 +1,65 @@ +routeMatch = $route_match; + $this->helpManager = $help_manager; } /** @@ -40,62 +51,57 @@ public function __construct(RouteMatchInterface $route_match) { */ public static function create(ContainerInterface $container) { return new static( - $container->get('current_route_match') + $container->get('current_route_match'), + $container->get('plugin.manager.help_section') ); } /** - * Prints a page listing a glossary of Drupal terminology. + * Prints a page listing various types of help. + * + * The page has sections defined by \Drupal\help\HelpSectionPluginInterface + * plugins. * - * @return string - * An HTML string representing the contents of help page. + * @return array + * A render array for the help page. */ public function helpMain() { - $output = array( - '#markup' => '

                          ' . $this->t('Help topics') . '

                          ' . $this->t('Help is available on the following items:') . '

                          ', - 'links' => $this->helpLinksAsList(), - ); - return $output; - } + $output = []; - /** - * Provides a formatted list of available help topics. - * - * @return string - * A string containing the formatted list. - */ - protected function helpLinksAsList() { - $modules = array(); - foreach ($this->moduleHandler()->getImplementations('help') as $module) { - $modules[$module] = $this->moduleHandler->getName($module); - } - asort($modules); - - // Output pretty four-column list. - $count = count($modules); - $break = ceil($count / 4); - $column = array( - '#type' => 'container', - 'links' => array('#theme' => 'item_list'), - '#attributes' => array('class' => array('layout-column', 'layout-column--quarter')), - ); - $output = array( - '#prefix' => '
                          ', - '#suffix' => '
                          ', - 0 => $column, - ); + // We are checking permissions, so add the user.permissions cache context. + $cacheability = new CacheableMetadata(); + $cacheability->addCacheContexts(['user.permissions']); + + $plugins = $this->helpManager->getDefinitions(); + $cacheability->addCacheableDependency($this->helpManager); - $i = 0; - $current_column = 0; - foreach ($modules as $module => $name) { - $output[$current_column]['links']['#items'][] = $this->l($name, new Url('help.page', array('name' => $module))); - if (($i + 1) % $break == 0 && ($i + 1) != $count) { - $current_column++; - $output[$current_column] = $column; + foreach ($plugins as $plugin_id => $plugin_definition) { + // Check the provided permission. + if (!empty($plugin_definition['permission']) && !$this->currentuser()->hasPermission($plugin_definition['permission'])) { + continue; } - $i++; + + // Add the section to the page. + /** @var \Drupal\help\HelpSectionPluginInterface $plugin */ + $plugin = $this->helpManager->createInstance($plugin_id); + $this_output = [ + '#theme' => 'help_section', + '#title' => $plugin->getTitle(), + '#description' => $plugin->getDescription(), + '#empty' => $this->t('There is currently nothing in this section.'), + '#links' => [], + ]; + + $links = $plugin->listTopics(); + if (is_array($links) && count($links)) { + $this_output['#links'] = $links; + } + + $cacheability->addCacheableDependency($plugin); + $output[$plugin_id] = $this_output; } + $cacheability->applyTo($output); return $output; } @@ -116,12 +122,20 @@ public function helpPage($name) { $module_name = $this->moduleHandler()->getName($name); $build['#title'] = $module_name; + $info = system_get_info('module', $name); + if ($info['package'] === 'Core (Experimental)') { + drupal_set_message($this->t('This module is experimental. Experimental modules are provided for testing purposes only. Use at your own risk.', [':url' => 'https://www.drupal.org/core/experimental']), 'warning'); + } + $temp = $this->moduleHandler()->invoke($name, 'help', array("help.page.$name", $this->routeMatch)); if (empty($temp)) { - $build['top']['#markup'] = $this->t('No help is available for module %module.', array('%module' => $module_name)); + $build['top'] = ['#markup' => $this->t('No help is available for module %module.', array('%module' => $module_name))]; } else { - $build['top']['#markup'] = $temp; + if (!is_array($temp)) { + $temp = ['#markup' => $temp]; + } + $build['top'] = $temp; } // Only print list of administration pages if the module in question has diff --git a/core/modules/help/src/HelpSectionManager.php b/core/modules/help/src/HelpSectionManager.php new file mode 100644 index 0000000..45dfa3c --- /dev/null +++ b/core/modules/help/src/HelpSectionManager.php @@ -0,0 +1,42 @@ +alterInfo('help_section_info'); + $this->setCacheBackend($cache_backend, 'help_section_plugins'); + } + +} diff --git a/core/modules/help/src/HelpSectionPluginInterface.php b/core/modules/help/src/HelpSectionPluginInterface.php new file mode 100644 index 0000000..49bc463 --- /dev/null +++ b/core/modules/help/src/HelpSectionPluginInterface.php @@ -0,0 +1,54 @@ +moduleHandler->invokeAll('help', array($this->routeMatch->getRouteName(), $this->routeMatch)); - return $help ? implode("\n", $help) : ''; + $build = []; + foreach ($help as $item) { + if (!is_array($item)) { + $item = ['#markup' => $item]; + } + $build[] = $item; + } + + return $build; } /** @@ -111,10 +119,11 @@ public function build() { if (!$help) { return []; } + elseif (!is_array($help)) { + return ['#markup' => $help]; + } else { - return [ - '#children' => $help, - ]; + return $help; } } @@ -122,9 +131,7 @@ public function build() { * {@inheritdoc} */ public function getCacheContexts() { - // The "Help" block must be cached per URL: help is defined for a - // given path, and does not come with any access restrictions. - return Cache::mergeContexts(parent::getCacheContexts(), ['url']); + return Cache::mergeContexts(parent::getCacheContexts(), ['route']); } } diff --git a/core/modules/help/src/Plugin/HelpSection/HelpSectionPluginBase.php b/core/modules/help/src/Plugin/HelpSection/HelpSectionPluginBase.php new file mode 100644 index 0000000..c593473 --- /dev/null +++ b/core/modules/help/src/Plugin/HelpSection/HelpSectionPluginBase.php @@ -0,0 +1,39 @@ +getPluginDefinition()['title']; + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return $this->getPluginDefinition()['description']; + } + +} diff --git a/core/modules/help/src/Plugin/HelpSection/HookHelpSection.php b/core/modules/help/src/Plugin/HelpSection/HookHelpSection.php new file mode 100644 index 0000000..e4cf4eb --- /dev/null +++ b/core/modules/help/src/Plugin/HelpSection/HookHelpSection.php @@ -0,0 +1,77 @@ +moduleHandler = $module_handler; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('module_handler') + ); + } + + /** + * {@inheritdoc} + */ + public function listTopics() { + $topics = []; + foreach ($this->moduleHandler->getImplementations('help') as $module) { + $title = $this->moduleHandler->getName($module); + $topics[$title] = Link::createFromRoute($title, 'help.page', ['name' => $module]); + } + + // Sort topics by title, which is the array key above. + ksort($topics); + return $topics; + } + +} diff --git a/core/modules/help/src/Tests/ExperimentalHelpTest.php b/core/modules/help/src/Tests/ExperimentalHelpTest.php new file mode 100644 index 0000000..169469f --- /dev/null +++ b/core/modules/help/src/Tests/ExperimentalHelpTest.php @@ -0,0 +1,61 @@ +adminUser = $this->drupalCreateUser(['access administration pages']); + } + + /** + * Verifies that a warning message is displayed for experimental modules. + */ + public function testExperimentalHelp() { + $this->drupalLogin($this->adminUser); + $this->drupalGet('admin/help/experimental_module_test'); + $this->assertText('This module is experimental.'); + + // Regular modules should not display the message. + $this->drupalGet('admin/help/help_page_test'); + $this->assertNoText('This module is experimental.'); + + // Ensure the actual help page is displayed to avoid a false positive. + $this->assertResponse(200); + $this->assertText('online documentation for the Help Page Test module'); + } + +} diff --git a/core/modules/help/src/Tests/HelpEmptyPageTest.php b/core/modules/help/src/Tests/HelpEmptyPageTest.php deleted file mode 100644 index 5fd1339..0000000 --- a/core/modules/help/src/Tests/HelpEmptyPageTest.php +++ /dev/null @@ -1,65 +0,0 @@ -installSchema('system', 'router'); - } - - /** - * {@inheritdoc} - */ - public function containerBuild(ContainerBuilder $container) { - parent::containerBuild($container); - - $container->set('url_generator', new SupernovaGenerator()); - } - - /** - * Ensures that no URL generator is called on a page without hook_help(). - */ - public function testEmptyHookHelp() { - $all_modules = system_rebuild_module_data(); - $all_modules = array_filter($all_modules, function ($module) { - // Filter contrib, hidden, already enabled modules and modules in the - // Testing package. - if ($module->origin !== 'core' || !empty($module->info['hidden']) || $module->status == TRUE || $module->info['package'] == 'Testing') { - return FALSE; - } - return TRUE; - }); - - \Drupal::service('module_installer')->install(array_keys($all_modules)); - - $route = \Drupal::service('router.route_provider')->getRouteByName(''); - \Drupal::service('module_handler')->invokeAll('help', ['', new RouteMatch('', $route)]); - } - -} diff --git a/core/modules/help/src/Tests/HelpTest.php b/core/modules/help/src/Tests/HelpTest.php index 64ad4a7..4f90cb7 100644 --- a/core/modules/help/src/Tests/HelpTest.php +++ b/core/modules/help/src/Tests/HelpTest.php @@ -20,11 +20,12 @@ class HelpTest extends WebTestBase { * Modules to enable. * * The help_test module implements hook_help() but does not provide a module - * overview page. + * overview page. The help_page_test module has a page section plugin that + * returns no links. * * @var array. */ - public static $modules = array('help_test'); + public static $modules = array('help_test', 'help_page_test'); /** * Use the Standard profile to test help implementations of many core modules. @@ -52,7 +53,7 @@ protected function setUp() { } /** - * Logs in users, creates dblog events, and tests dblog functionality. + * Logs in users, tests help pages. */ public function testHelp() { // Login the root user to ensure as many admin links appear as possible on @@ -67,10 +68,16 @@ public function testHelp() { // Verify that introductory help text exists, goes for 100% module coverage. $this->drupalLogin($this->adminUser); $this->drupalGet('admin/help'); - $this->assertRaw(t('For more information, refer to the subjects listed in the Help Topics section or to the online documentation and support pages at drupal.org.', array(':docs' => 'https://www.drupal.org/documentation', ':support' => 'https://www.drupal.org/support', ':drupal' => 'https://www.drupal.org')), 'Help intro text correctly appears.'); + $this->assertRaw(t('For more information, refer to the help listed on this page or to the online documentation and support pages at drupal.org.', array(':docs' => 'https://www.drupal.org/documentation', ':support' => 'https://www.drupal.org/support', ':drupal' => 'https://www.drupal.org'))); - // Verify that help topics text appears. - $this->assertRaw('

                          ' . t('Help topics') . '

                          ' . t('Help is available on the following items:') . '

                          ', 'Help topics text correctly appears.'); + // Verify that hook_help() section title and description appear. + $this->assertRaw('

                          ' . t('Module overviews') . '

                          '); + $this->assertRaw('

                          ' . t('Module overviews are provided by modules. Overviews available for your installed modules:'), '

                          '); + + // Verify that an empty section is handled correctly. + $this->assertRaw('

                          ' . t('Empty section') . '

                          '); + $this->assertRaw('

                          ' . t('This description should appear.'), '

                          '); + $this->assertText(t('There is currently nothing in this section.')); // Make sure links are properly added for modules implementing hook_help(). foreach ($this->getModuleList() as $module => $name) { @@ -81,10 +88,25 @@ public function testHelp() { // handled correctly. $this->clickLink(\Drupal::moduleHandler()->getName('help_test')); $this->assertRaw(t('No help is available for module %module.', array('%module' => \Drupal::moduleHandler()->getName('help_test')))); + + // Verify that the order of topics is alphabetical by displayed module + // name, by checking the order of some modules, including some that would + // have a different order if it was done by machine name instead. + $this->drupalGet('admin/help'); + $page_text = $this->getTextContent(); + $start = strpos($page_text, 'Module overviews'); + $pos = $start; + $list = ['Block', 'Color', 'Custom Block', 'History', 'Text Editor']; + foreach ($list as $name) { + $this->assertLink($name); + $new_pos = strpos($page_text, $name, $start); + $this->assertTrue($new_pos > $pos, 'Order of ' . $name . ' is correct on page'); + $pos = $new_pos; + } } /** - * Verifies the logged in user has access to the various help nodes. + * Verifies the logged in user has access to the various help pages. * * @param int $response * (optional) An HTTP response code. Defaults to 200. @@ -100,7 +122,7 @@ protected function verifyHelp($response = 200) { } foreach ($this->getModuleList() as $module => $name) { - // View module help node. + // View module help page. $this->drupalGet('admin/help/' . $module); $this->assertResponse($response); if ($response == 200) { diff --git a/core/modules/help/src/Tests/NoHelpTest.php b/core/modules/help/src/Tests/NoHelpTest.php index f0213a9..cce40b2 100644 --- a/core/modules/help/src/Tests/NoHelpTest.php +++ b/core/modules/help/src/Tests/NoHelpTest.php @@ -43,7 +43,7 @@ public function testMainPageNoHelp() { $this->drupalGet('admin/help'); $this->assertResponse(200); - $this->assertText('Help is available on the following items', 'Help page is found.'); + $this->assertText('Module overviews are provided by modules'); $this->assertFalse(\Drupal::moduleHandler()->implementsHook('menu_test', 'help'), 'The menu_test module does not implement hook_help'); $this->assertNoText(\Drupal::moduleHandler()->getName('menu_test'), 'Making sure the test module menu_test does not display a help link on admin/help.'); diff --git a/core/modules/help/templates/help-section.html.twig b/core/modules/help/templates/help-section.html.twig new file mode 100644 index 0000000..9d63b4b --- /dev/null +++ b/core/modules/help/templates/help-section.html.twig @@ -0,0 +1,25 @@ +{# +/** + * @file + * Default theme implementation for a section of the help page. + * + * Available variables: + * - title: The section title. + * - description: The description text for the section. + * - links: Links to display in the section. + * - empty: Text to display if there are no links. + * + * @ingroup themeable + */ +#} +

                          {{ title }}

                          +

                          {{ description }}

                          +{% if links %} +
                            + {% for link in links %} +
                          • {{ link }}
                          • + {% endfor %} +
                          +{% else %} +

                          {{ empty }}

                          +{% endif %} diff --git a/core/modules/help/tests/modules/help_page_test/help_page_test.info.yml b/core/modules/help/tests/modules/help_page_test/help_page_test.info.yml new file mode 100644 index 0000000..7de2a0d --- /dev/null +++ b/core/modules/help/tests/modules/help_page_test/help_page_test.info.yml @@ -0,0 +1,7 @@ +name: 'Help Page Test' +type: module +description: 'Module to test the help page.' +package: Testing +version: VERSION +core: 8.x +hidden: true diff --git a/core/modules/help/tests/modules/help_page_test/help_page_test.module b/core/modules/help/tests/modules/help_page_test/help_page_test.module new file mode 100644 index 0000000..68639a8 --- /dev/null +++ b/core/modules/help/tests/modules/help_page_test/help_page_test.module @@ -0,0 +1,22 @@ +online documentation for the Help Page Test module.', [':url' => 'http://www.example.com']); + } + +} diff --git a/core/modules/help/tests/modules/help_page_test/src/Plugin/HelpSection/EmptyHelpSection.php b/core/modules/help/tests/modules/help_page_test/src/Plugin/HelpSection/EmptyHelpSection.php new file mode 100644 index 0000000..f7adac7 --- /dev/null +++ b/core/modules/help/tests/modules/help_page_test/src/Plugin/HelpSection/EmptyHelpSection.php @@ -0,0 +1,29 @@ +set('url_generator', new SupernovaGenerator()); + } + + /** + * Ensures that no URL generator is called on a page without hook_help(). + */ + public function testEmptyHookHelp() { + $all_modules = system_rebuild_module_data(); + $all_modules = array_filter($all_modules, function ($module) { + // Filter contrib, hidden, already enabled modules and modules in the + // Testing package. + if ($module->origin !== 'core' || !empty($module->info['hidden']) || $module->status == TRUE || $module->info['package'] == 'Testing') { + return FALSE; + } + return TRUE; + }); + + $this->enableModules(array_keys($all_modules)); + $this->installEntitySchema('menu_link_content'); + + $route = \Drupal::service('router.route_provider')->getRouteByName(''); + \Drupal::service('module_handler')->invokeAll('help', ['', new RouteMatch('', $route)]); + } + +} diff --git a/core/modules/history/history.install b/core/modules/history/history.install index dcfd871..ede5255 100644 --- a/core/modules/history/history.install +++ b/core/modules/history/history.install @@ -5,6 +5,8 @@ * Installation functions for History module. */ +use Drupal\Core\Database\Database; + /** * Implements hook_schema(). */ @@ -21,6 +23,7 @@ function history_schema() { 'nid' => array( 'description' => 'The {node}.nid that was read.', 'type' => 'int', + 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), @@ -39,3 +42,59 @@ function history_schema() { return $schema; } + +/** + * @defgroup updates-8.0.x-to-8.1.x Updates from 8.0.x to 8.1.x + * @{ + * Update functions from 8.0.x to 8.1.x. + */ + +/** + * Change {history}.nid to an unsigned int in order to match {node}.nid. + */ +function history_update_8101() { + $schema = Database::getConnection()->schema(); + $schema->dropPrimaryKey('history'); + $schema->dropIndex('history', 'nid'); + $schema->changeField('history', 'nid', 'nid', array( + 'description' => 'The {node}.nid that was read.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + )); + $schema->addPrimaryKey('history', array('uid', 'nid')); + $spec = array( + 'description' => 'A record of which {users} have read which {node}s.', + 'fields' => array( + 'uid' => array( + 'description' => 'The {users}.uid that read the {node} nid.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'nid' => array( + 'description' => 'The {node}.nid that was read.', + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0, + ), + 'timestamp' => array( + 'description' => 'The Unix timestamp at which the read occurred.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('uid', 'nid'), + 'indexes' => array( + 'nid' => array('nid'), + ), + ); + $schema->addIndex('history', 'nid', array('nid'), $spec); +} + +/** + * @} End of "defgroup updates-8.0.x-to-8.1.x". + */ diff --git a/core/modules/image/config/schema/image.source.schema.yml b/core/modules/image/config/schema/image.source.schema.yml deleted file mode 100644 index 1230023..0000000 --- a/core/modules/image/config/schema/image.source.schema.yml +++ /dev/null @@ -1,3 +0,0 @@ -migrate.source.d6_imagecache_presets: - type: migrate_source_sql - label: 'Drupal 6 ImageCache Presets' diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 97b3dd2..7f8314a 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -14,26 +14,36 @@ /** * Image style constant for user presets in the database. + * + * @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0. */ const IMAGE_STORAGE_NORMAL = 1; /** * Image style constant for user presets that override module-defined presets. + * + * @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0. */ const IMAGE_STORAGE_OVERRIDE = 2; /** * Image style constant for module-defined presets in code. + * + * @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0. */ const IMAGE_STORAGE_DEFAULT = 4; /** * Image style constant to represent an editable preset. + * + * @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0. */ define('IMAGE_STORAGE_EDITABLE', IMAGE_STORAGE_NORMAL | IMAGE_STORAGE_OVERRIDE); /** * Image style constant to represent any module-based preset. + * + * @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.0. */ define('IMAGE_STORAGE_MODULE', IMAGE_STORAGE_OVERRIDE | IMAGE_STORAGE_DEFAULT); @@ -66,7 +76,7 @@ function image_help($route_name, RouteMatchInterface $route_match) { $output .= '
                          ' . t('For accessibility and search engine optimization, all images that convey meaning on web sites should have alternate text. Drupal also allows entry of title text for images, but it can lead to confusion for screen reader users and its use is not recommended. Image fields can be configured so that alternate and title text fields are enabled or disabled; if enabled, the fields can be set to be required. The recommended setting is to enable and require alternate text and disable title text.') . '
                          '; $output .= '
                          ' . t('When you create an image field, you will need to choose whether the uploaded images will be stored in the public or private file directory defined in your settings.php file and shown on the File system page. This choice cannot be changed later. You can also configure your field to store files in a subdirectory of the public or private directory; this setting can be changed later and can be different for each entity sub-type using the field. For more information on file storage, see the System module help page.', array(':file-system' => \Drupal::url('system.file_system_settings'), ':system-help' => \Drupal::url('help.page', array('name' => 'system')))) . '
                          '; $output .= '
                          ' . t('The maximum file size that can be uploaded is limited by PHP settings of the server, but you can restrict it further by configuring a Maximum upload size in the field settings (this setting can be changed later). The maximum file size, either from PHP server settings or field configuration, is automatically displayed to users in the help text of the image field.') . '
                          '; - $output .= '
                          ' . t('You can also configure a minimum and/or maximum resolution for uploaded images. Images that are too small will be rejected. Images that are to large will be resized. During the resizing the EXIF data in the image will be lost.', array(':exif' => 'http://en.wikipedia.org/wiki/Exchangeable_image_file_format')) . '
                          '; + $output .= '
                          ' . t('You can also configure a minimum and/or maximum resolution for uploaded images. Images that are too small will be rejected. Images that are to large will be resized. During the resizing the EXIF data in the image will be lost.') . '
                          '; $output .= '
                          ' . t('You can also configure a default image that will be used if no image is uploaded in an image field. This default can be defined for all instances of the field in the field storage settings when you create a field, and the setting can be overridden for each entity sub-type that uses the field.') . '
                          '; $output .= '
                          ' . t('Configuring displays and form displays') . '
                          '; $output .= '
                          ' . t('On the Manage display page, you can choose the image formatter, which determines the image style used to display the image in each display mode and whether or not to display the image as a link. On the Manage form display page, you can configure the image upload widget, including setting the preview image style shown on the entity edit form.') . '
                          '; diff --git a/core/modules/image/src/Controller/ImageStyleDownloadController.php b/core/modules/image/src/Controller/ImageStyleDownloadController.php index 200a425..eda399a 100644 --- a/core/modules/image/src/Controller/ImageStyleDownloadController.php +++ b/core/modules/image/src/Controller/ImageStyleDownloadController.php @@ -12,7 +12,6 @@ use Drupal\Core\Lock\LockBackendInterface; use Drupal\image\ImageStyleInterface; use Drupal\system\FileDownloadController; -use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\Request; @@ -53,13 +52,11 @@ class ImageStyleDownloadController extends FileDownloadController { * The lock backend. * @param \Drupal\Core\Image\ImageFactory $image_factory * The image factory. - * @param \Psr\Log\LoggerInterface $logger - * A logger instance. */ - public function __construct(LockBackendInterface $lock, ImageFactory $image_factory, LoggerInterface $logger) { + public function __construct(LockBackendInterface $lock, ImageFactory $image_factory) { $this->lock = $lock; $this->imageFactory = $image_factory; - $this->logger = $logger; + $this->logger = $this->getLogger('image'); } /** @@ -68,8 +65,7 @@ public function __construct(LockBackendInterface $lock, ImageFactory $image_fact public static function create(ContainerInterface $container) { return new static( $container->get('lock'), - $container->get('image.factory'), - $container->get('logger.factory')->get('image') + $container->get('image.factory') ); } diff --git a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php index b616b17..a867b9e 100644 --- a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php +++ b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php @@ -213,7 +213,7 @@ public function fieldSettingsForm(array $form, FormStateInterface $form_state) { '#weight' => 4.1, '#field_prefix' => '
                          ', '#field_suffix' => '
                          ', - '#description' => t('The maximum allowed image size expressed as WIDTH×HEIGHT (e.g. 640×480). Leave blank for no restriction. If a larger image is uploaded, it will be resized to reflect the given width and height. Resizing images on upload will cause the loss of EXIF data in the image.', array(':url' => 'http://en.wikipedia.org/wiki/Exchangeable_image_file_format')), + '#description' => t('The maximum allowed image size expressed as WIDTH×HEIGHT (e.g. 640×480). Leave blank for no restriction. If a larger image is uploaded, it will be resized to reflect the given width and height. Resizing images on upload will cause the loss of EXIF data in the image.'), ); $element['max_resolution']['x'] = array( '#type' => 'number', diff --git a/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php b/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php index 10ebfb5..cf2b34e 100644 --- a/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php +++ b/core/modules/image/src/Plugin/Field/FieldWidget/ImageWidget.php @@ -98,7 +98,7 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f if ($cardinality == 1) { // If there's only one field, return it as delta 0. if (empty($elements[0]['#default_value']['fids'])) { - $file_upload_help['#description'] = $this->fieldDefinition->getDescription(); + $file_upload_help['#description'] = $this->getFilteredDescription(); $elements[0]['#description'] = \Drupal::service('renderer')->renderPlain($file_upload_help); } } diff --git a/core/modules/image/src/Plugin/ImageEffect/RotateImageEffect.php b/core/modules/image/src/Plugin/ImageEffect/RotateImageEffect.php index 2507a8c..3b79e43 100644 --- a/core/modules/image/src/Plugin/ImageEffect/RotateImageEffect.php +++ b/core/modules/image/src/Plugin/ImageEffect/RotateImageEffect.php @@ -8,6 +8,7 @@ namespace Drupal\image\Plugin\ImageEffect; use Drupal\Component\Utility\Color; +use Drupal\Component\Utility\Rectangle; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Image\ImageInterface; use Drupal\image\ConfigurableImageEffectBase; @@ -43,14 +44,13 @@ public function applyEffect(ImageInterface $image) { * {@inheritdoc} */ public function transformDimensions(array &$dimensions, $uri) { - // If the rotate is not random and the angle is a multiple of 90 degrees, + // If the rotate is not random and current dimensions are set, // then the new dimensions can be determined. - if (!$this->configuration['random'] && ((int) ($this->configuration['degrees']) == $this->configuration['degrees']) && ($this->configuration['degrees'] % 90 == 0)) { - if ($this->configuration['degrees'] % 180 != 0) { - $temp = $dimensions['width']; - $dimensions['width'] = $dimensions['height']; - $dimensions['height'] = $temp; - } + if (!$this->configuration['random'] && $dimensions['width'] && $dimensions['height']) { + $rect = new Rectangle($dimensions['width'], $dimensions['height']); + $rect = $rect->rotate($this->configuration['degrees']); + $dimensions['width'] = $rect->getBoundingWidth(); + $dimensions['height'] = $rect->getBoundingHeight(); } else { $dimensions['width'] = $dimensions['height'] = NULL; diff --git a/core/modules/image/src/Tests/FileMoveTest.php b/core/modules/image/src/Tests/FileMoveTest.php index 34c54a7..4c11725 100644 --- a/core/modules/image/src/Tests/FileMoveTest.php +++ b/core/modules/image/src/Tests/FileMoveTest.php @@ -7,6 +7,7 @@ namespace Drupal\image\Tests; +use Drupal\file\Entity\File; use Drupal\simpletest\WebTestBase; use Drupal\image\Entity\ImageStyle; @@ -29,7 +30,7 @@ class FileMoveTest extends WebTestBase { */ function testNormal() { // Pick a file for testing. - $file = entity_create('file', (array) current($this->drupalGetTestFiles('image'))); + $file = File::create((array) current($this->drupalGetTestFiles('image'))); // Create derivative image. $styles = ImageStyle::loadMultiple(); diff --git a/core/modules/image/src/Tests/ImageAdminStylesTest.php b/core/modules/image/src/Tests/ImageAdminStylesTest.php index 56d80fe..1538769 100644 --- a/core/modules/image/src/Tests/ImageAdminStylesTest.php +++ b/core/modules/image/src/Tests/ImageAdminStylesTest.php @@ -339,7 +339,7 @@ function testStyleReplacement() { // Create a new style. $style_name = strtolower($this->randomMachineName(10)); $style_label = $this->randomString(); - $style = entity_create('image_style', array('name' => $style_name, 'label' => $style_label)); + $style = ImageStyle::create(array('name' => $style_name, 'label' => $style_label)); $style->save(); $style_path = 'admin/config/media/image-styles/manage/'; @@ -444,7 +444,7 @@ public function testFlushUserInterface() { // Create a new style. $style_name = strtolower($this->randomMachineName(10)); - $style = entity_create('image_style', array('name' => $style_name, 'label' => $this->randomString())); + $style = ImageStyle::create(array('name' => $style_name, 'label' => $this->randomString())); $style->save(); // Create an image to make sure it gets flushed. @@ -474,7 +474,7 @@ function testConfigImport() { // Create a new style. $style_name = strtolower($this->randomMachineName(10)); $style_label = $this->randomString(); - $style = entity_create('image_style', array('name' => $style_name, 'label' => $style_label)); + $style = ImageStyle::create(array('name' => $style_name, 'label' => $style_label)); $style->save(); // Create an image field that uses the new style. @@ -520,7 +520,7 @@ function testConfigImport() { * Tests access for the image style listing. */ public function testImageStyleAccess() { - $style = entity_create('image_style', array('name' => 'style_foo', 'label' => $this->randomString())); + $style = ImageStyle::create(array('name' => 'style_foo', 'label' => $this->randomString())); $style->save(); $this->drupalGet('admin/config/media/image-styles'); diff --git a/core/modules/image/src/Tests/ImageDimensionsTest.php b/core/modules/image/src/Tests/ImageDimensionsTest.php index 984e060..f232d71 100644 --- a/core/modules/image/src/Tests/ImageDimensionsTest.php +++ b/core/modules/image/src/Tests/ImageDimensionsTest.php @@ -38,7 +38,7 @@ function testImageDimensions() { // Create a style. /** @var $style \Drupal\image\ImageStyleInterface */ - $style = entity_create('image_style', array('name' => 'test', 'label' => 'Test')); + $style = ImageStyle::create(array('name' => 'test', 'label' => 'Test')); $style->save(); $generated_uri = 'public://styles/test/public/'. \Drupal::service('file_system')->basename($original_uri); $url = file_url_transform_relative($style->buildUrl($original_uri)); @@ -213,11 +213,14 @@ function testImageDimensions() { $effect_id = $style->addImageEffect($effect); $style->save(); - $this->assertEqual($this->getImageTag($variables), ''); + $this->assertEqual($this->getImageTag($variables), ''); $this->assertFalse(file_exists($generated_uri), 'Generated file does not exist.'); $this->drupalGet($this->getAbsoluteUrl($url)); $this->assertResponse(200, 'Image was generated at the URL.'); $this->assertTrue(file_exists($generated_uri), 'Generated file does exist after we accessed it.'); + $image_file = $image_factory->get($generated_uri); + $this->assertEqual($image_file->getWidth(), 41); + $this->assertEqual($image_file->getHeight(), 41); $effect_plugin = $style->getEffect($effect_id); $style->deleteImageEffect($effect_plugin); diff --git a/core/modules/image/src/Tests/ImageFieldDefaultImagesTest.php b/core/modules/image/src/Tests/ImageFieldDefaultImagesTest.php index ee47e30..627bd74 100644 --- a/core/modules/image/src/Tests/ImageFieldDefaultImagesTest.php +++ b/core/modules/image/src/Tests/ImageFieldDefaultImagesTest.php @@ -8,7 +8,9 @@ namespace Drupal\image\Tests; use Drupal\Component\Utility\Unicode; use Drupal\Core\Entity\Entity\EntityViewDisplay; +use Drupal\field\Entity\FieldConfig; use Drupal\file\Entity\File; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests setting up default images both to the field and field field. @@ -36,12 +38,12 @@ public function testDefaultImages() { $filename = $this->randomMachineName() . "$i"; $desired_filepath = 'public://' . $filename; file_unmanaged_copy($files[0]->uri, $desired_filepath, FILE_EXISTS_ERROR); - $file = entity_create('file', array('uri' => $desired_filepath, 'filename' => $filename, 'name' => $filename)); + $file = File::create(['uri' => $desired_filepath, 'filename' => $filename, 'name' => $filename]); $file->save(); } $default_images = array(); foreach (array('field', 'field', 'field2', 'field_new', 'field_new') as $image_target) { - $file = entity_create('file', (array) array_pop($files)); + $file = File::create((array) array_pop($files)); $file->save(); $default_images[$image_target] = $file; } @@ -82,7 +84,7 @@ public function testDefaultImages() { $this->assertEqual($field_storage->getSettings()['default_image']['uuid'], $default_images['field']->uuid()); // Add another field with another default image to the page content type. - $field2 = entity_create('field_config', array( + $field2 = FieldConfig::create([ 'field_storage' => $field_storage, 'bundle' => 'page', 'label' => $field->label(), @@ -96,7 +98,7 @@ public function testDefaultImages() { 'height' => 0, ), ), - )); + ]); $field2->save(); $widget_settings = entity_get_form_display('node', $field->getTargetBundle(), 'default')->getComponent($field_name); @@ -319,7 +321,7 @@ public function testDefaultImages() { * Tests image field and field having an invalid default image. */ public function testInvalidDefaultImage() { - $field_storage = entity_create('field_storage_config', array( + $field_storage = FieldStorageConfig::create(array( 'field_name' => Unicode::strtolower($this->randomMachineName()), 'entity_type' => 'node', 'type' => 'image', @@ -334,7 +336,7 @@ public function testInvalidDefaultImage() { // The non-existent default image should not be saved. $this->assertNull($settings['default_image']['uuid']); - $field = entity_create('field_config', array( + $field = FieldConfig::create([ 'field_storage' => $field_storage, 'bundle' => 'page', 'label' => $this->randomMachineName(), @@ -343,7 +345,7 @@ public function testInvalidDefaultImage() { 'uuid' => 100000, ) ), - )); + ]); $field->save(); $settings = $field->getSettings(); // The non-existent default image should not be saved. diff --git a/core/modules/image/src/Tests/ImageFieldTestBase.php b/core/modules/image/src/Tests/ImageFieldTestBase.php index bb59f83..79148a3 100644 --- a/core/modules/image/src/Tests/ImageFieldTestBase.php +++ b/core/modules/image/src/Tests/ImageFieldTestBase.php @@ -7,7 +7,9 @@ namespace Drupal\image\Tests; +use Drupal\field\Entity\FieldConfig; use Drupal\simpletest\WebTestBase; +use Drupal\field\Entity\FieldStorageConfig; /** * TODO: Test the following functions. @@ -69,9 +71,11 @@ protected function setUp() { * Widget settings to be added to the widget defaults. * @param array $formatter_settings * Formatter settings to be added to the formatter defaults. + * @param string $description + * A description for the field. */ - function createImageField($name, $type_name, $storage_settings = array(), $field_settings = array(), $widget_settings = array(), $formatter_settings = array()) { - entity_create('field_storage_config', array( + function createImageField($name, $type_name, $storage_settings = array(), $field_settings = array(), $widget_settings = array(), $formatter_settings = array(), $description = '') { + FieldStorageConfig::create(array( 'field_name' => $name, 'entity_type' => 'node', 'type' => 'image', @@ -79,14 +83,15 @@ function createImageField($name, $type_name, $storage_settings = array(), $field 'cardinality' => !empty($storage_settings['cardinality']) ? $storage_settings['cardinality'] : 1, ))->save(); - $field_config = entity_create('field_config', array( + $field_config = FieldConfig::create([ 'field_name' => $name, 'label' => $name, 'entity_type' => 'node', 'bundle' => $type_name, 'required' => !empty($field_settings['required']), 'settings' => $field_settings, - )); + 'description' => $description, + ]); $field_config->save(); entity_get_form_display('node', $type_name, 'default') diff --git a/core/modules/image/src/Tests/ImageFieldWidgetTest.php b/core/modules/image/src/Tests/ImageFieldWidgetTest.php index 8bc70bc..4a80c9a 100644 --- a/core/modules/image/src/Tests/ImageFieldWidgetTest.php +++ b/core/modules/image/src/Tests/ImageFieldWidgetTest.php @@ -27,9 +27,10 @@ public function testWidgetElement() { 'min_resolution' => $min_resolution . 'x' . $min_resolution, 'alt_field' => 0, ); - $this->createImageField($field_name, 'article', array(), $field_settings); + $this->createImageField($field_name, 'article', array(), $field_settings, array(), array(), 'Image test on [site:name]'); $this->drupalGet('node/add/article'); $this->assertNotEqual(0, count($this->xpath('//div[contains(@class, "field--widget-image-image")]')), 'Image field widget found on add/node page', 'Browser'); + $this->assertNoText('Image test on [site:name]'); } } diff --git a/core/modules/image/src/Tests/ImageImportTest.php b/core/modules/image/src/Tests/ImageImportTest.php deleted file mode 100644 index aa73086..0000000 --- a/core/modules/image/src/Tests/ImageImportTest.php +++ /dev/null @@ -1,51 +0,0 @@ - 'test' - ]); - - $style->addImageEffect(['id' => 'image_module_test_null']); - $style->addImageEffect(['id' => 'image_module_test_null']); - $style->save(); - - $this->assertEqual(count($style->getEffects()), 2); - - $uuid = \Drupal::service('uuid')->generate(); - $style->set('effects', [ - $uuid => [ - 'id' => 'image_module_test_null', - ], - ]); - $style->save(); - - $style = ImageStyle::load('test'); - $this->assertEqual(count($style->getEffects()), 1); - } - -} diff --git a/core/modules/image/src/Tests/ImageItemTest.php b/core/modules/image/src/Tests/ImageItemTest.php deleted file mode 100644 index fddbd3f..0000000 --- a/core/modules/image/src/Tests/ImageItemTest.php +++ /dev/null @@ -1,129 +0,0 @@ -installEntitySchema('file'); - $this->installSchema('file', array('file_usage')); - - entity_create('field_storage_config', array( - 'entity_type' => 'entity_test', - 'field_name' => 'image_test', - 'type' => 'image', - 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, - ))->save(); - entity_create('field_config', array( - 'entity_type' => 'entity_test', - 'field_name' => 'image_test', - 'bundle' => 'entity_test', - ))->save(); - file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example.jpg'); - $this->image = entity_create('file', array( - 'uri' => 'public://example.jpg', - )); - $this->image->save(); - $this->imageFactory = $this->container->get('image.factory'); - } - - /** - * Tests using entity fields of the image field type. - */ - public function testImageItem() { - // Create a test entity with the image field set. - $entity = entity_create('entity_test'); - $entity->image_test->target_id = $this->image->id(); - $entity->image_test->alt = $alt = $this->randomMachineName(); - $entity->image_test->title = $title = $this->randomMachineName(); - $entity->name->value = $this->randomMachineName(); - $entity->save(); - - $entity = entity_load('entity_test', $entity->id()); - $this->assertTrue($entity->image_test instanceof FieldItemListInterface, 'Field implements interface.'); - $this->assertTrue($entity->image_test[0] instanceof FieldItemInterface, 'Field item implements interface.'); - $this->assertEqual($entity->image_test->target_id, $this->image->id()); - $this->assertEqual($entity->image_test->alt, $alt); - $this->assertEqual($entity->image_test->title, $title); - $image = $this->imageFactory->get('public://example.jpg'); - $this->assertEqual($entity->image_test->width, $image->getWidth()); - $this->assertEqual($entity->image_test->height, $image->getHeight()); - $this->assertEqual($entity->image_test->entity->id(), $this->image->id()); - $this->assertEqual($entity->image_test->entity->uuid(), $this->image->uuid()); - - // Make sure the computed entity reflects updates to the referenced file. - file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example-2.jpg'); - $image2 = entity_create('file', array( - 'uri' => 'public://example-2.jpg', - )); - $image2->save(); - - $entity->image_test->target_id = $image2->id(); - $entity->image_test->alt = $new_alt = $this->randomMachineName(); - // The width and height is only updated when width is not set. - $entity->image_test->width = NULL; - $entity->save(); - $this->assertEqual($entity->image_test->entity->id(), $image2->id()); - $this->assertEqual($entity->image_test->entity->getFileUri(), $image2->getFileUri()); - $image = $this->imageFactory->get('public://example-2.jpg'); - $this->assertEqual($entity->image_test->width, $image->getWidth()); - $this->assertEqual($entity->image_test->height, $image->getHeight()); - $this->assertEqual($entity->image_test->alt, $new_alt); - - // Check that the image item can be set to the referenced file directly. - $entity->image_test = $this->image; - $this->assertEqual($entity->image_test->target_id, $this->image->id()); - - // Delete the image and try to save the entity again. - $this->image->delete(); - $entity = entity_create('entity_test', array('mame' => $this->randomMachineName())); - $entity->save(); - - // Test image item properties. - $expected = array('target_id', 'entity', 'alt', 'title', 'width', 'height'); - $properties = $entity->getFieldDefinition('image_test')->getFieldStorageDefinition()->getPropertyDefinitions(); - $this->assertEqual(array_keys($properties), $expected); - - // Test the generateSampleValue() method. - $entity = entity_create('entity_test'); - $entity->image_test->generateSampleItems(); - $this->entityValidateAndSave($entity); - } - -} diff --git a/core/modules/image/src/Tests/ImageStylesPathAndUrlTest.php b/core/modules/image/src/Tests/ImageStylesPathAndUrlTest.php index 81c1628..8987691 100644 --- a/core/modules/image/src/Tests/ImageStylesPathAndUrlTest.php +++ b/core/modules/image/src/Tests/ImageStylesPathAndUrlTest.php @@ -7,6 +7,7 @@ namespace Drupal\image\Tests; +use Drupal\image\Entity\ImageStyle; use Drupal\simpletest\WebTestBase; /** @@ -31,7 +32,7 @@ class ImageStylesPathAndUrlTest extends WebTestBase { protected function setUp() { parent::setUp(); - $this->style = entity_create('image_style', array('name' => 'style_foo', 'label' => $this->randomString())); + $this->style = ImageStyle::create(array('name' => 'style_foo', 'label' => $this->randomString())); $this->style->save(); } @@ -237,7 +238,7 @@ function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_slash = // image derivative using the first one as a source. $nested_url = $this->style->buildUrl($generated_uri, $clean_url); $matches_expected_url_format = (boolean) preg_match('/styles\/' . $this->style->id() . '\/' . $scheme . '\/styles\/' . $this->style->id() . '\/' . $scheme . '/', $nested_url); - $this->assertTrue($matches_expected_url_format, "Url for a derivative of an image style matches expected format."); + $this->assertTrue($matches_expected_url_format, "URL for a derivative of an image style matches expected format."); $nested_url_with_wrong_token = str_replace(IMAGE_DERIVATIVE_TOKEN . '=', 'wrongparam=', $nested_url); $this->drupalGet($nested_url_with_wrong_token); $this->assertResponse(403, 'Image generated from an earlier derivative was inaccessible at the URL with a missing token.'); diff --git a/core/modules/image/src/Tests/ImageThemeFunctionTest.php b/core/modules/image/src/Tests/ImageThemeFunctionTest.php index 4d4dd98..e2f7e9e 100644 --- a/core/modules/image/src/Tests/ImageThemeFunctionTest.php +++ b/core/modules/image/src/Tests/ImageThemeFunctionTest.php @@ -9,7 +9,12 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Url; +use Drupal\entity_test\Entity\EntityTest; +use Drupal\field\Entity\FieldConfig; +use Drupal\file\Entity\File; +use Drupal\image\Entity\ImageStyle; use Drupal\simpletest\WebTestBase; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests image theme functions. @@ -40,21 +45,21 @@ class ImageThemeFunctionTest extends WebTestBase { protected function setUp() { parent::setUp(); - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'entity_type' => 'entity_test', 'field_name' => 'image_test', 'type' => 'image', 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'entity_type' => 'entity_test', 'field_name' => 'image_test', 'bundle' => 'entity_test', - ))->save(); + ])->save(); file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example.jpg'); - $this->image = entity_create('file', array( + $this->image = File::create([ 'uri' => 'public://example.jpg', - )); + ]); $this->image->save(); $this->imageFactory = $this->container->get('image.factory'); } @@ -72,12 +77,12 @@ function testImageFormatterTheme() { $original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME); // Create a style. - $style = entity_create('image_style', array('name' => 'test', 'label' => 'Test')); + $style = ImageStyle::create(array('name' => 'test', 'label' => 'Test')); $style->save(); $url = file_url_transform_relative($style->buildUrl($original_uri)); // Create a test entity with the image field set. - $entity = entity_create('entity_test'); + $entity = EntityTest::create(); $entity->image_test->target_id = $this->image->id(); $entity->image_test->alt = NULL; $entity->image_test->uri = $original_uri; @@ -134,7 +139,7 @@ function testImageStyleTheme() { $original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME); // Create a style. - $style = entity_create('image_style', array('name' => 'image_test', 'label' => 'Test')); + $style = ImageStyle::create(array('name' => 'image_test', 'label' => 'Test')); $style->save(); $url = file_url_transform_relative($style->buildUrl($original_uri)); diff --git a/core/modules/image/src/Tests/Migrate/d6/MigrateImageCacheTest.php b/core/modules/image/src/Tests/Migrate/d6/MigrateImageCacheTest.php deleted file mode 100644 index 11e5c73..0000000 --- a/core/modules/image/src/Tests/Migrate/d6/MigrateImageCacheTest.php +++ /dev/null @@ -1,181 +0,0 @@ -installConfig(['image']); - } - - /** - * Tests that an exception is thrown when ImageCache is not installed. - */ - public function testMissingTable() { - $this->sourceDatabase->update('system') - ->fields(array( - 'status' => 0, - )) - ->condition('name', 'imagecache') - ->condition('type', 'module') - ->execute(); - - try { - Migration::load('d6_imagecache_presets') - ->getSourcePlugin() - ->checkRequirements(); - $this->fail('Did not catch expected RequirementsException.'); - } - catch (RequirementsException $e) { - $this->pass('Caught expected RequirementsException: ' . $e->getMessage()); - } - } - - /** - * Test basic passing migrations. - */ - public function testPassingMigration() { - $this->executeMigration('d6_imagecache_presets'); - - /** @var \Drupal\image\Entity\ImageStyle $style */ - $style = ImageStyle::load('big_blue_cheese'); - - // Check basic Style info. - $this->assertIdentical('big_blue_cheese', $style->get('name'), 'ImageStyle name set correctly'); - $this->assertIdentical('big_blue_cheese', $style->get('label'), 'ImageStyle label set correctly'); - - // Test effects. - $effects = $style->getEffects(); - - // Check crop effect. - $this->assertImageEffect($effects, 'image_crop', [ - 'width' => 555, - 'height' => 5555, - 'anchor' => 'center-center', - ]); - - // Check resize effect. - $this->assertImageEffect($effects, 'image_resize', [ - 'width' => 55, - 'height' => 55, - ]); - - // Check rotate effect. - $this->assertImageEffect($effects, 'image_rotate', [ - 'degrees' => 55, - 'random' => FALSE, - 'bgcolor' => '', - ]); - } - - /** - * Test that missing actions causes failures. - */ - public function testMissingEffectPlugin() { - Database::getConnection('default', 'migrate')->insert("imagecache_action") - ->fields([ - 'presetid', - 'weight', - 'module', - 'action', - 'data', - ]) - ->values([ - 'presetid' => '1', - 'weight' => '0', - 'module' => 'imagecache', - 'action' => 'imagecache_deprecated_scale', - 'data' => 'a:3:{s:3:"fit";s:7:"outside";s:5:"width";s:3:"200";s:6:"height";s:3:"200";}', - ])->execute(); - - $this->startCollectingMessages(); - $this->executeMigration('d6_imagecache_presets'); - $messages = $this->migration->getIdMap()->getMessageIterator(); - $count = 0; - foreach ($messages as $message) { - $count++; - $this->assertEqual($message->message, 'The "image_deprecated_scale" plugin does not exist.'); - $this->assertEqual($message->level, MigrationInterface::MESSAGE_ERROR); - } - // There should be only the one message. - $this->assertEqual($count, 1); - } - - /** - * Test that missing action's causes failures. - */ - public function testInvalidCropValues() { - Database::getConnection('default', 'migrate')->insert("imagecache_action") - ->fields([ - 'presetid', - 'weight', - 'module', - 'action', - 'data', - ]) - ->values([ - 'presetid' => '1', - 'weight' => '0', - 'module' => 'imagecache', - 'action' => 'imagecache_crop', - 'data' => serialize([ - 'xoffset' => '10', - 'yoffset' => '10', - ]), - ])->execute(); - - $this->startCollectingMessages(); - $this->executeMigration('d6_imagecache_presets'); - $this->assertEqual(['error' => [ - 'The Drupal 8 image crop effect does not support numeric values for x and y offsets. Use keywords to set crop effect offsets instead.' - ]], $this->migrateMessages); - } - - /** - * Assert that a given image effect is migrated. - * - * @param array $collection - * Collection of effects - * @param $id - * Id that should exist in the collection. - * @param $config - * Expected configuration for the collection. - * - * @return bool - */ - protected function assertImageEffect($collection, $id, $config) { - /** @var \Drupal\image\ConfigurableImageEffectBase $effect */ - foreach ($collection as $key => $effect) { - $effect_config = $effect->getConfiguration(); - - if ($effect_config['id'] == $id && $effect_config['data'] == $config) { - // We found this effect so succeed and return. - return $this->pass('Effect ' . $id . ' imported correctly'); - } - } - // The loop did not find the effect so we it was not imported correctly. - return $this->fail('Effect ' . $id . ' did not import correctly'); - } - -} diff --git a/core/modules/image/src/Tests/Migrate/d7/MigrateImageSettingsTest.php b/core/modules/image/src/Tests/Migrate/d7/MigrateImageSettingsTest.php deleted file mode 100644 index f1933da..0000000 --- a/core/modules/image/src/Tests/Migrate/d7/MigrateImageSettingsTest.php +++ /dev/null @@ -1,40 +0,0 @@ -executeMigration('d7_image_settings'); - } - - /** - * Tests the migration. - */ - public function testMigration() { - $config = $this->config('image.settings'); - // These settings are not recommended... - $this->assertTrue($config->get('allow_insecure_derivatives')); - $this->assertTrue($config->get('suppress_itok_output')); - $this->assertIdentical("core/modules/image/testsample.png", $config->get('preview_image')); - } - -} diff --git a/core/modules/image/src/Tests/Migrate/d7/MigrateImageStylesTest.php b/core/modules/image/src/Tests/Migrate/d7/MigrateImageStylesTest.php deleted file mode 100644 index a4d11ce..0000000 --- a/core/modules/image/src/Tests/Migrate/d7/MigrateImageStylesTest.php +++ /dev/null @@ -1,78 +0,0 @@ -installConfig(static::$modules); - $this->executeMigration('d7_image_styles'); - } - - /** - * Test the image styles migration. - */ - public function testImageStylesMigration() { - $this->assertEntity('custom_image_style_1', "Custom image style 1", ['image_scale_and_crop', 'image_desaturate'], [['width' => 55, 'height' => 55], []]); - $this->assertEntity('custom_image_style_2', "Custom image style 2", ['image_resize', 'image_rotate'], [['width' => 55, 'height' => 100], ['degrees' => 45, 'bgcolor' => '#FFFFFF', 'random' => false]]); - $this->assertEntity('custom_image_style_3', "Custom image style 3", ['image_scale', 'image_crop'], [['width' => 150, 'height' => NULL, 'upscale' => false], ['width' => 50, 'height' => 50, 'anchor' => 'left-top']]); - } - - /** - * Asserts various aspects of an ImageStyle entity. - * - * @param string $id - * The expected image style ID. - * @param string $label - * The expected image style label. - * @param array $expected_effect_plugins - * An array of expected plugins attached to the image style entity - * @param array $expected_effect_config - * An array of expected configuration for each effect in the image style - */ - protected function assertEntity($id, $label, array $expected_effect_plugins, array $expected_effect_config) { - $style = ImageStyle::load($id); - $this->assertTrue($style instanceof ImageStyleInterface); - /** @var \Drupal\image\ImageStyleInterface $style */ - $this->assertIdentical($id, $style->id()); - $this->assertIdentical($label, $style->label()); - - // Check the number of effects associated with the style. - $effects = $style->getEffects(); - $this->assertIdentical(count($effects), count($expected_effect_plugins)); - - $index = 0; - foreach ($effects as $effect) { - $this->assertTrue($effect instanceof ImageEffectBase); - $this->assertIdentical($expected_effect_plugins[$index], $effect->getPluginId()); - $config = $effect->getConfiguration(); - $this->assertIdentical($expected_effect_config[$index], $config['data']); - $index++; - } - } - -} diff --git a/core/modules/image/src/Tests/Views/ImageViewsDataTest.php b/core/modules/image/src/Tests/Views/ImageViewsDataTest.php deleted file mode 100644 index c9d1215..0000000 --- a/core/modules/image/src/Tests/Views/ImageViewsDataTest.php +++ /dev/null @@ -1,97 +0,0 @@ - 'entity_test', - 'field_name' => 'field_base_image', - 'type' => 'image', - ))->save(); - FieldConfig::create(array( - 'entity_type' => 'entity_test', - 'field_name' => 'field_base_image', - 'bundle' => 'entity_test', - ))->save(); - // Check the generated views data. - $views_data = Views::viewsData()->get('entity_test__field_base_image'); - $relationship = $views_data['field_base_image_target_id']['relationship']; - $this->assertEqual($relationship['id'], 'standard'); - $this->assertEqual($relationship['base'], 'file_managed'); - $this->assertEqual($relationship['base field'], 'fid'); - $this->assertEqual($relationship['entity type'], 'file'); - // Check the backwards reference. - $views_data = Views::viewsData()->get('file_managed'); - $relationship = $views_data['reverse_field_base_image_entity_test']['relationship']; - $this->assertEqual($relationship['id'], 'entity_reverse'); - $this->assertEqual($relationship['base'], 'entity_test'); - $this->assertEqual($relationship['base field'], 'id'); - $this->assertEqual($relationship['field table'], 'entity_test__field_base_image'); - $this->assertEqual($relationship['field field'], 'field_base_image_target_id'); - $this->assertEqual($relationship['field_name'], 'field_base_image'); - $this->assertEqual($relationship['entity_type'], 'entity_test'); - $this->assertEqual($relationship['join_extra'][0], ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE]); - - // Create image field to entity_test_mul. - FieldStorageConfig::create(array( - 'entity_type' => 'entity_test_mul', - 'field_name' => 'field_data_image', - 'type' => 'image', - ))->save(); - FieldConfig::create(array( - 'entity_type' => 'entity_test_mul', - 'field_name' => 'field_data_image', - 'bundle' => 'entity_test_mul', - ))->save(); - // Check the generated views data. - $views_data = Views::viewsData()->get('entity_test_mul__field_data_image'); - $relationship = $views_data['field_data_image_target_id']['relationship']; - $this->assertEqual($relationship['id'], 'standard'); - $this->assertEqual($relationship['base'], 'file_managed'); - $this->assertEqual($relationship['base field'], 'fid'); - $this->assertEqual($relationship['entity type'], 'file'); - // Check the backwards reference. - $views_data = Views::viewsData()->get('file_managed'); - $relationship = $views_data['reverse_field_data_image_entity_test_mul']['relationship']; - $this->assertEqual($relationship['id'], 'entity_reverse'); - $this->assertEqual($relationship['base'], 'entity_test_mul_property_data'); - $this->assertEqual($relationship['base field'], 'id'); - $this->assertEqual($relationship['field table'], 'entity_test_mul__field_data_image'); - $this->assertEqual($relationship['field field'], 'field_data_image_target_id'); - $this->assertEqual($relationship['field_name'], 'field_data_image'); - $this->assertEqual($relationship['entity_type'], 'entity_test_mul'); - $this->assertEqual($relationship['join_extra'][0], ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE]); - } - -} diff --git a/core/modules/image/src/Tests/Views/RelationshipUserImageDataTest.php b/core/modules/image/src/Tests/Views/RelationshipUserImageDataTest.php index 4ef0626..0ef0dd1 100644 --- a/core/modules/image/src/Tests/Views/RelationshipUserImageDataTest.php +++ b/core/modules/image/src/Tests/Views/RelationshipUserImageDataTest.php @@ -7,9 +7,12 @@ namespace Drupal\image\Tests\Views; +use Drupal\field\Entity\FieldConfig; +use Drupal\file\Entity\File; use Drupal\views\Tests\ViewTestBase; use Drupal\views\Views; use Drupal\views\Tests\ViewTestData; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests image on user relationship handler. @@ -36,20 +39,20 @@ protected function setUp() { parent::setUp(); // Create the user profile field and instance. - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'entity_type' => 'user', 'field_name' => 'user_picture', 'type' => 'image', 'translatable' => '0', ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'label' => 'User Picture', 'description' => '', 'field_name' => 'user_picture', 'entity_type' => 'user', 'bundle' => 'user', 'required' => 0, - ))->save(); + ])->save(); ViewTestData::createTestViews(get_class($this), array('image_test_views')); } @@ -58,7 +61,7 @@ protected function setUp() { * Tests using the views image relationship. */ public function testViewsHandlerRelationshipUserImageData() { - $file = entity_create('file', array( + $file = File::create([ 'fid' => 2, 'uid' => 2, 'filename' => 'image-test.jpg', @@ -67,7 +70,7 @@ public function testViewsHandlerRelationshipUserImageData() { 'created' => 1, 'changed' => 1, 'status' => FILE_STATUS_PERMANENT, - )); + ]); $file->enforceIsNew(); file_put_contents($file->getFileUri(), file_get_contents('core/modules/simpletest/files/image-1.png')); $file->save(); diff --git a/core/modules/image/tests/src/Kernel/ImageImportTest.php b/core/modules/image/tests/src/Kernel/ImageImportTest.php new file mode 100644 index 0000000..6771ecc --- /dev/null +++ b/core/modules/image/tests/src/Kernel/ImageImportTest.php @@ -0,0 +1,51 @@ + 'test' + ]); + + $style->addImageEffect(['id' => 'image_module_test_null']); + $style->addImageEffect(['id' => 'image_module_test_null']); + $style->save(); + + $this->assertEqual(count($style->getEffects()), 2); + + $uuid = \Drupal::service('uuid')->generate(); + $style->set('effects', [ + $uuid => [ + 'id' => 'image_module_test_null', + ], + ]); + $style->save(); + + $style = ImageStyle::load('test'); + $this->assertEqual(count($style->getEffects()), 1); + } + +} diff --git a/core/modules/image/tests/src/Kernel/ImageItemTest.php b/core/modules/image/tests/src/Kernel/ImageItemTest.php new file mode 100644 index 0000000..8eb7946 --- /dev/null +++ b/core/modules/image/tests/src/Kernel/ImageItemTest.php @@ -0,0 +1,133 @@ +installEntitySchema('file'); + $this->installSchema('file', array('file_usage')); + + FieldStorageConfig::create(array( + 'entity_type' => 'entity_test', + 'field_name' => 'image_test', + 'type' => 'image', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + ))->save(); + FieldConfig::create([ + 'entity_type' => 'entity_test', + 'field_name' => 'image_test', + 'bundle' => 'entity_test', + ])->save(); + file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example.jpg'); + $this->image = File::create([ + 'uri' => 'public://example.jpg', + ]); + $this->image->save(); + $this->imageFactory = $this->container->get('image.factory'); + } + + /** + * Tests using entity fields of the image field type. + */ + public function testImageItem() { + // Create a test entity with the image field set. + $entity = EntityTest::create(); + $entity->image_test->target_id = $this->image->id(); + $entity->image_test->alt = $alt = $this->randomMachineName(); + $entity->image_test->title = $title = $this->randomMachineName(); + $entity->name->value = $this->randomMachineName(); + $entity->save(); + + $entity = entity_load('entity_test', $entity->id()); + $this->assertTrue($entity->image_test instanceof FieldItemListInterface, 'Field implements interface.'); + $this->assertTrue($entity->image_test[0] instanceof FieldItemInterface, 'Field item implements interface.'); + $this->assertEqual($entity->image_test->target_id, $this->image->id()); + $this->assertEqual($entity->image_test->alt, $alt); + $this->assertEqual($entity->image_test->title, $title); + $image = $this->imageFactory->get('public://example.jpg'); + $this->assertEqual($entity->image_test->width, $image->getWidth()); + $this->assertEqual($entity->image_test->height, $image->getHeight()); + $this->assertEqual($entity->image_test->entity->id(), $this->image->id()); + $this->assertEqual($entity->image_test->entity->uuid(), $this->image->uuid()); + + // Make sure the computed entity reflects updates to the referenced file. + file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example-2.jpg'); + $image2 = File::create([ + 'uri' => 'public://example-2.jpg', + ]); + $image2->save(); + + $entity->image_test->target_id = $image2->id(); + $entity->image_test->alt = $new_alt = $this->randomMachineName(); + // The width and height is only updated when width is not set. + $entity->image_test->width = NULL; + $entity->save(); + $this->assertEqual($entity->image_test->entity->id(), $image2->id()); + $this->assertEqual($entity->image_test->entity->getFileUri(), $image2->getFileUri()); + $image = $this->imageFactory->get('public://example-2.jpg'); + $this->assertEqual($entity->image_test->width, $image->getWidth()); + $this->assertEqual($entity->image_test->height, $image->getHeight()); + $this->assertEqual($entity->image_test->alt, $new_alt); + + // Check that the image item can be set to the referenced file directly. + $entity->image_test = $this->image; + $this->assertEqual($entity->image_test->target_id, $this->image->id()); + + // Delete the image and try to save the entity again. + $this->image->delete(); + $entity = EntityTest::create(array('mame' => $this->randomMachineName())); + $entity->save(); + + // Test image item properties. + $expected = array('target_id', 'entity', 'alt', 'title', 'width', 'height'); + $properties = $entity->getFieldDefinition('image_test')->getFieldStorageDefinition()->getPropertyDefinitions(); + $this->assertEqual(array_keys($properties), $expected); + + // Test the generateSampleValue() method. + $entity = EntityTest::create(); + $entity->image_test->generateSampleItems(); + $this->entityValidateAndSave($entity); + } + +} diff --git a/core/modules/image/tests/src/Kernel/Migrate/d6/MigrateImageCacheTest.php b/core/modules/image/tests/src/Kernel/Migrate/d6/MigrateImageCacheTest.php new file mode 100644 index 0000000..b8b40f8 --- /dev/null +++ b/core/modules/image/tests/src/Kernel/Migrate/d6/MigrateImageCacheTest.php @@ -0,0 +1,180 @@ +installConfig(['image']); + } + + /** + * Tests that an exception is thrown when ImageCache is not installed. + */ + public function testMissingTable() { + $this->sourceDatabase->update('system') + ->fields(array( + 'status' => 0, + )) + ->condition('name', 'imagecache') + ->condition('type', 'module') + ->execute(); + + try { + $this->getMigration('d6_imagecache_presets') + ->getSourcePlugin() + ->checkRequirements(); + $this->fail('Did not catch expected RequirementsException.'); + } + catch (RequirementsException $e) { + $this->pass('Caught expected RequirementsException: ' . $e->getMessage()); + } + } + + /** + * Test basic passing migrations. + */ + public function testPassingMigration() { + $this->executeMigration('d6_imagecache_presets'); + + /** @var \Drupal\image\Entity\ImageStyle $style */ + $style = ImageStyle::load('big_blue_cheese'); + + // Check basic Style info. + $this->assertIdentical('big_blue_cheese', $style->get('name'), 'ImageStyle name set correctly'); + $this->assertIdentical('big_blue_cheese', $style->get('label'), 'ImageStyle label set correctly'); + + // Test effects. + $effects = $style->getEffects(); + + // Check crop effect. + $this->assertImageEffect($effects, 'image_crop', [ + 'width' => 555, + 'height' => 5555, + 'anchor' => 'center-center', + ]); + + // Check resize effect. + $this->assertImageEffect($effects, 'image_resize', [ + 'width' => 55, + 'height' => 55, + ]); + + // Check rotate effect. + $this->assertImageEffect($effects, 'image_rotate', [ + 'degrees' => 55, + 'random' => FALSE, + 'bgcolor' => '', + ]); + } + + /** + * Test that missing actions causes failures. + */ + public function testMissingEffectPlugin() { + Database::getConnection('default', 'migrate')->insert("imagecache_action") + ->fields([ + 'presetid', + 'weight', + 'module', + 'action', + 'data', + ]) + ->values([ + 'presetid' => '1', + 'weight' => '0', + 'module' => 'imagecache', + 'action' => 'imagecache_deprecated_scale', + 'data' => 'a:3:{s:3:"fit";s:7:"outside";s:5:"width";s:3:"200";s:6:"height";s:3:"200";}', + ])->execute(); + + $this->startCollectingMessages(); + $this->executeMigration('d6_imagecache_presets'); + $messages = $this->migration->getIdMap()->getMessageIterator(); + $count = 0; + foreach ($messages as $message) { + $count++; + $this->assertEqual($message->message, 'The "image_deprecated_scale" plugin does not exist.'); + $this->assertEqual($message->level, MigrationInterface::MESSAGE_ERROR); + } + // There should be only the one message. + $this->assertEqual($count, 1); + } + + /** + * Test that missing action's causes failures. + */ + public function testInvalidCropValues() { + Database::getConnection('default', 'migrate')->insert("imagecache_action") + ->fields([ + 'presetid', + 'weight', + 'module', + 'action', + 'data', + ]) + ->values([ + 'presetid' => '1', + 'weight' => '0', + 'module' => 'imagecache', + 'action' => 'imagecache_crop', + 'data' => serialize([ + 'xoffset' => '10', + 'yoffset' => '10', + ]), + ])->execute(); + + $this->startCollectingMessages(); + $this->executeMigration('d6_imagecache_presets'); + $this->assertEqual(['error' => [ + 'The Drupal 8 image crop effect does not support numeric values for x and y offsets. Use keywords to set crop effect offsets instead.' + ]], $this->migrateMessages); + } + + /** + * Assert that a given image effect is migrated. + * + * @param array $collection + * Collection of effects + * @param $id + * Id that should exist in the collection. + * @param $config + * Expected configuration for the collection. + * + * @return bool + */ + protected function assertImageEffect($collection, $id, $config) { + /** @var \Drupal\image\ConfigurableImageEffectBase $effect */ + foreach ($collection as $key => $effect) { + $effect_config = $effect->getConfiguration(); + + if ($effect_config['id'] == $id && $effect_config['data'] == $config) { + // We found this effect so succeed and return. + return $this->pass('Effect ' . $id . ' imported correctly'); + } + } + // The loop did not find the effect so we it was not imported correctly. + return $this->fail('Effect ' . $id . ' did not import correctly'); + } + +} diff --git a/core/modules/image/tests/src/Kernel/Migrate/d7/MigrateImageSettingsTest.php b/core/modules/image/tests/src/Kernel/Migrate/d7/MigrateImageSettingsTest.php new file mode 100644 index 0000000..1a30b6a --- /dev/null +++ b/core/modules/image/tests/src/Kernel/Migrate/d7/MigrateImageSettingsTest.php @@ -0,0 +1,40 @@ +executeMigration('d7_image_settings'); + } + + /** + * Tests the migration. + */ + public function testMigration() { + $config = $this->config('image.settings'); + // These settings are not recommended... + $this->assertTrue($config->get('allow_insecure_derivatives')); + $this->assertTrue($config->get('suppress_itok_output')); + $this->assertIdentical("core/modules/image/testsample.png", $config->get('preview_image')); + } + +} diff --git a/core/modules/image/tests/src/Kernel/Migrate/d7/MigrateImageStylesTest.php b/core/modules/image/tests/src/Kernel/Migrate/d7/MigrateImageStylesTest.php new file mode 100644 index 0000000..1ded5dd --- /dev/null +++ b/core/modules/image/tests/src/Kernel/Migrate/d7/MigrateImageStylesTest.php @@ -0,0 +1,78 @@ +installConfig(static::$modules); + $this->executeMigration('d7_image_styles'); + } + + /** + * Test the image styles migration. + */ + public function testImageStylesMigration() { + $this->assertEntity('custom_image_style_1', "Custom image style 1", ['image_scale_and_crop', 'image_desaturate'], [['width' => 55, 'height' => 55], []]); + $this->assertEntity('custom_image_style_2', "Custom image style 2", ['image_resize', 'image_rotate'], [['width' => 55, 'height' => 100], ['degrees' => 45, 'bgcolor' => '#FFFFFF', 'random' => false]]); + $this->assertEntity('custom_image_style_3', "Custom image style 3", ['image_scale', 'image_crop'], [['width' => 150, 'height' => NULL, 'upscale' => false], ['width' => 50, 'height' => 50, 'anchor' => 'left-top']]); + } + + /** + * Asserts various aspects of an ImageStyle entity. + * + * @param string $id + * The expected image style ID. + * @param string $label + * The expected image style label. + * @param array $expected_effect_plugins + * An array of expected plugins attached to the image style entity + * @param array $expected_effect_config + * An array of expected configuration for each effect in the image style + */ + protected function assertEntity($id, $label, array $expected_effect_plugins, array $expected_effect_config) { + $style = ImageStyle::load($id); + $this->assertTrue($style instanceof ImageStyleInterface); + /** @var \Drupal\image\ImageStyleInterface $style */ + $this->assertIdentical($id, $style->id()); + $this->assertIdentical($label, $style->label()); + + // Check the number of effects associated with the style. + $effects = $style->getEffects(); + $this->assertIdentical(count($effects), count($expected_effect_plugins)); + + $index = 0; + foreach ($effects as $effect) { + $this->assertTrue($effect instanceof ImageEffectBase); + $this->assertIdentical($expected_effect_plugins[$index], $effect->getPluginId()); + $config = $effect->getConfiguration(); + $this->assertIdentical($expected_effect_config[$index], $config['data']); + $index++; + } + } + +} diff --git a/core/modules/image/tests/src/Kernel/Views/ImageViewsDataTest.php b/core/modules/image/tests/src/Kernel/Views/ImageViewsDataTest.php new file mode 100644 index 0000000..0300fa5 --- /dev/null +++ b/core/modules/image/tests/src/Kernel/Views/ImageViewsDataTest.php @@ -0,0 +1,97 @@ + 'entity_test', + 'field_name' => 'field_base_image', + 'type' => 'image', + ))->save(); + FieldConfig::create(array( + 'entity_type' => 'entity_test', + 'field_name' => 'field_base_image', + 'bundle' => 'entity_test', + ))->save(); + // Check the generated views data. + $views_data = Views::viewsData()->get('entity_test__field_base_image'); + $relationship = $views_data['field_base_image_target_id']['relationship']; + $this->assertEqual($relationship['id'], 'standard'); + $this->assertEqual($relationship['base'], 'file_managed'); + $this->assertEqual($relationship['base field'], 'fid'); + $this->assertEqual($relationship['entity type'], 'file'); + // Check the backwards reference. + $views_data = Views::viewsData()->get('file_managed'); + $relationship = $views_data['reverse_field_base_image_entity_test']['relationship']; + $this->assertEqual($relationship['id'], 'entity_reverse'); + $this->assertEqual($relationship['base'], 'entity_test'); + $this->assertEqual($relationship['base field'], 'id'); + $this->assertEqual($relationship['field table'], 'entity_test__field_base_image'); + $this->assertEqual($relationship['field field'], 'field_base_image_target_id'); + $this->assertEqual($relationship['field_name'], 'field_base_image'); + $this->assertEqual($relationship['entity_type'], 'entity_test'); + $this->assertEqual($relationship['join_extra'][0], ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE]); + + // Create image field to entity_test_mul. + FieldStorageConfig::create(array( + 'entity_type' => 'entity_test_mul', + 'field_name' => 'field_data_image', + 'type' => 'image', + ))->save(); + FieldConfig::create(array( + 'entity_type' => 'entity_test_mul', + 'field_name' => 'field_data_image', + 'bundle' => 'entity_test_mul', + ))->save(); + // Check the generated views data. + $views_data = Views::viewsData()->get('entity_test_mul__field_data_image'); + $relationship = $views_data['field_data_image_target_id']['relationship']; + $this->assertEqual($relationship['id'], 'standard'); + $this->assertEqual($relationship['base'], 'file_managed'); + $this->assertEqual($relationship['base field'], 'fid'); + $this->assertEqual($relationship['entity type'], 'file'); + // Check the backwards reference. + $views_data = Views::viewsData()->get('file_managed'); + $relationship = $views_data['reverse_field_data_image_entity_test_mul']['relationship']; + $this->assertEqual($relationship['id'], 'entity_reverse'); + $this->assertEqual($relationship['base'], 'entity_test_mul_property_data'); + $this->assertEqual($relationship['base field'], 'id'); + $this->assertEqual($relationship['field table'], 'entity_test_mul__field_data_image'); + $this->assertEqual($relationship['field field'], 'field_data_image_target_id'); + $this->assertEqual($relationship['field_name'], 'field_data_image'); + $this->assertEqual($relationship['entity_type'], 'entity_test_mul'); + $this->assertEqual($relationship['join_extra'][0], ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE]); + } + +} diff --git a/core/modules/inline_form_errors/inline_form_errors.info.yml b/core/modules/inline_form_errors/inline_form_errors.info.yml index a5949fa..04c05b9 100644 --- a/core/modules/inline_form_errors/inline_form_errors.info.yml +++ b/core/modules/inline_form_errors/inline_form_errors.info.yml @@ -1,6 +1,6 @@ type: module name: Inline Form Errors -description: 'Enables inline form errors.' +description: 'Adds WCAG 2.0 accessibility compliance for web form errors. Some form functionality may not work with this experimental module.' version: VERSION core: 8.x package: Core (Experimental) diff --git a/core/modules/language/language.module b/core/modules/language/language.module index 5904685..99ebc3b 100644 --- a/core/modules/language/language.module +++ b/core/modules/language/language.module @@ -29,7 +29,7 @@ function language_help($route_name, RouteMatchInterface $route_match) { case 'help.page.language': $output = ''; $output .= '

                          ' . t('About') . '

                          '; - $output .= '

                          ' . t('The Language module allows you to configure the languages used on your site, and provides information for the for Content Translation, Interface Translation, and Configuration Translation modules, if they are enabled. For more information, see the online documentation for the Language module.', array(':doc_url' => 'https://www.drupal.org/documentation/modules/language', ':content' => (\Drupal::moduleHandler()->moduleExists('content_translation')) ? \Drupal::url('help.page', array('name' => 'content_translation')) : '#', ':interface' => (\Drupal::moduleHandler()->moduleExists('locale')) ? \Drupal::url('help.page', array('name' => 'locale')) : '#', ':configuration' => (\Drupal::moduleHandler()->moduleExists('config_translation')) ? \Drupal::url('help.page', array('name' => 'config_translation')) : '#')) . '

                          '; + $output .= '

                          ' . t('The Language module allows you to configure the languages used on your site, and provides information for the Content Translation, Interface Translation, and Configuration Translation modules, if they are enabled. For more information, see the online documentation for the Language module.', array(':doc_url' => 'https://www.drupal.org/documentation/modules/language', ':content' => (\Drupal::moduleHandler()->moduleExists('content_translation')) ? \Drupal::url('help.page', array('name' => 'content_translation')) : '#', ':interface' => (\Drupal::moduleHandler()->moduleExists('locale')) ? \Drupal::url('help.page', array('name' => 'locale')) : '#', ':configuration' => (\Drupal::moduleHandler()->moduleExists('config_translation')) ? \Drupal::url('help.page', array('name' => 'config_translation')) : '#')) . '

                          '; $output .= '

                          ' . t('Uses') . '

                          '; $output .= '
                          '; $output .= '
                          ' . t('Adding languages') . '
                          '; diff --git a/core/modules/language/src/Element/LanguageConfiguration.php b/core/modules/language/src/Element/LanguageConfiguration.php index 2fad091..74d36aa 100644 --- a/core/modules/language/src/Element/LanguageConfiguration.php +++ b/core/modules/language/src/Element/LanguageConfiguration.php @@ -10,7 +10,6 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Render\Element\FormElement; -use Drupal\language\Entity\ContentLanguageSettings; /** * Provides language element configuration. @@ -41,7 +40,7 @@ public static function processLanguageConfiguration(&$element, FormStateInterfac // Avoid validation failure since we are moving the '#options' key in the // nested 'language' select element. unset($element['#options']); - /** @var ContentLanguageSettings $default_config */ + /** @var \Drupal\language\Entity\ContentLanguageSettings $default_config */ $default_config = $element['#default_value']; $element['langcode'] = array( '#type' => 'select', diff --git a/core/modules/language/src/Entity/ContentLanguageSettings.php b/core/modules/language/src/Entity/ContentLanguageSettings.php index 49ebc63..209c3b5 100644 --- a/core/modules/language/src/Entity/ContentLanguageSettings.php +++ b/core/modules/language/src/Entity/ContentLanguageSettings.php @@ -68,7 +68,7 @@ class ContentLanguageSettings extends ConfigEntityBase implements ContentLanguag * Constructs a ContentLanguageSettings object. * * In most cases, Field entities are created via - * entity_create('field_config', $values), where $values is the same + * FieldConfig::create($values), where $values is the same * parameter as in this constructor. * * @param array $values diff --git a/core/modules/language/src/Form/NegotiationUrlForm.php b/core/modules/language/src/Form/NegotiationUrlForm.php index f90022f..1dcfe05 100644 --- a/core/modules/language/src/Form/NegotiationUrlForm.php +++ b/core/modules/language/src/Form/NegotiationUrlForm.php @@ -100,7 +100,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#tree' => TRUE, '#title' => $this->t('Domain configuration'), '#open' => TRUE, - '#description' => $this->t('The domain names to use for these languages. Modifying this value may break existing URLs. Use with caution in a production environment. Example: Specifying "de.example.com" as language domain for German will result in an URL like "http://de.example.com/contact".'), + '#description' => $this->t('The domain names to use for these languages. Modifying this value may break existing URLs. Use with caution in a production environment. Example: Specifying "de.example.com" as language domain for German will result in a URL like "http://de.example.com/contact".'), '#states' => array( 'visible' => array( ':input[name="language_negotiation_url_part"]' => array( diff --git a/core/modules/language/src/LanguageAccessControlHandler.php b/core/modules/language/src/LanguageAccessControlHandler.php index 6d14471..fd7b669 100644 --- a/core/modules/language/src/LanguageAccessControlHandler.php +++ b/core/modules/language/src/LanguageAccessControlHandler.php @@ -26,13 +26,13 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter switch ($operation) { case 'update': /* @var \Drupal\Core\Language\LanguageInterface $entity */ - return AccessResult::allowedIf(!$entity->isLocked())->cacheUntilEntityChanges($entity) + return AccessResult::allowedIf(!$entity->isLocked())->addCacheableDependency($entity) ->andIf(parent::checkAccess($entity, $operation, $account)); case 'delete': /* @var \Drupal\Core\Language\LanguageInterface $entity */ - return AccessResult::allowedIf(!$entity->isLocked())->cacheUntilEntityChanges($entity) - ->andIf(AccessResult::allowedIf(!$entity->isDefault())->cacheUntilEntityChanges($entity)) + return AccessResult::allowedIf(!$entity->isLocked())->addCacheableDependency($entity) + ->andIf(AccessResult::allowedIf(!$entity->isDefault())->addCacheableDependency($entity)) ->andIf(parent::checkAccess($entity, $operation, $account)); default: diff --git a/core/modules/language/src/LanguageListBuilder.php b/core/modules/language/src/LanguageListBuilder.php index e6b279d..ecbf347 100644 --- a/core/modules/language/src/LanguageListBuilder.php +++ b/core/modules/language/src/LanguageListBuilder.php @@ -14,7 +14,6 @@ use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageManagerInterface; -use Drupal\Core\Render\Element; use Symfony\Component\DependencyInjection\ContainerInterface; /** diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationContentEntity.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationContentEntity.php index fd9da35..4ce022d 100644 --- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationContentEntity.php +++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationContentEntity.php @@ -181,8 +181,8 @@ public function getLanguageSwitchLinks(Request $request, $type, Url $url) { * \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationContentEntity::processOutbound(). * * @return bool - * TRUE if the content entity language negotiator has higher priority than - * the url language negotiator, FALSE otherwise. + * TRUE if the the content entity language negotiator has higher priority + * than the url language negotiator, FALSE otherwise. */ protected function hasLowerLanguageNegotiationWeight() { if (!isset($this->hasLowerLanguageNegotiationWeightResult)) { diff --git a/core/modules/language/src/Tests/EntityUrlLanguageTest.php b/core/modules/language/src/Tests/EntityUrlLanguageTest.php index f172b38..5d0872e 100644 --- a/core/modules/language/src/Tests/EntityUrlLanguageTest.php +++ b/core/modules/language/src/Tests/EntityUrlLanguageTest.php @@ -41,7 +41,6 @@ protected function setUp() { $this->installEntitySchema('entity_test'); $this->installEntitySchema('configurable_language'); - $this->installSchema('system', 'router'); \Drupal::service('router.builder')->rebuild(); // In order to reflect the changes for a multilingual site in the container diff --git a/core/modules/language/src/Tests/LanguageConfigOverrideInstallTest.php b/core/modules/language/src/Tests/LanguageConfigOverrideInstallTest.php index 28d6330..b3cdd09 100644 --- a/core/modules/language/src/Tests/LanguageConfigOverrideInstallTest.php +++ b/core/modules/language/src/Tests/LanguageConfigOverrideInstallTest.php @@ -4,6 +4,7 @@ * @file * Contains \Drupal\language\Tests\LanguageConfigOverrideInstallTest. */ + namespace Drupal\language\Tests; use Drupal\language\Entity\ConfigurableLanguage; diff --git a/core/modules/language/src/Tests/LanguageConfigurationElementTest.php b/core/modules/language/src/Tests/LanguageConfigurationElementTest.php index 6e2e70a..04cb8da 100644 --- a/core/modules/language/src/Tests/LanguageConfigurationElementTest.php +++ b/core/modules/language/src/Tests/LanguageConfigurationElementTest.php @@ -11,6 +11,7 @@ use Drupal\language\Entity\ConfigurableLanguage; use Drupal\language\Entity\ContentLanguageSettings; use Drupal\simpletest\WebTestBase; +use Drupal\taxonomy\Entity\Vocabulary; /** * Tests the features of the language configuration element field. @@ -223,10 +224,10 @@ public function testNodeTypeDelete() { * Tests that the configuration is retained when a vocabulary is updated. */ public function testTaxonomyVocabularyUpdate() { - $vocabulary = entity_create('taxonomy_vocabulary', array( + $vocabulary = Vocabulary::create([ 'name' => 'Country', 'vid' => 'country', - )); + ]); $vocabulary->save(); $admin_user = $this->drupalCreateUser(array('administer taxonomy')); diff --git a/core/modules/language/src/Tests/LanguageLocaleListTest.php b/core/modules/language/src/Tests/LanguageLocaleListTest.php index ca13172..b3946b1 100644 --- a/core/modules/language/src/Tests/LanguageLocaleListTest.php +++ b/core/modules/language/src/Tests/LanguageLocaleListTest.php @@ -8,8 +8,6 @@ namespace Drupal\language\Tests; use Drupal\simpletest\WebTestBase; -use Drupal\Core\Language\Language; -use Drupal\Core\Language\LanguageInterface; /** * Adds a new language with translations and tests language list order. diff --git a/core/modules/language/src/Tests/Migrate/MigrateLanguageTest.php b/core/modules/language/src/Tests/Migrate/MigrateLanguageTest.php index 7b225ae..9706d71 100644 --- a/core/modules/language/src/Tests/Migrate/MigrateLanguageTest.php +++ b/core/modules/language/src/Tests/Migrate/MigrateLanguageTest.php @@ -9,7 +9,7 @@ use Drupal\language\ConfigurableLanguageInterface; use Drupal\language\Entity\ConfigurableLanguage; -use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase; +use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase; /** * @group migrate_drupal_6 diff --git a/core/modules/language/src/Tests/Migrate/d7/MigrateLanguageNegotiationSettingsTest.php b/core/modules/language/src/Tests/Migrate/d7/MigrateLanguageNegotiationSettingsTest.php deleted file mode 100644 index 242d818..0000000 --- a/core/modules/language/src/Tests/Migrate/d7/MigrateLanguageNegotiationSettingsTest.php +++ /dev/null @@ -1,43 +0,0 @@ -executeMigration('d7_language_negotiation_settings'); - } - - /** - * Tests migration of language negotiation variables to language.negotiation.yml. - */ - public function testLanguageNegotiation() { - $config = $this->config('language.negotiation'); - $this->assertIdentical($config->get('session.parameter'), 'language'); - $this->assertIdentical($config->get('url.source'), 'domain'); - } - -} diff --git a/core/modules/language/src/Tests/Views/ArgumentLanguageTest.php b/core/modules/language/src/Tests/Views/ArgumentLanguageTest.php deleted file mode 100644 index 2403b68..0000000 --- a/core/modules/language/src/Tests/Views/ArgumentLanguageTest.php +++ /dev/null @@ -1,51 +0,0 @@ - 'John', 'xx-lolspeak' => 'George') as $langcode => $name) { - $view->setDisplay(); - $view->displayHandlers->get('default')->overrideOption('arguments', array( - 'langcode' => array( - 'id' => 'langcode', - 'table' => 'views_test_data', - 'field' => 'langcode', - ), - )); - $this->executeView($view, array($langcode)); - - $expected = array(array( - 'name' => $name, - )); - $this->assertIdenticalResultset($view, $expected, array('views_test_data_name' => 'name')); - $view->destroy(); - } - } - -} diff --git a/core/modules/language/src/Tests/Views/FieldLanguageTest.php b/core/modules/language/src/Tests/Views/FieldLanguageTest.php deleted file mode 100644 index 78845ee..0000000 --- a/core/modules/language/src/Tests/Views/FieldLanguageTest.php +++ /dev/null @@ -1,46 +0,0 @@ -setDisplay(); - $view->displayHandlers->get('default')->overrideOption('fields', array( - 'langcode' => array( - 'id' => 'langcode', - 'table' => 'views_test_data', - 'field' => 'langcode', - ), - )); - $this->executeView($view); - - $this->assertEqual($view->field['langcode']->advancedRender($view->result[0]), 'English'); - $this->assertEqual($view->field['langcode']->advancedRender($view->result[1]), 'Lolspeak'); - } - -} diff --git a/core/modules/language/src/Tests/Views/FilterLanguageTest.php b/core/modules/language/src/Tests/Views/FilterLanguageTest.php deleted file mode 100644 index b3cc5d8..0000000 --- a/core/modules/language/src/Tests/Views/FilterLanguageTest.php +++ /dev/null @@ -1,64 +0,0 @@ - 'John', 'xx-lolspeak' => 'George') as $langcode => $name) { - $view->setDisplay(); - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'langcode' => array( - 'id' => 'langcode', - 'table' => 'views_test_data', - 'field' => 'langcode', - 'value' => array($langcode), - ), - )); - $this->executeView($view); - - $expected = array(array( - 'name' => $name, - )); - $this->assertIdenticalResultset($view, $expected, array('views_test_data_name' => 'name')); - - $expected = [ - '***LANGUAGE_site_default***', - '***LANGUAGE_language_interface***', - '***LANGUAGE_language_content***', - 'en', - 'xx-lolspeak', - 'und', - 'zxx' - ]; - $this->assertIdentical(array_keys($view->filter['langcode']->getValueOptions()), $expected); - - $view->destroy(); - } - } - -} diff --git a/core/modules/language/src/Tests/Views/LanguageTestBase.php b/core/modules/language/src/Tests/Views/LanguageTestBase.php deleted file mode 100644 index 822b651..0000000 --- a/core/modules/language/src/Tests/Views/LanguageTestBase.php +++ /dev/null @@ -1,84 +0,0 @@ -installConfig(array('language')); - - // Create another language beside English. - ConfigurableLanguage::create(array('id' => 'xx-lolspeak', 'label' => 'Lolspeak'))->save(); - } - - /** - * {@inheritdoc} - */ - protected function schemaDefinition() { - $schema = parent::schemaDefinition(); - $schema['views_test_data']['fields']['langcode'] = array( - 'description' => 'The {language}.langcode of this beatle.', - 'type' => 'varchar', - 'length' => 12, - 'default' => '', - ); - - return $schema; - } - - /** - * {@inheritdoc} - */ - protected function viewsData() { - $data = parent::viewsData(); - $data['views_test_data']['langcode'] = array( - 'title' => t('Langcode'), - 'help' => t('Langcode'), - 'field' => array( - 'id' => 'language', - ), - 'argument' => array( - 'id' => 'language', - ), - 'filter' => array( - 'id' => 'language', - ), - ); - - return $data; - } - - /** - * {@inheritdoc} - */ - protected function dataSet() { - $data = parent::dataSet(); - $data[0]['langcode'] = 'en'; - $data[1]['langcode'] = 'xx-lolspeak'; - $data[2]['langcode'] = ''; - $data[3]['langcode'] = ''; - $data[4]['langcode'] = ''; - - return $data; - } - -} diff --git a/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageNegotiationSettingsTest.php b/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageNegotiationSettingsTest.php new file mode 100644 index 0000000..38d545f --- /dev/null +++ b/core/modules/language/tests/src/Kernel/Migrate/d7/MigrateLanguageNegotiationSettingsTest.php @@ -0,0 +1,43 @@ +executeMigration('d7_language_negotiation_settings'); + } + + /** + * Tests migration of language negotiation variables to language.negotiation.yml. + */ + public function testLanguageNegotiation() { + $config = $this->config('language.negotiation'); + $this->assertIdentical($config->get('session.parameter'), 'language'); + $this->assertIdentical($config->get('url.source'), 'domain'); + } + +} diff --git a/core/modules/language/tests/src/Kernel/Views/ArgumentLanguageTest.php b/core/modules/language/tests/src/Kernel/Views/ArgumentLanguageTest.php new file mode 100644 index 0000000..70f2da5 --- /dev/null +++ b/core/modules/language/tests/src/Kernel/Views/ArgumentLanguageTest.php @@ -0,0 +1,51 @@ + 'John', 'xx-lolspeak' => 'George') as $langcode => $name) { + $view->setDisplay(); + $view->displayHandlers->get('default')->overrideOption('arguments', array( + 'langcode' => array( + 'id' => 'langcode', + 'table' => 'views_test_data', + 'field' => 'langcode', + ), + )); + $this->executeView($view, array($langcode)); + + $expected = array(array( + 'name' => $name, + )); + $this->assertIdenticalResultset($view, $expected, array('views_test_data_name' => 'name')); + $view->destroy(); + } + } + +} diff --git a/core/modules/language/tests/src/Kernel/Views/FieldLanguageTest.php b/core/modules/language/tests/src/Kernel/Views/FieldLanguageTest.php new file mode 100644 index 0000000..f6e8612 --- /dev/null +++ b/core/modules/language/tests/src/Kernel/Views/FieldLanguageTest.php @@ -0,0 +1,46 @@ +setDisplay(); + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'langcode' => array( + 'id' => 'langcode', + 'table' => 'views_test_data', + 'field' => 'langcode', + ), + )); + $this->executeView($view); + + $this->assertEqual($view->field['langcode']->advancedRender($view->result[0]), 'English'); + $this->assertEqual($view->field['langcode']->advancedRender($view->result[1]), 'Lolspeak'); + } + +} diff --git a/core/modules/language/tests/src/Kernel/Views/FilterLanguageTest.php b/core/modules/language/tests/src/Kernel/Views/FilterLanguageTest.php new file mode 100644 index 0000000..1f67dcd --- /dev/null +++ b/core/modules/language/tests/src/Kernel/Views/FilterLanguageTest.php @@ -0,0 +1,64 @@ + 'John', 'xx-lolspeak' => 'George') as $langcode => $name) { + $view->setDisplay(); + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'langcode' => array( + 'id' => 'langcode', + 'table' => 'views_test_data', + 'field' => 'langcode', + 'value' => array($langcode), + ), + )); + $this->executeView($view); + + $expected = array(array( + 'name' => $name, + )); + $this->assertIdenticalResultset($view, $expected, array('views_test_data_name' => 'name')); + + $expected = [ + '***LANGUAGE_site_default***', + '***LANGUAGE_language_interface***', + '***LANGUAGE_language_content***', + 'en', + 'xx-lolspeak', + 'und', + 'zxx' + ]; + $this->assertIdentical(array_keys($view->filter['langcode']->getValueOptions()), $expected); + + $view->destroy(); + } + } + +} diff --git a/core/modules/language/tests/src/Kernel/Views/LanguageTestBase.php b/core/modules/language/tests/src/Kernel/Views/LanguageTestBase.php new file mode 100644 index 0000000..1b46513 --- /dev/null +++ b/core/modules/language/tests/src/Kernel/Views/LanguageTestBase.php @@ -0,0 +1,84 @@ +installConfig(array('language')); + + // Create another language beside English. + ConfigurableLanguage::create(array('id' => 'xx-lolspeak', 'label' => 'Lolspeak'))->save(); + } + + /** + * {@inheritdoc} + */ + protected function schemaDefinition() { + $schema = parent::schemaDefinition(); + $schema['views_test_data']['fields']['langcode'] = array( + 'description' => 'The {language}.langcode of this beatle.', + 'type' => 'varchar', + 'length' => 12, + 'default' => '', + ); + + return $schema; + } + + /** + * {@inheritdoc} + */ + protected function viewsData() { + $data = parent::viewsData(); + $data['views_test_data']['langcode'] = array( + 'title' => t('Langcode'), + 'help' => t('Langcode'), + 'field' => array( + 'id' => 'language', + ), + 'argument' => array( + 'id' => 'language', + ), + 'filter' => array( + 'id' => 'language', + ), + ); + + return $data; + } + + /** + * {@inheritdoc} + */ + protected function dataSet() { + $data = parent::dataSet(); + $data[0]['langcode'] = 'en'; + $data[1]['langcode'] = 'xx-lolspeak'; + $data[2]['langcode'] = ''; + $data[3]['langcode'] = ''; + $data[4]['langcode'] = ''; + + return $data; + } + +} diff --git a/core/modules/link/config/schema/link.schema.yml b/core/modules/link/config/schema/link.schema.yml index 4c25a8f..fd2edb1 100644 --- a/core/modules/link/config/schema/link.schema.yml +++ b/core/modules/link/config/schema/link.schema.yml @@ -66,18 +66,18 @@ field.value.link: mapping: query: type: sequence - label: 'Url query key value pairs' + label: 'URL query key value pairs' sequence: type: string fragment: type: string - label: 'Url fragment' + label: 'URL fragment' absolute: type: boolean - label: 'Is this Url absolute' + label: 'Is this URL absolute' https: type: boolean - label: 'If the Url should use a secure protocol' + label: 'If the URL should use a secure protocol' attributes: type: sequence label: 'Link attributes' diff --git a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php index 96064a1..9245ba3 100644 --- a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php +++ b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php @@ -207,7 +207,7 @@ public function viewElements(FieldItemListInterface $items, $langcode) { // field template wrapper, and set the URL value in a content // attribute. // @todo Does RDF need a URL rather than an internal URI here? - // @see \Drupal\rdf\Tests\Field\LinkFieldRdfaTest. + // @see \Drupal\Tests\rdf\Kernel\Field\LinkFieldRdfaTest. $content = str_replace('internal:/', '', $item->uri); $item->_attributes += array('content' => $content); } diff --git a/core/modules/link/src/Plugin/migrate/cckfield/LinkField.php b/core/modules/link/src/Plugin/migrate/cckfield/LinkField.php index 327cbda..6f65254 100644 --- a/core/modules/link/src/Plugin/migrate/cckfield/LinkField.php +++ b/core/modules/link/src/Plugin/migrate/cckfield/LinkField.php @@ -7,7 +7,7 @@ namespace Drupal\link\Plugin\migrate\cckfield; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate_drupal\Plugin\migrate\cckfield\CckFieldPluginBase; /** diff --git a/core/modules/link/src/Plugin/migrate/process/d6/CckLink.php b/core/modules/link/src/Plugin/migrate/process/d6/CckLink.php index bdd9822..e0d107a 100644 --- a/core/modules/link/src/Plugin/migrate/process/d6/CckLink.php +++ b/core/modules/link/src/Plugin/migrate/process/d6/CckLink.php @@ -8,7 +8,7 @@ namespace Drupal\link\Plugin\migrate\process\d6; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\ProcessPluginBase; use Drupal\migrate\Row; diff --git a/core/modules/link/src/Tests/LinkFieldTest.php b/core/modules/link/src/Tests/LinkFieldTest.php index 79c2cbb..2d00cfa 100644 --- a/core/modules/link/src/Tests/LinkFieldTest.php +++ b/core/modules/link/src/Tests/LinkFieldTest.php @@ -10,8 +10,11 @@ use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Unicode; use Drupal\Core\Url; +use Drupal\entity_test\Entity\EntityTest; +use Drupal\field\Entity\FieldConfig; use Drupal\link\LinkItemInterface; use Drupal\simpletest\WebTestBase; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests link field widgets and formatters. @@ -57,20 +60,20 @@ protected function setUp() { function testURLValidation() { $field_name = Unicode::strtolower($this->randomMachineName()); // Create a field with settings to validate. - $this->fieldStorage = entity_create('field_storage_config', array( + $this->fieldStorage = FieldStorageConfig::create(array( 'field_name' => $field_name, 'entity_type' => 'entity_test', 'type' => 'link', )); $this->fieldStorage->save(); - $this->field = entity_create('field_config', array( + $this->field = FieldConfig::create([ 'field_storage' => $this->fieldStorage, 'bundle' => 'entity_test', 'settings' => array( 'title' => DRUPAL_DISABLED, 'link_type' => LinkItemInterface::LINK_GENERIC, ), - )); + ]); $this->field->save(); entity_get_form_display('entity_test', 'entity_test', 'default') ->setComponent($field_name, array( @@ -97,6 +100,12 @@ function testURLValidation() { // Create a node to test the link widget. $node = $this->drupalCreateNode(); + // Create an entity with restricted view access. + $entity_test_no_label_access = EntityTest::create([ + 'name' => 'forbid_access', + ]); + $entity_test_no_label_access->save(); + // Define some valid URLs (keys are the entered values, values are the // strings displayed to the user). $valid_external_entries = array( @@ -130,7 +139,7 @@ function testURLValidation() { // Entity URI displayed as ER autocomplete value when displayed in a form. 'entity:node/1' => $node->label() . ' (1)', // URI for an entity that exists, but is not accessible by the user. - 'entity:user/1' => '- Restricted access - (1)', + 'entity:entity_test/' . $entity_test_no_label_access->id() => '- Restricted access - (' . $entity_test_no_label_access->id() . ')', // URI for an entity that doesn't exist, but with a valid ID. 'entity:user/999999' => 'entity:user/999999', ); @@ -222,13 +231,13 @@ protected function assertInvalidEntries($field_name, array $invalid_entries) { function testLinkTitle() { $field_name = Unicode::strtolower($this->randomMachineName()); // Create a field with settings to validate. - $this->fieldStorage = entity_create('field_storage_config', array( + $this->fieldStorage = FieldStorageConfig::create(array( 'field_name' => $field_name, 'entity_type' => 'entity_test', 'type' => 'link', )); $this->fieldStorage->save(); - $this->field = entity_create('field_config', array( + $this->field = FieldConfig::create([ 'field_storage' => $this->fieldStorage, 'bundle' => 'entity_test', 'label' => 'Read more about this entity', @@ -236,7 +245,7 @@ function testLinkTitle() { 'title' => DRUPAL_OPTIONAL, 'link_type' => LinkItemInterface::LINK_GENERIC, ), - )); + ]); $this->field->save(); entity_get_form_display('entity_test', 'entity_test', 'default') ->setComponent($field_name, array( @@ -336,14 +345,14 @@ function testLinkTitle() { function testLinkFormatter() { $field_name = Unicode::strtolower($this->randomMachineName()); // Create a field with settings to validate. - $this->fieldStorage = entity_create('field_storage_config', array( + $this->fieldStorage = FieldStorageConfig::create(array( 'field_name' => $field_name, 'entity_type' => 'entity_test', 'type' => 'link', 'cardinality' => 2, )); $this->fieldStorage->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_storage' => $this->fieldStorage, 'label' => 'Read more about this entity', 'bundle' => 'entity_test', @@ -351,7 +360,7 @@ function testLinkFormatter() { 'title' => DRUPAL_OPTIONAL, 'link_type' => LinkItemInterface::LINK_GENERIC, ), - ))->save(); + ])->save(); entity_get_form_display('entity_test', 'entity_test', 'default') ->setComponent($field_name, array( 'type' => 'link_default', @@ -476,21 +485,21 @@ function testLinkFormatter() { function testLinkSeparateFormatter() { $field_name = Unicode::strtolower($this->randomMachineName()); // Create a field with settings to validate. - $this->fieldStorage = entity_create('field_storage_config', array( + $this->fieldStorage = FieldStorageConfig::create(array( 'field_name' => $field_name, 'entity_type' => 'entity_test', 'type' => 'link', 'cardinality' => 2, )); $this->fieldStorage->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_storage' => $this->fieldStorage, 'bundle' => 'entity_test', 'settings' => array( 'title' => DRUPAL_OPTIONAL, 'link_type' => LinkItemInterface::LINK_GENERIC, ), - ))->save(); + ])->save(); $display_options = array( 'type' => 'link_separate', 'label' => 'hidden', diff --git a/core/modules/link/src/Tests/LinkItemTest.php b/core/modules/link/src/Tests/LinkItemTest.php deleted file mode 100644 index 1115983..0000000 --- a/core/modules/link/src/Tests/LinkItemTest.php +++ /dev/null @@ -1,168 +0,0 @@ - 'field_test', - 'entity_type' => 'entity_test', - 'type' => 'link', - ])->save(); - FieldConfig::create([ - 'entity_type' => 'entity_test', - 'field_name' => 'field_test', - 'bundle' => 'entity_test', - 'settings' => ['link_type' => LinkItemInterface::LINK_GENERIC], - ])->save(); - FieldStorageConfig::create([ - 'field_name' => 'field_test_external', - 'entity_type' => 'entity_test', - 'type' => 'link', - ])->save(); - FieldConfig::create([ - 'entity_type' => 'entity_test', - 'field_name' => 'field_test_external', - 'bundle' => 'entity_test', - 'settings' => ['link_type' => LinkItemInterface::LINK_EXTERNAL], - ])->save(); - FieldStorageConfig::create([ - 'field_name' => 'field_test_internal', - 'entity_type' => 'entity_test', - 'type' => 'link', - ])->save(); - FieldConfig::create([ - 'entity_type' => 'entity_test', - 'field_name' => 'field_test_internal', - 'bundle' => 'entity_test', - 'settings' => ['link_type' => LinkItemInterface::LINK_INTERNAL], - ])->save(); - } - - /** - * Tests using entity fields of the link field type. - */ - public function testLinkItem() { - // Create entity. - $entity = entity_create('entity_test'); - $url = 'https://www.drupal.org?test_param=test_value'; - $parsed_url = UrlHelper::parse($url); - $title = $this->randomMachineName(); - $class = $this->randomMachineName(); - $entity->field_test->uri = $parsed_url['path']; - $entity->field_test->title = $title; - $entity->field_test->first()->get('options')->set('query', $parsed_url['query']); - $entity->field_test->first()->get('options')->set('attributes', array('class' => $class)); - $entity->name->value = $this->randomMachineName(); - $entity->save(); - - // Verify that the field value is changed. - $id = $entity->id(); - $entity = entity_load('entity_test', $id); - $this->assertTrue($entity->field_test instanceof FieldItemListInterface, 'Field implements interface.'); - $this->assertTrue($entity->field_test[0] instanceof FieldItemInterface, 'Field item implements interface.'); - $this->assertEqual($entity->field_test->uri, $parsed_url['path']); - $this->assertEqual($entity->field_test[0]->uri, $parsed_url['path']); - $this->assertEqual($entity->field_test->title, $title); - $this->assertEqual($entity->field_test[0]->title, $title); - $this->assertEqual($entity->field_test->options['attributes']['class'], $class); - $this->assertEqual($entity->field_test->options['query'], $parsed_url['query']); - - // Update only the entity name property to check if the link field data will - // remain intact. - $entity->name->value = $this->randomMachineName(); - $entity->save(); - $id = $entity->id(); - $entity = entity_load('entity_test', $id); - $this->assertEqual($entity->field_test->uri, $parsed_url['path']); - $this->assertEqual($entity->field_test->options['attributes']['class'], $class); - $this->assertEqual($entity->field_test->options['query'], $parsed_url['query']); - - // Verify changing the field value. - $new_url = 'https://www.drupal.org'; - $new_title = $this->randomMachineName(); - $new_class = $this->randomMachineName(); - $entity->field_test->uri = $new_url; - $entity->field_test->title = $new_title; - $entity->field_test->first()->get('options')->set('query', NULL); - $entity->field_test->first()->get('options')->set('attributes', array('class' => $new_class)); - $this->assertEqual($entity->field_test->uri, $new_url); - $this->assertEqual($entity->field_test->title, $new_title); - $this->assertEqual($entity->field_test->options['attributes']['class'], $new_class); - $this->assertNull($entity->field_test->options['query']); - - // Read changed entity and assert changed values. - $entity->save(); - $entity = entity_load('entity_test', $id); - $this->assertEqual($entity->field_test->uri, $new_url); - $this->assertEqual($entity->field_test->title, $new_title); - $this->assertEqual($entity->field_test->options['attributes']['class'], $new_class); - - // Check that if we only set uri the default values for title and options - // are also initialized. - $entity->field_test = ['uri' => 'internal:/node/add']; - $this->assertEqual($entity->field_test->uri, 'internal:/node/add'); - $this->assertNull($entity->field_test->title); - $this->assertIdentical($entity->field_test->options, []); - - // Check that if set uri and serialize options then the default values are - // properly initialized. - $entity->field_test = [ - 'uri' => 'internal:/node/add', - 'options' => serialize(['query' => NULL]), - ]; - $this->assertEqual($entity->field_test->uri, 'internal:/node/add'); - $this->assertNull($entity->field_test->title); - $this->assertNull($entity->field_test->options['query']); - - // Check that if we set the direct value of link field it correctly set the - // uri and the default values of the field. - $entity->field_test = 'internal:/node/add'; - $this->assertEqual($entity->field_test->uri, 'internal:/node/add'); - $this->assertNull($entity->field_test->title); - $this->assertIdentical($entity->field_test->options, []); - - // Check that setting LinkItem value NULL doesn't generate any error or - // warning. - $entity->field_test[0] = NULL; - $this->assertNull($entity->field_test[0]->getValue()); - - // Test the generateSampleValue() method for generic, external, and internal - // link types. - $entity = entity_create('entity_test'); - $entity->field_test->generateSampleItems(); - $entity->field_test_external->generateSampleItems(); - $entity->field_test_internal->generateSampleItems(); - $this->entityValidateAndSave($entity); - } - -} diff --git a/core/modules/link/tests/src/Kernel/LinkItemTest.php b/core/modules/link/tests/src/Kernel/LinkItemTest.php new file mode 100644 index 0000000..342d8ed --- /dev/null +++ b/core/modules/link/tests/src/Kernel/LinkItemTest.php @@ -0,0 +1,169 @@ + 'field_test', + 'entity_type' => 'entity_test', + 'type' => 'link', + ])->save(); + FieldConfig::create([ + 'entity_type' => 'entity_test', + 'field_name' => 'field_test', + 'bundle' => 'entity_test', + 'settings' => ['link_type' => LinkItemInterface::LINK_GENERIC], + ])->save(); + FieldStorageConfig::create([ + 'field_name' => 'field_test_external', + 'entity_type' => 'entity_test', + 'type' => 'link', + ])->save(); + FieldConfig::create([ + 'entity_type' => 'entity_test', + 'field_name' => 'field_test_external', + 'bundle' => 'entity_test', + 'settings' => ['link_type' => LinkItemInterface::LINK_EXTERNAL], + ])->save(); + FieldStorageConfig::create([ + 'field_name' => 'field_test_internal', + 'entity_type' => 'entity_test', + 'type' => 'link', + ])->save(); + FieldConfig::create([ + 'entity_type' => 'entity_test', + 'field_name' => 'field_test_internal', + 'bundle' => 'entity_test', + 'settings' => ['link_type' => LinkItemInterface::LINK_INTERNAL], + ])->save(); + } + + /** + * Tests using entity fields of the link field type. + */ + public function testLinkItem() { + // Create entity. + $entity = EntityTest::create(); + $url = 'https://www.drupal.org?test_param=test_value'; + $parsed_url = UrlHelper::parse($url); + $title = $this->randomMachineName(); + $class = $this->randomMachineName(); + $entity->field_test->uri = $parsed_url['path']; + $entity->field_test->title = $title; + $entity->field_test->first()->get('options')->set('query', $parsed_url['query']); + $entity->field_test->first()->get('options')->set('attributes', array('class' => $class)); + $entity->name->value = $this->randomMachineName(); + $entity->save(); + + // Verify that the field value is changed. + $id = $entity->id(); + $entity = entity_load('entity_test', $id); + $this->assertTrue($entity->field_test instanceof FieldItemListInterface, 'Field implements interface.'); + $this->assertTrue($entity->field_test[0] instanceof FieldItemInterface, 'Field item implements interface.'); + $this->assertEqual($entity->field_test->uri, $parsed_url['path']); + $this->assertEqual($entity->field_test[0]->uri, $parsed_url['path']); + $this->assertEqual($entity->field_test->title, $title); + $this->assertEqual($entity->field_test[0]->title, $title); + $this->assertEqual($entity->field_test->options['attributes']['class'], $class); + $this->assertEqual($entity->field_test->options['query'], $parsed_url['query']); + + // Update only the entity name property to check if the link field data will + // remain intact. + $entity->name->value = $this->randomMachineName(); + $entity->save(); + $id = $entity->id(); + $entity = entity_load('entity_test', $id); + $this->assertEqual($entity->field_test->uri, $parsed_url['path']); + $this->assertEqual($entity->field_test->options['attributes']['class'], $class); + $this->assertEqual($entity->field_test->options['query'], $parsed_url['query']); + + // Verify changing the field value. + $new_url = 'https://www.drupal.org'; + $new_title = $this->randomMachineName(); + $new_class = $this->randomMachineName(); + $entity->field_test->uri = $new_url; + $entity->field_test->title = $new_title; + $entity->field_test->first()->get('options')->set('query', NULL); + $entity->field_test->first()->get('options')->set('attributes', array('class' => $new_class)); + $this->assertEqual($entity->field_test->uri, $new_url); + $this->assertEqual($entity->field_test->title, $new_title); + $this->assertEqual($entity->field_test->options['attributes']['class'], $new_class); + $this->assertNull($entity->field_test->options['query']); + + // Read changed entity and assert changed values. + $entity->save(); + $entity = entity_load('entity_test', $id); + $this->assertEqual($entity->field_test->uri, $new_url); + $this->assertEqual($entity->field_test->title, $new_title); + $this->assertEqual($entity->field_test->options['attributes']['class'], $new_class); + + // Check that if we only set uri the default values for title and options + // are also initialized. + $entity->field_test = ['uri' => 'internal:/node/add']; + $this->assertEqual($entity->field_test->uri, 'internal:/node/add'); + $this->assertNull($entity->field_test->title); + $this->assertIdentical($entity->field_test->options, []); + + // Check that if set uri and serialize options then the default values are + // properly initialized. + $entity->field_test = [ + 'uri' => 'internal:/node/add', + 'options' => serialize(['query' => NULL]), + ]; + $this->assertEqual($entity->field_test->uri, 'internal:/node/add'); + $this->assertNull($entity->field_test->title); + $this->assertNull($entity->field_test->options['query']); + + // Check that if we set the direct value of link field it correctly set the + // uri and the default values of the field. + $entity->field_test = 'internal:/node/add'; + $this->assertEqual($entity->field_test->uri, 'internal:/node/add'); + $this->assertNull($entity->field_test->title); + $this->assertIdentical($entity->field_test->options, []); + + // Check that setting LinkItem value NULL doesn't generate any error or + // warning. + $entity->field_test[0] = NULL; + $this->assertNull($entity->field_test[0]->getValue()); + + // Test the generateSampleValue() method for generic, external, and internal + // link types. + $entity = EntityTest::create(); + $entity->field_test->generateSampleItems(); + $entity->field_test_external->generateSampleItems(); + $entity->field_test_internal->generateSampleItems(); + $this->entityValidateAndSave($entity); + } + +} diff --git a/core/modules/locale/locale.batch.inc b/core/modules/locale/locale.batch.inc index a5ef423..81ca966 100644 --- a/core/modules/locale/locale.batch.inc +++ b/core/modules/locale/locale.batch.inc @@ -33,10 +33,10 @@ * batch is finished. Optional, defaults to TRUE. * - 'use_remote': Whether or not to check the remote translation file. * Optional, defaults to TRUE. - * @param array $context + * @param array|\ArrayAccess $context. * The batch context. */ -function locale_translation_batch_status_check($project, $langcode, array $options, array &$context) { +function locale_translation_batch_status_check($project, $langcode, array $options, &$context) { $failure = $checked = FALSE; $options += array( 'finish_feedback' => TRUE, diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index de43c85..72e8cfe 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -185,10 +185,10 @@ function locale_translate_batch_build(array $files, array $options) { * LOCALE_NOT_CUSTOMIZED. * - 'message': Alternative message to display during import. Note, this must * be sanitized text. - * @param array $context + * @param array|\ArrayAccess $context. * Contains a list of files imported. */ -function locale_translate_batch_import($file, array $options, array &$context) { +function locale_translate_batch_import($file, array $options, &$context) { // Merge the default values in the $options array. $options += array( 'overwrite_options' => array(), @@ -208,7 +208,7 @@ function locale_translate_batch_import($file, array $options, array &$context) { // Update the seek and the number of items in the $options array(). $options['seek'] = $context['sandbox']['parse_state']['seek']; $options['items'] = $context['sandbox']['parse_state']['chunk_size']; - $report = GetText::fileToDatabase($file, $options); + $report = Gettext::fileToDatabase($file, $options); // If not yet finished with reading, mark progress based on size and // position. if ($report['seek'] < filesize($file->uri)) { @@ -268,10 +268,10 @@ function locale_translate_batch_import($file, array $options, array &$context) { * * Save data of imported files. * - * @param array $context + * @param array|\ArrayAccess $context. * Contains a list of imported files. */ -function locale_translate_batch_import_save(array $context) { +function locale_translate_batch_import_save($context) { if (isset($context['results']['files'])) { foreach ($context['results']['files'] as $file) { // Update the file history if both project and version are known. This @@ -293,10 +293,10 @@ function locale_translate_batch_import_save(array $context) { * * Refreshes translations after importing strings. * - * @param array $context + * @param array|\ArrayAccess $context. * Contains a list of strings updated and information about the progress. */ -function locale_translate_batch_refresh(array &$context) { +function locale_translate_batch_refresh(&$context) { if (!isset($context['sandbox']['refresh'])) { $strings = $langcodes = array(); if (isset($context['results']['stats'])) { @@ -597,12 +597,12 @@ function locale_config_batch_build(array $names, array $langcodes, array $option * An array of names of configuration objects to update. * @param array $langcodes * (optional) Array of language codes to update. Defaults to all languages. - * @param array $context + * @param array|\ArrayAccess $context. * Contains a list of files imported. * * @see locale_config_batch_build() */ -function locale_config_batch_refresh_name(array $names, array $langcodes, array &$context) { +function locale_config_batch_refresh_name(array $names, array $langcodes, &$context) { if (!isset($context['result']['stats']['config'])) { $context['result']['stats']['config'] = 0; } diff --git a/core/modules/locale/locale.compare.inc b/core/modules/locale/locale.compare.inc index 2aa6526..e24490c 100644 --- a/core/modules/locale/locale.compare.inc +++ b/core/modules/locale/locale.compare.inc @@ -5,7 +5,6 @@ * The API for comparing project translation status with available translation. */ -use Drupal\Core\Cache; use Drupal\Core\Utility\ProjectInfo; /** diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc index d4776d5..9b2be23 100644 --- a/core/modules/locale/locale.pages.inc +++ b/core/modules/locale/locale.pages.inc @@ -6,7 +6,6 @@ */ use Drupal\Core\Url; -use Drupal\Core\Render\Element; use Symfony\Component\HttpFoundation\RedirectResponse; /** diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml index 1d55460..e37cb48 100644 --- a/core/modules/locale/locale.services.yml +++ b/core/modules/locale/locale.services.yml @@ -5,9 +5,7 @@ services: public: false locale.config_manager: class: Drupal\locale\LocaleConfigManager - arguments: ['@config.storage', '@locale.storage', '@config.factory', '@config.typed', '@language_manager', '@locale.default.config.storage'] - calls: - - [_setConfigManager, ['@config.manager']] + arguments: ['@config.storage', '@locale.storage', '@config.factory', '@config.typed', '@language_manager', '@locale.default.config.storage', '@config.manager'] locale.storage: class: Drupal\locale\StringDatabaseStorage arguments: ['@database'] diff --git a/core/modules/locale/src/LocaleConfigManager.php b/core/modules/locale/src/LocaleConfigManager.php index db6c181..028e75a 100644 --- a/core/modules/locale/src/LocaleConfigManager.php +++ b/core/modules/locale/src/LocaleConfigManager.php @@ -100,12 +100,8 @@ class LocaleConfigManager { * The configuration manager. * * @var \Drupal\Core\Config\ConfigManagerInterface - * - * @internal - * Will be made protected and renamed to $configManager in 8.1.0. - * https://www.drupal.org/node/2628132 */ - private $_configManager; + protected $configManager; /** * Creates a new typed configuration manager. @@ -122,44 +118,17 @@ class LocaleConfigManager { * The language manager. * @param \Drupal\locale\LocaleDefaultConfigStorage $default_config_storage * The locale default configuration storage. + * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager + * The configuration manager. */ - public function __construct(StorageInterface $config_storage, StringStorageInterface $locale_storage, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config, ConfigurableLanguageManagerInterface $language_manager, LocaleDefaultConfigStorage $default_config_storage) { + public function __construct(StorageInterface $config_storage, StringStorageInterface $locale_storage, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config, ConfigurableLanguageManagerInterface $language_manager, LocaleDefaultConfigStorage $default_config_storage, ConfigManagerInterface $config_manager) { $this->configStorage = $config_storage; $this->localeStorage = $locale_storage; $this->configFactory = $config_factory; $this->typedConfigManager = $typed_config; $this->languageManager = $language_manager; $this->defaultConfigStorage = $default_config_storage; - } - - /** - * Sets the configuration manager service. - * - * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager - * - * @internal - * Will be replaced by constructor injection in 8.1.0. - * https://www.drupal.org/node/2628132 - */ - public function _setConfigManager(ConfigManagerInterface $config_manager) { - $this->_configManager = $config_manager; - } - - /** - * Gets the configuration manager service. - * - * @return \Drupal\Core\Config\ConfigManagerInterface - * The config manager - * - * @internal - * Will be replaced by constructor injection in 8.1.0. - * https://www.drupal.org/node/2628132 - */ - private final function _getConfigManager() { - if (!isset($this->_configManager)) { - $this->_configManager = \Drupal::service('config.manager'); - } - return $this->_configManager; + $this->configManager = $config_manager; } /** @@ -524,7 +493,7 @@ public function getDefaultConfigLangcode($name) { // configurable_language entities are a special case since they can be // translated regardless of whether they are shipped if they in the standard // language list. - $config_entity_type = $this->_getConfigManager()->getEntityTypeIdByName($name); + $config_entity_type = $this->configManager->getEntityTypeIdByName($name); if (!$config_entity_type || $config_entity_type === 'configurable_language' || !empty($this->configFactory->get($name)->get('_core.default_config_hash')) ) { diff --git a/core/modules/locale/src/SourceString.php b/core/modules/locale/src/SourceString.php index 48266d2..04f37c6 100644 --- a/core/modules/locale/src/SourceString.php +++ b/core/modules/locale/src/SourceString.php @@ -7,8 +7,6 @@ namespace Drupal\locale; -use Drupal\locale\LocaleString; - /** * Defines the locale source string object. * diff --git a/core/modules/locale/src/StreamWrapper/TranslationsStream.php b/core/modules/locale/src/StreamWrapper/TranslationsStream.php index aa2f985..1333250 100644 --- a/core/modules/locale/src/StreamWrapper/TranslationsStream.php +++ b/core/modules/locale/src/StreamWrapper/TranslationsStream.php @@ -7,7 +7,6 @@ namespace Drupal\locale\StreamWrapper; -use Drupal\Core\Annotation\StreamWrapper; use Drupal\Core\StreamWrapper\LocalStream; use Drupal\Core\StreamWrapper\StreamWrapperInterface; diff --git a/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php b/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php index 376ec44..38e8ad0 100644 --- a/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php +++ b/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php @@ -62,7 +62,6 @@ protected function setUp() { $this->setUpDefaultLanguage(); $this->installSchema('locale', ['locales_source', 'locales_target', 'locales_location']); - $this->installSchema('system', ['queue']); $this->setupLanguages(); diff --git a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php index e03f061..271d804 100644 --- a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php +++ b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php @@ -8,7 +8,7 @@ namespace Drupal\locale\Tests; use Drupal\simpletest\WebTestBase; -use Drupal\core\language\languageInterface; +use Drupal\Core\Language\LanguageInterface; /** * Tests translation of configuration strings. diff --git a/core/modules/locale/src/Tests/LocaleExportTest.php b/core/modules/locale/src/Tests/LocaleExportTest.php index 061c161..6d309d0 100644 --- a/core/modules/locale/src/Tests/LocaleExportTest.php +++ b/core/modules/locale/src/Tests/LocaleExportTest.php @@ -38,8 +38,8 @@ protected function setUp() { $this->drupalLogin($this->adminUser); // Copy test po files to the translations directory. - file_unmanaged_copy(drupal_get_path('module', 'locale') . '/tests/test.de.po', 'translations://', FILE_EXISTS_REPLACE); - file_unmanaged_copy(drupal_get_path('module', 'locale') . '/tests/test.xx.po', 'translations://', FILE_EXISTS_REPLACE); + file_unmanaged_copy(__DIR__ . '/../../tests/test.de.po', 'translations://', FILE_EXISTS_REPLACE); + file_unmanaged_copy(__DIR__ . '/../../tests/test.xx.po', 'translations://', FILE_EXISTS_REPLACE); } /** diff --git a/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php b/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php index 74ef0ae..a78f286 100644 --- a/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php +++ b/core/modules/locale/src/Tests/LocaleImportFunctionalTest.php @@ -46,8 +46,8 @@ protected function setUp() { parent::setUp(); // Copy test po files to the translations directory. - file_unmanaged_copy(drupal_get_path('module', 'locale') . '/tests/test.de.po', 'translations://', FILE_EXISTS_REPLACE); - file_unmanaged_copy(drupal_get_path('module', 'locale') . '/tests/test.xx.po', 'translations://', FILE_EXISTS_REPLACE); + file_unmanaged_copy(__DIR__ . '/../../tests/test.de.po', 'translations://', FILE_EXISTS_REPLACE); + file_unmanaged_copy(__DIR__ . '/../../tests/test.xx.po', 'translations://', FILE_EXISTS_REPLACE); $this->adminUser = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages')); $this->adminUserAccessSiteReports = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages', 'access site reports')); diff --git a/core/modules/locale/src/Tests/LocaleJavascriptTranslationTest.php b/core/modules/locale/src/Tests/LocaleJavascriptTranslationTest.php index a87f866..0e62805 100644 --- a/core/modules/locale/src/Tests/LocaleJavascriptTranslationTest.php +++ b/core/modules/locale/src/Tests/LocaleJavascriptTranslationTest.php @@ -26,7 +26,7 @@ class LocaleJavascriptTranslationTest extends WebTestBase { public static $modules = array('locale', 'locale_test'); public function testFileParsing() { - $filename = drupal_get_path('module', 'locale') . '/tests/locale_test.js'; + $filename = __DIR__ . '/../../tests/locale_test.js'; // Parse the file to look for source strings. _locale_parse_js_file($filename); diff --git a/core/modules/locale/src/Tests/LocaleTranslatedSchemaDefinitionTest.php b/core/modules/locale/src/Tests/LocaleTranslatedSchemaDefinitionTest.php index 5422a10..326cccd 100644 --- a/core/modules/locale/src/Tests/LocaleTranslatedSchemaDefinitionTest.php +++ b/core/modules/locale/src/Tests/LocaleTranslatedSchemaDefinitionTest.php @@ -46,17 +46,17 @@ function testTranslatedSchemaDefinition() { $stringStorage = \Drupal::service('locale.storage'); $source = $stringStorage->createString(array( - 'source' => 'The node ID.', + 'source' => 'Revision ID', ))->save(); $stringStorage->createTranslation(array( 'lid' => $source->lid, 'language' => 'fr', - 'translation' => 'Translated node ID', + 'translation' => 'Translated Revision ID', ))->save(); // Ensure that the field is translated when access through the API. - $this->assertEqual('Translated node ID', \Drupal::entityManager()->getBaseFieldDefinitions('node')['nid']->getDescription()); + $this->assertEqual('Translated Revision ID', \Drupal::entityManager()->getBaseFieldDefinitions('node')['vid']->getLabel()); // Assert there are no updates. $this->assertFalse(\Drupal::service('entity.definition_update_manager')->needsUpdates()); diff --git a/core/modules/locale/src/Tests/LocaleUpdateBase.php b/core/modules/locale/src/Tests/LocaleUpdateBase.php index cc2aed3..78c2b39 100644 --- a/core/modules/locale/src/Tests/LocaleUpdateBase.php +++ b/core/modules/locale/src/Tests/LocaleUpdateBase.php @@ -8,6 +8,7 @@ namespace Drupal\locale\Tests; use Drupal\Core\StreamWrapper\PublicStream; +use Drupal\file\Entity\File; use Drupal\simpletest\WebTestBase; use Drupal\Component\Utility\SafeMarkup; @@ -133,14 +134,14 @@ protected function makePoFile($path, $filename, $timestamp = NULL, array $transl } file_prepare_directory($path, FILE_CREATE_DIRECTORY); - $file = entity_create('file', array( + $file = File::create([ 'uid' => 1, 'filename' => $filename, 'uri' => $path . '/' . $filename, 'filemime' => 'text/x-gettext-translation', 'timestamp' => $timestamp, 'status' => FILE_STATUS_PERMANENT, - )); + ]); file_put_contents($file->getFileUri(), $po_header . $text); touch(drupal_realpath($file->getFileUri()), $timestamp); $file->save(); diff --git a/core/modules/locale/src/Tests/Migrate/MigrateLocaleConfigsTest.php b/core/modules/locale/src/Tests/Migrate/MigrateLocaleConfigsTest.php deleted file mode 100644 index bdf144a..0000000 --- a/core/modules/locale/src/Tests/Migrate/MigrateLocaleConfigsTest.php +++ /dev/null @@ -1,45 +0,0 @@ -executeMigration('locale_settings'); - } - - /** - * Tests migration of locale variables to locale.settings.yml. - */ - public function testLocaleSettings() { - $config = $this->config('locale.settings'); - $this->assertIdentical(TRUE, $config->get('cache_strings')); - $this->assertIdentical('languages', $config->get('javascript.directory')); - $this->assertConfigSchema(\Drupal::service('config.typed'), 'locale.settings', $config->get()); - } - -} diff --git a/core/modules/locale/tests/src/Kernel/Migrate/MigrateLocaleConfigsTest.php b/core/modules/locale/tests/src/Kernel/Migrate/MigrateLocaleConfigsTest.php new file mode 100644 index 0000000..df3513a --- /dev/null +++ b/core/modules/locale/tests/src/Kernel/Migrate/MigrateLocaleConfigsTest.php @@ -0,0 +1,45 @@ +executeMigration('locale_settings'); + } + + /** + * Tests migration of locale variables to locale.settings.yml. + */ + public function testLocaleSettings() { + $config = $this->config('locale.settings'); + $this->assertIdentical(TRUE, $config->get('cache_strings')); + $this->assertIdentical('languages', $config->get('javascript.directory')); + $this->assertConfigSchema(\Drupal::service('config.typed'), 'locale.settings', $config->get()); + } + +} diff --git a/core/modules/menu_link_content/config/schema/menu_link_content.schema.yml b/core/modules/menu_link_content/config/schema/menu_link_content.schema.yml deleted file mode 100644 index bfc93f6..0000000 --- a/core/modules/menu_link_content/config/schema/menu_link_content.schema.yml +++ /dev/null @@ -1,7 +0,0 @@ -migrate.source.menu_link: - type: migrate_source_sql - label: 'Menu link' - mapping: - constants: - type: migrate_entity_constant - label: 'Constants' diff --git a/core/modules/menu_link_content/src/MenuLinkContentAccessControlHandler.php b/core/modules/menu_link_content/src/MenuLinkContentAccessControlHandler.php index d3c0998..276dda0 100644 --- a/core/modules/menu_link_content/src/MenuLinkContentAccessControlHandler.php +++ b/core/modules/menu_link_content/src/MenuLinkContentAccessControlHandler.php @@ -64,7 +64,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter } else { // If there is a URL, this is an external link so always accessible. - $access = AccessResult::allowed()->cachePerPermissions()->cacheUntilEntityChanges($entity); + $access = AccessResult::allowed()->cachePerPermissions()->addCacheableDependency($entity); /** @var \Drupal\menu_link_content\MenuLinkContentInterface $entity */ // We allow access, but only if the link is accessible as well. if (($url_object = $entity->getUrlObject()) && $url_object->isRouted()) { @@ -75,7 +75,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter } case 'delete': - return AccessResult::allowedIf(!$entity->isNew() && $account->hasPermission('administer menu'))->cachePerPermissions()->cacheUntilEntityChanges($entity); + return AccessResult::allowedIf(!$entity->isNew() && $account->hasPermission('administer menu'))->cachePerPermissions()->addCacheableDependency($entity); } } diff --git a/core/modules/menu_link_content/src/Tests/LinksTest.php b/core/modules/menu_link_content/src/Tests/LinksTest.php index 29a8d97..46e3d60 100644 --- a/core/modules/menu_link_content/src/Tests/LinksTest.php +++ b/core/modules/menu_link_content/src/Tests/LinksTest.php @@ -8,7 +8,9 @@ namespace Drupal\menu_link_content\Tests; use Drupal\Component\Utility\SafeMarkup; +use Drupal\menu_link_content\Entity\MenuLinkContent; use Drupal\simpletest\WebTestBase; +use Drupal\system\Entity\Menu; /** * Tests handling of menu links hierarchies. @@ -39,7 +41,7 @@ protected function setUp() { $this->menuLinkManager = \Drupal::service('plugin.manager.menu.link'); - entity_create('menu', array( + Menu::create(array( 'id' => 'menu_test', 'label' => 'Test menu', 'description' => 'Description text', @@ -68,7 +70,7 @@ function createLinkHierarchy($module = 'menu_test') { $parent = $base_options + array( 'link' => ['uri' => 'internal:/menu-test/hierarchy/parent'], ); - $link = entity_create('menu_link_content', $parent); + $link = MenuLinkContent::create($parent); $link->save(); $links['parent'] = $link->getPluginId(); @@ -76,7 +78,7 @@ function createLinkHierarchy($module = 'menu_test') { 'link' => ['uri' => 'internal:/menu-test/hierarchy/parent/child'], 'parent' => $links['parent'], ); - $link = entity_create('menu_link_content', $child_1); + $link = MenuLinkContent::create($child_1); $link->save(); $links['child-1'] = $link->getPluginId(); @@ -84,7 +86,7 @@ function createLinkHierarchy($module = 'menu_test') { 'link' => ['uri' => 'internal:/menu-test/hierarchy/parent/child2/child'], 'parent' => $links['child-1'], ); - $link = entity_create('menu_link_content', $child_1_1); + $link = MenuLinkContent::create($child_1_1); $link->save(); $links['child-1-1'] = $link->getPluginId(); @@ -92,7 +94,7 @@ function createLinkHierarchy($module = 'menu_test') { 'link' => ['uri' => 'internal:/menu-test/hierarchy/parent/child2/child'], 'parent' => $links['child-1'], ); - $link = entity_create('menu_link_content', $child_1_2); + $link = MenuLinkContent::create($child_1_2); $link->save(); $links['child-1-2'] = $link->getPluginId(); @@ -100,7 +102,7 @@ function createLinkHierarchy($module = 'menu_test') { 'link' => ['uri' => 'internal:/menu-test/hierarchy/parent/child'], 'parent' => $links['parent'], ); - $link = entity_create('menu_link_content', $child_2); + $link = MenuLinkContent::create($child_2); $link->save(); $links['child-2'] = $link->getPluginId(); @@ -129,7 +131,7 @@ public function testCreateLink() { 'bundle' => 'menu_link_content', 'link' => [['uri' => 'internal:/']], ); - $link = entity_create('menu_link_content', $options); + $link = MenuLinkContent::create($options); $link->save(); // Make sure the changed timestamp is set. $this->assertEqual($link->getChangedTime(), REQUEST_TIME, 'Creating a menu link sets the "changed" timestamp.'); diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentCacheabilityBubblingTest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentCacheabilityBubblingTest.php index 825f493..9e1dbf1 100644 --- a/core/modules/menu_link_content/src/Tests/MenuLinkContentCacheabilityBubblingTest.php +++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentCacheabilityBubblingTest.php @@ -39,7 +39,6 @@ protected function setUp() { $this->installEntitySchema('menu_link_content'); $this->installEntitySchema('user'); - $this->installSchema('system', ['url_alias', 'router']); // Ensure that the weight of module_link_content is higher than system. // @see menu_link_content_install() diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentDeleteFormTest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentDeleteFormTest.php index c411f83..362c691 100644 --- a/core/modules/menu_link_content/src/Tests/MenuLinkContentDeleteFormTest.php +++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentDeleteFormTest.php @@ -56,6 +56,8 @@ public function testMenuLinkContentDeleteForm() { $this->assertLinkByHref($menu_link->url('edit-form')); \Drupal::service('module_installer')->install(['menu_ui']); + \Drupal::service('router.builder')->rebuild(); + // Make sure cancel URL points to menu_ui route now. $this->drupalGet($menu_link->urlInfo('delete-form')); $menu = Menu::load($menu_link->getMenuName()); diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentDeriverTest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentDeriverTest.php index b5ae7ae..defbaad 100644 --- a/core/modules/menu_link_content/src/Tests/MenuLinkContentDeriverTest.php +++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentDeriverTest.php @@ -7,7 +7,6 @@ namespace Drupal\menu_link_content\Tests; -use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Menu\MenuTreeParameters; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\menu_link_content\Entity\MenuLinkContent; @@ -33,7 +32,6 @@ protected function setUp() { parent::setUp(); $this->installEntitySchema('menu_link_content'); - $this->installSchema('system', 'router'); } /** @@ -73,7 +71,6 @@ public function testRediscover() { $title = $tree_element->link->getTitle(); $this->assertFalse($title instanceof TranslatableMarkup); $this->assertIdentical('', $title); - $this->assertFalse(SafeMarkup::isSafe($title)); // Create a hierarchy. \Drupal::state()->set('menu_link_content_dynamic_route.routes', [ diff --git a/core/modules/menu_link_content/src/Tests/Migrate/MigrateMenuLinkContentStubTest.php b/core/modules/menu_link_content/src/Tests/Migrate/MigrateMenuLinkContentStubTest.php deleted file mode 100644 index 47e94d1..0000000 --- a/core/modules/menu_link_content/src/Tests/Migrate/MigrateMenuLinkContentStubTest.php +++ /dev/null @@ -1,42 +0,0 @@ -installEntitySchema('menu_link_content'); - } - - /** - * Tests creation of menu link content stubs. - */ - public function testStub() { - $this->performStubTest('menu_link_content'); - } - -} diff --git a/core/modules/menu_link_content/src/Tests/Migrate/d6/MigrateMenuLinkTest.php b/core/modules/menu_link_content/src/Tests/Migrate/d6/MigrateMenuLinkTest.php deleted file mode 100644 index 8fc22ef..0000000 --- a/core/modules/menu_link_content/src/Tests/Migrate/d6/MigrateMenuLinkTest.php +++ /dev/null @@ -1,81 +0,0 @@ -installSchema('system', ['router']); - $this->installEntitySchema('menu_link_content'); - $this->executeMigrations(['menu', 'menu_links']); - } - - /** - * Tests migration of menu links. - */ - public function testMenuLinks() { - $menu_link = MenuLinkContent::load(138); - $this->assertIdentical('Test 1', $menu_link->getTitle()); - $this->assertIdentical('secondary-links', $menu_link->getMenuName()); - $this->assertIdentical('Test menu link 1', $menu_link->getDescription()); - $this->assertIdentical(TRUE, $menu_link->isEnabled()); - $this->assertIdentical(FALSE, $menu_link->isExpanded()); - $this->assertIdentical(['attributes' => ['title' => 'Test menu link 1']], $menu_link->link->options); - $this->assertIdentical('internal:/user/login', $menu_link->link->uri); - $this->assertIdentical(-50, $menu_link->getWeight()); - - $menu_link = MenuLinkContent::load(139); - $this->assertIdentical('Test 2', $menu_link->getTitle()); - $this->assertIdentical('secondary-links', $menu_link->getMenuName()); - $this->assertIdentical('Test menu link 2', $menu_link->getDescription()); - $this->assertIdentical(TRUE, $menu_link->isEnabled()); - $this->assertIdentical(TRUE, $menu_link->isExpanded()); - $this->assertIdentical(['query' => 'foo=bar', 'attributes' => ['title' => 'Test menu link 2']], $menu_link->link->options); - $this->assertIdentical('internal:/admin', $menu_link->link->uri); - $this->assertIdentical(-49, $menu_link->getWeight()); - - $menu_link = MenuLinkContent::load(140); - $this->assertIdentical('Drupal.org', $menu_link->getTitle()); - $this->assertIdentical('secondary-links', $menu_link->getMenuName()); - $this->assertIdentical(NULL, $menu_link->getDescription()); - $this->assertIdentical(TRUE, $menu_link->isEnabled()); - $this->assertIdentical(FALSE, $menu_link->isExpanded()); - $this->assertIdentical(['attributes' => ['title' => '']], $menu_link->link->options); - $this->assertIdentical('https://www.drupal.org', $menu_link->link->uri); - $this->assertIdentical(-50, $menu_link->getWeight()); - - // assert that missing title attributes don't stop or break migration. - $menu_link = MenuLinkContent::load(393); - $this->assertIdentical('Test 3', $menu_link->getTitle()); - $this->assertIdentical('secondary-links', $menu_link->getMenuName()); - $this->assertIdentical(NULL, $menu_link->getDescription()); - $this->assertIdentical(TRUE, $menu_link->isEnabled()); - $this->assertIdentical(FALSE, $menu_link->isExpanded()); - $this->assertIdentical([], $menu_link->link->options); - $this->assertIdentical('internal:/user/login', $menu_link->link->uri); - $this->assertIdentical(-47, $menu_link->getWeight()); - } - -} diff --git a/core/modules/menu_link_content/src/Tests/Migrate/d7/MigrateMenuLinkTest.php b/core/modules/menu_link_content/src/Tests/Migrate/d7/MigrateMenuLinkTest.php deleted file mode 100644 index 68c6d7a..0000000 --- a/core/modules/menu_link_content/src/Tests/Migrate/d7/MigrateMenuLinkTest.php +++ /dev/null @@ -1,133 +0,0 @@ -installSchema('system', ['router']); - $this->installEntitySchema('menu_link_content'); - $this->executeMigration('menu'); - \Drupal::service('router.builder')->rebuild(); - } - - /** - * Asserts various aspects of a menu link entity. - * - * @param string $id - * The link ID. - * @param string $title - * The expected title of the link. - * @param string $menu - * The expected ID of the menu to which the link will belong. - * @param string $description - * The link's expected description. - * @param bool $enabled - * Whether the link is enabled. - * @param bool $expanded - * Whether the link is expanded - * @param array $attributes - * Additional attributes the link is expected to have. - * @param string $uri - * The expected URI of the link. - * @param int $weight - * The expected weight of the link. - */ - protected function assertEntity($id, $title, $menu, $description, $enabled, $expanded, array $attributes, $uri, $weight) { - /** @var \Drupal\menu_link_content\MenuLinkContentInterface $menu_link */ - $menu_link = MenuLinkContent::load($id); - $this->assertTrue($menu_link instanceof MenuLinkContentInterface); - $this->assertIdentical($title, $menu_link->getTitle()); - $this->assertIdentical($menu, $menu_link->getMenuName()); - // The migration sets the description of the link to the value of the - // 'title' attribute. Bit strange, but there you go. - $this->assertIdentical($description, $menu_link->getDescription()); - $this->assertIdentical($enabled, $menu_link->isEnabled()); - $this->assertIdentical($expanded, $menu_link->isExpanded()); - $this->assertIdentical($attributes, $menu_link->link->options); - $this->assertIdentical($uri, $menu_link->link->uri); - $this->assertIdentical($weight, $menu_link->getWeight()); - return $menu_link; - } - - /** - * Tests migration of menu links. - */ - public function testMenuLinks() { - $this->executeMigration('menu_links'); - $this->assertEntity(469, 'Bing', static::MENU_NAME, 'Bing', TRUE, FALSE, ['attributes' => ['title' => 'Bing']], 'http://bing.com', 0); - $this->assertEntity(467, 'Google', static::MENU_NAME, 'Google', TRUE, FALSE, ['attributes' => ['title' => 'Google']], 'http://google.com', 0); - $this->assertEntity(468, 'Yahoo', static::MENU_NAME, 'Yahoo', TRUE, FALSE, ['attributes' => ['title' => 'Yahoo']], 'http://yahoo.com', 0); - $menu_link_tree_service = \Drupal::service('menu.link_tree'); - $parameters = new MenuTreeParameters(); - $tree = $menu_link_tree_service->load(static::MENU_NAME, $parameters); - $this->assertEqual(2, count($tree)); - $children = 0; - $google_found = FALSE; - foreach ($tree as $menu_link_tree_element) { - $children += $menu_link_tree_element->hasChildren; - if ($menu_link_tree_element->link->getUrlObject()->toString() == 'http://bing.com') { - $this->assertEqual(reset($menu_link_tree_element->subtree)->link->getUrlObject()->toString(), 'http://google.com'); - $google_found = TRUE; - } - } - $this->assertEqual(1, $children); - $this->assertTrue($google_found); - // Now find the custom link under a system link. - $parameters->root = 'system.admin_structure'; - $tree = $menu_link_tree_service->load(static::MENU_NAME, $parameters); - $found = FALSE; - foreach ($tree as $menu_link_tree_element) { - $this->pass($menu_link_tree_element->link->getUrlObject()->toString()); - if ($menu_link_tree_element->link->getTitle() == 'custom link test') { - $found = TRUE; - break; - } - } - $this->assertTrue($found); - } - - /** - * Tests migrating a link with an undefined title attribute. - */ - public function testUndefinedLinkTitle() { - Database::getConnection('default', 'migrate') - ->update('menu_links') - ->fields(array( - 'options' => 'a:0:{}', - )) - ->condition('mlid', 467) - ->execute(); - - $this->executeMigration('menu_links'); - $this->assertEntity(467, 'Google', static::MENU_NAME, NULL, TRUE, FALSE, [], 'http://google.com', 0); - } - -} diff --git a/core/modules/menu_link_content/src/Tests/PathAliasMenuLinkContentTest.php b/core/modules/menu_link_content/src/Tests/PathAliasMenuLinkContentTest.php index 3cdec3f..7be92e1 100644 --- a/core/modules/menu_link_content/src/Tests/PathAliasMenuLinkContentTest.php +++ b/core/modules/menu_link_content/src/Tests/PathAliasMenuLinkContentTest.php @@ -32,7 +32,6 @@ protected function setUp() { parent::setUp(); $this->installEntitySchema('menu_link_content'); - $this->installSchema('system', ['url_alias', 'router']); // Ensure that the weight of module_link_content is higher than system. // @see menu_link_content_install() diff --git a/core/modules/menu_link_content/tests/src/Kernel/Migrate/MigrateMenuLinkContentStubTest.php b/core/modules/menu_link_content/tests/src/Kernel/Migrate/MigrateMenuLinkContentStubTest.php new file mode 100644 index 0000000..5736bd9 --- /dev/null +++ b/core/modules/menu_link_content/tests/src/Kernel/Migrate/MigrateMenuLinkContentStubTest.php @@ -0,0 +1,42 @@ +installEntitySchema('menu_link_content'); + } + + /** + * Tests creation of menu link content stubs. + */ + public function testStub() { + $this->performStubTest('menu_link_content'); + } + +} diff --git a/core/modules/menu_link_content/tests/src/Kernel/Migrate/d6/MigrateMenuLinkTest.php b/core/modules/menu_link_content/tests/src/Kernel/Migrate/d6/MigrateMenuLinkTest.php new file mode 100644 index 0000000..f043850 --- /dev/null +++ b/core/modules/menu_link_content/tests/src/Kernel/Migrate/d6/MigrateMenuLinkTest.php @@ -0,0 +1,80 @@ +installEntitySchema('menu_link_content'); + $this->executeMigrations(['menu', 'menu_links']); + } + + /** + * Tests migration of menu links. + */ + public function testMenuLinks() { + $menu_link = MenuLinkContent::load(138); + $this->assertIdentical('Test 1', $menu_link->getTitle()); + $this->assertIdentical('secondary-links', $menu_link->getMenuName()); + $this->assertIdentical('Test menu link 1', $menu_link->getDescription()); + $this->assertIdentical(TRUE, $menu_link->isEnabled()); + $this->assertIdentical(FALSE, $menu_link->isExpanded()); + $this->assertIdentical(['attributes' => ['title' => 'Test menu link 1']], $menu_link->link->options); + $this->assertIdentical('internal:/user/login', $menu_link->link->uri); + $this->assertIdentical(-50, $menu_link->getWeight()); + + $menu_link = MenuLinkContent::load(139); + $this->assertIdentical('Test 2', $menu_link->getTitle()); + $this->assertIdentical('secondary-links', $menu_link->getMenuName()); + $this->assertIdentical('Test menu link 2', $menu_link->getDescription()); + $this->assertIdentical(TRUE, $menu_link->isEnabled()); + $this->assertIdentical(TRUE, $menu_link->isExpanded()); + $this->assertIdentical(['query' => 'foo=bar', 'attributes' => ['title' => 'Test menu link 2']], $menu_link->link->options); + $this->assertIdentical('internal:/admin', $menu_link->link->uri); + $this->assertIdentical(-49, $menu_link->getWeight()); + + $menu_link = MenuLinkContent::load(140); + $this->assertIdentical('Drupal.org', $menu_link->getTitle()); + $this->assertIdentical('secondary-links', $menu_link->getMenuName()); + $this->assertIdentical(NULL, $menu_link->getDescription()); + $this->assertIdentical(TRUE, $menu_link->isEnabled()); + $this->assertIdentical(FALSE, $menu_link->isExpanded()); + $this->assertIdentical(['attributes' => ['title' => '']], $menu_link->link->options); + $this->assertIdentical('https://www.drupal.org', $menu_link->link->uri); + $this->assertIdentical(-50, $menu_link->getWeight()); + + // assert that missing title attributes don't stop or break migration. + $menu_link = MenuLinkContent::load(393); + $this->assertIdentical('Test 3', $menu_link->getTitle()); + $this->assertIdentical('secondary-links', $menu_link->getMenuName()); + $this->assertIdentical(NULL, $menu_link->getDescription()); + $this->assertIdentical(TRUE, $menu_link->isEnabled()); + $this->assertIdentical(FALSE, $menu_link->isExpanded()); + $this->assertIdentical([], $menu_link->link->options); + $this->assertIdentical('internal:/user/login', $menu_link->link->uri); + $this->assertIdentical(-47, $menu_link->getWeight()); + } + +} diff --git a/core/modules/menu_link_content/tests/src/Kernel/Migrate/d7/MigrateMenuLinkTest.php b/core/modules/menu_link_content/tests/src/Kernel/Migrate/d7/MigrateMenuLinkTest.php new file mode 100644 index 0000000..2c3aef8 --- /dev/null +++ b/core/modules/menu_link_content/tests/src/Kernel/Migrate/d7/MigrateMenuLinkTest.php @@ -0,0 +1,131 @@ +installEntitySchema('menu_link_content'); + $this->executeMigration('menu'); + \Drupal::service('router.builder')->rebuild(); + } + + /** + * Asserts various aspects of a menu link entity. + * + * @param string $id + * The link ID. + * @param string $title + * The expected title of the link. + * @param string $menu + * The expected ID of the menu to which the link will belong. + * @param string $description + * The link's expected description. + * @param bool $enabled + * Whether the link is enabled. + * @param bool $expanded + * Whether the link is expanded + * @param array $attributes + * Additional attributes the link is expected to have. + * @param string $uri + * The expected URI of the link. + * @param int $weight + * The expected weight of the link. + */ + protected function assertEntity($id, $title, $menu, $description, $enabled, $expanded, array $attributes, $uri, $weight) { + /** @var \Drupal\menu_link_content\MenuLinkContentInterface $menu_link */ + $menu_link = MenuLinkContent::load($id); + $this->assertTrue($menu_link instanceof MenuLinkContentInterface); + $this->assertIdentical($title, $menu_link->getTitle()); + $this->assertIdentical($menu, $menu_link->getMenuName()); + // The migration sets the description of the link to the value of the + // 'title' attribute. Bit strange, but there you go. + $this->assertIdentical($description, $menu_link->getDescription()); + $this->assertIdentical($enabled, $menu_link->isEnabled()); + $this->assertIdentical($expanded, $menu_link->isExpanded()); + $this->assertIdentical($attributes, $menu_link->link->options); + $this->assertIdentical($uri, $menu_link->link->uri); + $this->assertIdentical($weight, $menu_link->getWeight()); + return $menu_link; + } + + /** + * Tests migration of menu links. + */ + public function testMenuLinks() { + $this->executeMigration('menu_links'); + $this->assertEntity(469, 'Bing', static::MENU_NAME, 'Bing', TRUE, FALSE, ['attributes' => ['title' => 'Bing']], 'http://bing.com', 0); + $this->assertEntity(467, 'Google', static::MENU_NAME, 'Google', TRUE, FALSE, ['attributes' => ['title' => 'Google']], 'http://google.com', 0); + $this->assertEntity(468, 'Yahoo', static::MENU_NAME, 'Yahoo', TRUE, FALSE, ['attributes' => ['title' => 'Yahoo']], 'http://yahoo.com', 0); + $menu_link_tree_service = \Drupal::service('menu.link_tree'); + $parameters = new MenuTreeParameters(); + $tree = $menu_link_tree_service->load(static::MENU_NAME, $parameters); + $this->assertEqual(2, count($tree)); + $children = 0; + $google_found = FALSE; + foreach ($tree as $menu_link_tree_element) { + $children += $menu_link_tree_element->hasChildren; + if ($menu_link_tree_element->link->getUrlObject()->toString() == 'http://bing.com') { + $this->assertEqual(reset($menu_link_tree_element->subtree)->link->getUrlObject()->toString(), 'http://google.com'); + $google_found = TRUE; + } + } + $this->assertEqual(1, $children); + $this->assertTrue($google_found); + // Now find the custom link under a system link. + $parameters->root = 'system.admin_structure'; + $tree = $menu_link_tree_service->load(static::MENU_NAME, $parameters); + $found = FALSE; + foreach ($tree as $menu_link_tree_element) { + $this->pass($menu_link_tree_element->link->getUrlObject()->toString()); + if ($menu_link_tree_element->link->getTitle() == 'custom link test') { + $found = TRUE; + break; + } + } + $this->assertTrue($found); + } + + /** + * Tests migrating a link with an undefined title attribute. + */ + public function testUndefinedLinkTitle() { + Database::getConnection('default', 'migrate') + ->update('menu_links') + ->fields(array( + 'options' => 'a:0:{}', + )) + ->condition('mlid', 467) + ->execute(); + + $this->executeMigration('menu_links'); + $this->assertEntity(467, 'Google', static::MENU_NAME, NULL, TRUE, FALSE, [], 'http://google.com', 0); + } + +} diff --git a/core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/source/MenuLinkSourceTest.php b/core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/source/MenuLinkSourceTest.php index 9484ca4..72955cb 100644 --- a/core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/source/MenuLinkSourceTest.php +++ b/core/modules/menu_link_content/tests/src/Unit/Plugin/migrate/source/MenuLinkSourceTest.php @@ -4,6 +4,7 @@ * @file * Contains \Drupal\Tests\menu_link_content\Unit\Plugin\migrate\source\MenuLinkSourceTest. */ + namespace Drupal\Tests\menu_link_content\Unit\Plugin\migrate\source; use Drupal\Component\Utility\Unicode; diff --git a/core/modules/menu_ui/menu_ui.admin.js b/core/modules/menu_ui/menu_ui.admin.js index 2bc80dd..345138f 100644 --- a/core/modules/menu_ui/menu_ui.admin.js +++ b/core/modules/menu_ui/menu_ui.admin.js @@ -3,7 +3,7 @@ * Menu UI admin behaviors. */ -(function ($) { +(function ($, Drupal) { 'use strict'; @@ -65,4 +65,4 @@ }); }; -})(jQuery); +})(jQuery, Drupal); diff --git a/core/modules/menu_ui/menu_ui.install b/core/modules/menu_ui/menu_ui.install deleted file mode 100644 index a79e503..0000000 --- a/core/modules/menu_ui/menu_ui.install +++ /dev/null @@ -1,25 +0,0 @@ -rebuild(); -} - -/** - * Implements hook_uninstall(). - */ -function menu_ui_uninstall() { - \Drupal::service('router.builder')->setRebuildNeeded(); -} diff --git a/core/modules/menu_ui/menu_ui.js b/core/modules/menu_ui/menu_ui.js index 113b90a..5eb10e0 100644 --- a/core/modules/menu_ui/menu_ui.js +++ b/core/modules/menu_ui/menu_ui.js @@ -3,7 +3,7 @@ * Menu UI behaviors. */ -(function ($) { +(function ($, Drupal) { 'use strict'; @@ -88,4 +88,4 @@ } }; -})(jQuery); +})(jQuery, Drupal); diff --git a/core/modules/menu_ui/menu_ui.module b/core/modules/menu_ui/menu_ui.module index 8a5cb9e..463f8ca 100644 --- a/core/modules/menu_ui/menu_ui.module +++ b/core/modules/menu_ui/menu_ui.module @@ -15,7 +15,6 @@ use Drupal\Core\Link; use Drupal\Core\Menu\MenuLinkInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\Element; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\menu_link_content\Entity\MenuLinkContent; use Drupal\node\NodeTypeInterface; @@ -150,7 +149,7 @@ function _menu_ui_node_save(NodeInterface $node, array $values) { } else { // Create a new menu_link_content entity. - $entity = entity_create('menu_link_content', array( + $entity = MenuLinkContent::create(array( 'link' => ['uri' => 'entity:node/' . $node->id()], 'langcode' => $node->language()->getId(), )); diff --git a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php index 520627e..5e8e691 100644 --- a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php +++ b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php @@ -8,7 +8,9 @@ namespace Drupal\menu_ui\Tests; use Drupal\Core\Url; +use Drupal\menu_link_content\Entity\MenuLinkContent; use Drupal\system\Tests\Cache\PageCacheTagsTestBase; +use Drupal\system\Entity\Menu; /** * Tests the Menu and Menu Link entities' cache tags. @@ -32,7 +34,7 @@ public function testMenuBlock() { $url = Url::fromRoute('test_page_test.test_page'); // Create a Llama menu, add a link to it and place the corresponding block. - $menu = entity_create('menu', array( + $menu = Menu::create(array( 'id' => 'llama', 'label' => 'Llama', 'description' => 'Description text', @@ -79,7 +81,7 @@ public function testMenuBlock() { // Verify that after adding a menu link, there is a cache miss. $this->pass('Test addition of menu link.', 'Debug'); - $menu_link_2 = entity_create('menu_link_content', array( + $menu_link_2 = MenuLinkContent::create(array( 'id' => '', 'parent' => '', 'title' => 'Alpaca', diff --git a/core/modules/menu_ui/src/Tests/MenuNodeTest.php b/core/modules/menu_ui/src/Tests/MenuNodeTest.php index 9521836..0022d58 100644 --- a/core/modules/menu_ui/src/Tests/MenuNodeTest.php +++ b/core/modules/menu_ui/src/Tests/MenuNodeTest.php @@ -204,7 +204,7 @@ function testMenuNodeFormWidget() { $this->assertNoLink($node_title); // Add a menu link to the Administration menu. - $item = entity_create('menu_link_content', array( + $item = MenuLinkContent::create(array( 'link' => [['uri' => 'entity:node/' . $node->id()]], 'title' => $this->randomMachineName(16), 'menu_name' => 'admin', @@ -226,7 +226,7 @@ function testMenuNodeFormWidget() { // Create a second node. $child_node = $this->drupalCreateNode(array('type' => 'article')); // Assign a menu link to the second node, being a child of the first one. - $child_item = entity_create('menu_link_content', array( + $child_item = MenuLinkContent::create(array( 'link' => [['uri' => 'entity:node/' . $child_node->id()]], 'title' => $this->randomMachineName(16), 'parent' => $item->getPluginId(), diff --git a/core/modules/menu_ui/src/Tests/MenuTest.php b/core/modules/menu_ui/src/Tests/MenuTest.php index d88911a..7e3e62d 100644 --- a/core/modules/menu_ui/src/Tests/MenuTest.php +++ b/core/modules/menu_ui/src/Tests/MenuTest.php @@ -157,7 +157,7 @@ function addCustomMenuCRUD() { $menu_name = substr(hash('sha256', $this->randomMachineName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI); $label = $this->randomMachineName(16); - $menu = entity_create('menu', array( + $menu = Menu::create(array( 'id' => $menu_name, 'label' => $label, 'description' => 'Description text', @@ -298,7 +298,7 @@ function doMenuTests() { // Verify add link button. $this->drupalGet('admin/structure/menu'); - $this->assertLinkByHref('admin/structure/menu/manage/' . $menu_name . '/add', 0, "The add menu link button url is correct"); + $this->assertLinkByHref('admin/structure/menu/manage/' . $menu_name . '/add', 0, "The add menu link button URL is correct"); // Verify form defaults. $this->doMenuLinkFormDefaultsTest(); diff --git a/core/modules/menu_ui/src/Tests/Migrate/MigrateMenuSettingsTest.php b/core/modules/menu_ui/src/Tests/Migrate/MigrateMenuSettingsTest.php deleted file mode 100644 index c1655c6..0000000 --- a/core/modules/menu_ui/src/Tests/Migrate/MigrateMenuSettingsTest.php +++ /dev/null @@ -1,34 +0,0 @@ -installConfig(['menu_ui']); - $this->executeMigration('menu_settings'); - } - - public function testMigration() { - $this->assertTrue(\Drupal::config('menu_ui.settings')->get('override_parent_selector')); - } - -} diff --git a/core/modules/menu_ui/tests/tests/src/Kernel/Migrate/MigrateMenuSettingsTest.php b/core/modules/menu_ui/tests/tests/src/Kernel/Migrate/MigrateMenuSettingsTest.php new file mode 100644 index 0000000..297384c --- /dev/null +++ b/core/modules/menu_ui/tests/tests/src/Kernel/Migrate/MigrateMenuSettingsTest.php @@ -0,0 +1,34 @@ +installConfig(['menu_ui']); + $this->executeMigration('menu_settings'); + } + + public function testMigration() { + $this->assertTrue(\Drupal::config('menu_ui.settings')->get('override_parent_selector')); + } + +} diff --git a/core/modules/migrate/config/schema/migrate.data_types.schema.yml b/core/modules/migrate/config/schema/migrate.data_types.schema.yml deleted file mode 100644 index 27c8006..0000000 --- a/core/modules/migrate/config/schema/migrate.data_types.schema.yml +++ /dev/null @@ -1,40 +0,0 @@ -# Basic data types for Migrate. - -migrate_plugin: - type: mapping - mapping: - plugin: - type: string - label: 'Plugin' - -migrate_destination: - type: migrate_plugin - label: 'Destination' - mapping: - overwrite_properties: - type: sequence - label: 'Properties to overwrite' - sequence: - type: string - label: 'Property' - -migrate_source: - type: migrate_plugin - label: 'Source' - mapping: - constants: - type: ignore - label: 'Constants' - -migrate_process: - type: migrate_plugin - label: 'Process' - -# Base schema for migrate source plugins that extend -# \Drupal\migrate\Plugin\migrate\source\SqlBase. -migrate_source_sql: - type: migrate_source - mapping: - target: - type: string - label: 'The migration database target' diff --git a/core/modules/migrate/config/schema/migrate.destination.schema.yml b/core/modules/migrate/config/schema/migrate.destination.schema.yml deleted file mode 100644 index b295741..0000000 --- a/core/modules/migrate/config/schema/migrate.destination.schema.yml +++ /dev/null @@ -1,18 +0,0 @@ -# Schema for the migrate destination plugins. - -migrate.destination.*: - type: migrate_destination - label: 'Default destination' - mapping: - no_stub: - type: boolean - label: 'Whether stubbing is allowed.' - default: false - -migrate.destination.config: - type: migrate_destination - label: 'Config' - mapping: - config_name: - type: string - label: 'Configuration name' diff --git a/core/modules/migrate/config/schema/migrate.process.schema.yml b/core/modules/migrate/config/schema/migrate.process.schema.yml deleted file mode 100644 index a82ff63..0000000 --- a/core/modules/migrate/config/schema/migrate.process.schema.yml +++ /dev/null @@ -1,145 +0,0 @@ -# Schema for the migrate process plugins. - -migrate.process.*: - type: migrate_process - label: 'Default process' - -migrate.process.callback: - type: migrate_process - label: 'Callback process' - mapping: - callback: - type: string - label: 'Callback' - -migrate.process.concat: - type: migrate_process - label: 'Concat process' - mapping: - delimiter: - type: string - label: 'Delimiter' - -migrate.process.dedupe_entity: - type: migrate_process - label: 'Dedupe Entity process' - mapping: - entity_type: - type: string - label: 'Entity type' - field: - type: string - label: 'Field name' - postfix: - type: string - label: 'Postfix' - start: - type: integer - label: 'Start' - length: - type: integer - label: 'Length' - -migrate.process.explode: - type: migrate_process - label: 'Explode process' - mapping: - delimiter: - type: string - label: 'Delimiter' - limit: - type: integer - label: 'Limit' - -migrate.process.extract: - type: migrate_process - label: 'Extract process' - mapping: - default: - type: string - label: 'Default value' - -migrate.process.flatten: - type: migrate_process - label: 'Flatten process' - -migrate.process.get: - type: migrate_process - label: 'Get process' - mapping: - source: - type: string - label: 'Source key' - -migrate.process.iterator: - type: migrate_process - label: 'Iterator process' - mapping: - process: - type: ignore - label: 'Process' - key: - type: string - label: 'Key' - -migrate.process.machine_name: - type: migrate_process - label: 'Machine name process' - -migrate.process.migration: - type: migrate_process - label: 'Migration process' - mapping: - migration: - type: sequence - label: 'Migration' - source: - type: sequence - label: 'Source keys' - source_ids: - type: string - label: 'Source IDs' - stub_id: - type: string - label: 'Stub ID' - -migrate.process.route: - type: migrate_process - label: 'Route process' - -migrate.process.skip_on_empty: - type: migrate_process - label: 'Skip on Empty' - -migrate.process.skip_row_if_not_set: - type: migrate_process - label: 'Skip Row process if not set' - mapping: - index: - type: integer - label: 'Index' - -migrate.process.static_map: - type: migrate_process - label: 'Static Map' - mapping: - map: - type: sequence - label: 'Map' - default_value: - type: string - label: 'Default value' - bypass: - type: boolean - label: 'Bypass lookup' - -migrate.process.default_value: - type: migrate_process - label: 'Default value' - mapping: - strict: - type: boolean - label: 'Strict type check' - default_value: - type: string - label: 'Default value' diff --git a/core/modules/migrate/config/schema/migrate.schema.yml b/core/modules/migrate/config/schema/migrate.schema.yml deleted file mode 100644 index fcadda9..0000000 --- a/core/modules/migrate/config/schema/migrate.schema.yml +++ /dev/null @@ -1,46 +0,0 @@ -# Schema for the configuration files of the Migrate module. - -migrate.migration.*: - type: config_entity - label: 'Migration' - mapping: - id: - type: string - label: 'ID' - migration_tags: - type: sequence - label: 'Migration Tags' - sequence: - type: string - label: 'Tag' - label: - type: label - label: 'Label' - source: - type: migrate.source.[plugin] - label: 'Source' - process: - type: ignore - label: 'Process' - destination: - type: migrate.destination.[plugin] - label: 'Destination' - template: - type: string - label: 'Template' - migration_dependencies: - type: mapping - label: 'Dependencies' - mapping: - required: - type: sequence - label: 'Required dependencies' - sequence: - type: string - label: 'Dependency' - optional: - type: sequence - label: 'Optional dependencies' - sequence: - type: string - label: 'Dependency' diff --git a/core/modules/migrate/config/schema/migrate.source.schema.yml b/core/modules/migrate/config/schema/migrate.source.schema.yml deleted file mode 100644 index bb6ae96..0000000 --- a/core/modules/migrate/config/schema/migrate.source.schema.yml +++ /dev/null @@ -1,34 +0,0 @@ -# Schema for the migrate source plugins. - -migrate.source.*: - type: migrate_source - label: 'Default source' - -migrate.source.empty: - type: migrate_source_sql - label: 'Empty source' - mapping: - provider: - type: string - label: 'Provider' - -migrate.source.embedded_data: - type: migrate_source - label: 'Embedded data source' - mapping: - data_rows: - type: sequence - label: 'Data rows' - sequence: - type: ignore - label: 'Data row' - ids: - type: sequence - label: 'Unique key' - sequence: - type: mapping - label: 'Key column' - mapping: - type: - type: string - label: 'Column type' diff --git a/core/modules/migrate/migrate.api.php b/core/modules/migrate/migrate.api.php index df5c66d..fa55b7d 100644 --- a/core/modules/migrate/migrate.api.php +++ b/core/modules/migrate/migrate.api.php @@ -5,7 +5,7 @@ * Hooks provided by the Migrate module. */ -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\MigrateSourceInterface; use Drupal\migrate\Row; diff --git a/core/modules/migrate/migrate.services.yml b/core/modules/migrate/migrate.services.yml index 40270f6..da43b38 100644 --- a/core/modules/migrate/migrate.services.yml +++ b/core/modules/migrate/migrate.services.yml @@ -5,12 +5,6 @@ services: - { name: cache.bin } factory: cache_factory:get arguments: [migrate] - migrate.template_storage: - class: Drupal\migrate\MigrateTemplateStorage - arguments: ['@module_handler'] - migrate.migration_builder: - class: Drupal\migrate\MigrationBuilder - arguments: ['@plugin.manager.migrate.builder'] plugin.manager.migrate.source: class: Drupal\migrate\Plugin\MigratePluginManager arguments: [source, '@container.namespaces', '@cache.discovery', '@module_handler', 'Drupal\migrate\Annotation\MigrateSource'] @@ -23,6 +17,6 @@ services: plugin.manager.migrate.id_map: class: Drupal\migrate\Plugin\MigratePluginManager arguments: [id_map, '@container.namespaces', '@cache.discovery', '@module_handler'] - plugin.manager.migrate.builder: - class: Drupal\migrate\Plugin\MigratePluginManager - arguments: [builder, '@container.namespaces', '@cache.discovery', '@module_handler'] + plugin.manager.migration: + class: Drupal\migrate\Plugin\MigrationPluginManager + arguments: ['@module_handler', '@cache.discovery', '@language_manager'] diff --git a/core/modules/migrate/src/Annotation/MigrateDestination.php b/core/modules/migrate/src/Annotation/MigrateDestination.php index 3837370..d06e161 100644 --- a/core/modules/migrate/src/Annotation/MigrateDestination.php +++ b/core/modules/migrate/src/Annotation/MigrateDestination.php @@ -48,13 +48,4 @@ class MigrateDestination extends Plugin { */ public $requirements_met = TRUE; - /** - * A class to make the plugin derivative aware. - * - * @var string - * - * @see \Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator - */ - public $derivative; - } diff --git a/core/modules/migrate/src/Entity/Migration.php b/core/modules/migrate/src/Entity/Migration.php deleted file mode 100644 index 9e509f4..0000000 --- a/core/modules/migrate/src/Entity/Migration.php +++ /dev/null @@ -1,598 +0,0 @@ - array( - * // An array of migration IDs that must be run before this migration. - * ), - * 'optional' => array( - * // An array of migration IDs that, if they exist, must be run before - * // this migration. - * ), - * ); - * @endcode - * - * @var array - */ - protected $migration_dependencies = []; - - /** - * The migration's configuration dependencies. - * - * These store any dependencies on modules or other configuration (including - * other migrations) that must be available before the migration can be - * created. - * - * @see \Drupal\Core\Config\Entity\ConfigDependencyManager - * - * @var array - */ - protected $dependencies = []; - - /** - * The ID of the template from which this migration was derived, if any. - * - * @var string|NULL - */ - protected $template; - - /** - * The entity manager. - * - * @var \Drupal\Core\Entity\EntityManagerInterface - */ - protected $entityManager; - - /** - * Labels corresponding to each defined status. - * - * @var array - */ - protected $statusLabels = [ - self::STATUS_IDLE => 'Idle', - self::STATUS_IMPORTING => 'Importing', - self::STATUS_ROLLING_BACK => 'Rolling back', - self::STATUS_STOPPING => 'Stopping', - self::STATUS_DISABLED => 'Disabled', - ]; - - /** - * {@inheritdoc} - */ - public function getSourcePlugin() { - if (!isset($this->sourcePlugin)) { - $this->sourcePlugin = \Drupal::service('plugin.manager.migrate.source')->createInstance($this->source['plugin'], $this->source, $this); - } - return $this->sourcePlugin; - } - - /** - * {@inheritdoc} - */ - public function getProcessPlugins(array $process = NULL) { - if (!isset($process)) { - $process = $this->process; - } - $index = serialize($process); - if (!isset($this->processPlugins[$index])) { - $this->processPlugins[$index] = array(); - foreach ($this->getProcessNormalized($process) as $property => $configurations) { - $this->processPlugins[$index][$property] = array(); - foreach ($configurations as $configuration) { - if (isset($configuration['source'])) { - $this->processPlugins[$index][$property][] = \Drupal::service('plugin.manager.migrate.process')->createInstance('get', $configuration, $this); - } - // Get is already handled. - if ($configuration['plugin'] != 'get') { - $this->processPlugins[$index][$property][] = \Drupal::service('plugin.manager.migrate.process')->createInstance($configuration['plugin'], $configuration, $this); - } - if (!$this->processPlugins[$index][$property]) { - throw new MigrateException("Invalid process configuration for $property"); - } - } - } - } - return $this->processPlugins[$index]; - } - - /** - * Resolve shorthands into a list of plugin configurations. - * - * @param array $process - * A process configuration array. - * - * @return array - * The normalized process configuration. - */ - protected function getProcessNormalized(array $process) { - $normalized_configurations = array(); - foreach ($process as $destination => $configuration) { - if (is_string($configuration)) { - $configuration = array( - 'plugin' => 'get', - 'source' => $configuration, - ); - } - if (isset($configuration['plugin'])) { - $configuration = array($configuration); - } - $normalized_configurations[$destination] = $configuration; - } - return $normalized_configurations; - } - - /** - * {@inheritdoc} - */ - public function getDestinationPlugin($stub_being_requested = FALSE) { - if ($stub_being_requested && !empty($this->destination['no_stub'])) { - throw new MigrateSkipRowException; - } - if (!isset($this->destinationPlugin)) { - $this->destinationPlugin = \Drupal::service('plugin.manager.migrate.destination')->createInstance($this->destination['plugin'], $this->destination, $this); - } - return $this->destinationPlugin; - } - - /** - * {@inheritdoc} - */ - public function getIdMap() { - if (!isset($this->idMapPlugin)) { - $configuration = $this->idMap; - $plugin = isset($configuration['plugin']) ? $configuration['plugin'] : 'sql'; - $this->idMapPlugin = \Drupal::service('plugin.manager.migrate.id_map')->createInstance($plugin, $configuration, $this); - } - return $this->idMapPlugin; - } - - /** - * Get the high water storage object. - * - * @return \Drupal\Core\KeyValueStore\KeyValueStoreInterface - * The storage object. - */ - protected function getHighWaterStorage() { - if (!isset($this->highWaterStorage)) { - $this->highWaterStorage = \Drupal::keyValue('migrate:high_water'); - } - return $this->highWaterStorage; - } - - /** - * {@inheritdoc} - */ - public function getHighWater() { - return $this->getHighWaterStorage()->get($this->id()); - } - - /** - * {@inheritdoc} - */ - public function saveHighWater($high_water) { - $this->getHighWaterStorage()->set($this->id(), $high_water); - } - - /** - * {@inheritdoc} - */ - public function checkRequirements() { - // Check whether the current migration source and destination plugin - // requirements are met or not. - if ($this->getSourcePlugin() instanceof RequirementsInterface) { - $this->getSourcePlugin()->checkRequirements(); - } - if ($this->getDestinationPlugin() instanceof RequirementsInterface) { - $this->getDestinationPlugin()->checkRequirements(); - } - - /** @var \Drupal\migrate\Entity\MigrationInterface[] $required_migrations */ - $required_migrations = $this->getEntityManager()->getStorage('migration')->loadMultiple($this->requirements); - - $missing_migrations = array_diff($this->requirements, array_keys($required_migrations)); - // Check if the dependencies are in good shape. - foreach ($required_migrations as $migration_id => $required_migration) { - if (!$required_migration->allRowsProcessed()) { - $missing_migrations[] = $migration_id; - } - } - if ($missing_migrations) { - throw new RequirementsException('Missing migrations ' . implode(', ', $missing_migrations) . '.', ['requirements' => $missing_migrations]); - } - } - - /** - * Get the entity manager. - * - * @return \Drupal\Core\Entity\EntityManagerInterface - * The entity manager. - */ - protected function getEntityManager() { - if (!isset($this->entityManager)) { - $this->entityManager = \Drupal::entityManager(); - } - return $this->entityManager; - } - - /** - * {@inheritdoc} - */ - public function setStatus($status) { - \Drupal::keyValue('migrate_status')->set($this->id(), $status); - } - - /** - * {@inheritdoc} - */ - public function getStatus() { - return \Drupal::keyValue('migrate_status')->get($this->id(), static::STATUS_IDLE); - } - - /** - * {@inheritdoc} - */ - public function getStatusLabel() { - $status = $this->getStatus(); - if (isset($this->statusLabels[$status])) { - return $this->statusLabels[$status]; - } - else { - return ''; - } - } - - /** - * {@inheritdoc} - */ - public function getInterruptionResult() { - return \Drupal::keyValue('migrate_interruption_result')->get($this->id(), static::RESULT_INCOMPLETE); - } - - /** - * {@inheritdoc} - */ - public function clearInterruptionResult() { - \Drupal::keyValue('migrate_interruption_result')->delete($this->id()); - } - - /** - * {@inheritdoc} - */ - public function interruptMigration($result) { - $this->setStatus(MigrationInterface::STATUS_STOPPING); - \Drupal::keyValue('migrate_interruption_result')->set($this->id(), $result); - } - - /** - * {@inheritdoc} - */ - public function allRowsProcessed() { - $source_count = $this->getSourcePlugin()->count(); - // If the source is uncountable, we have no way of knowing if it's - // complete, so stipulate that it is. - if ($source_count < 0) { - return TRUE; - } - $processed_count = $this->getIdMap()->processedCount(); - // We don't use == because in some circumstances (like unresolved stubs - // being created), the processed count may be higher than the available - // source rows. - return $source_count <= $processed_count; - } - - /** - * {@inheritdoc} - */ - public function set($property_name, $value) { - if ($property_name == 'source') { - // Invalidate the source plugin. - unset($this->sourcePlugin); - } - elseif ($property_name === 'destination') { - // Invalidate the destination plugin. - unset($this->destinationPlugin); - } - return parent::set($property_name, $value); - } - - - /** - * {@inheritdoc} - */ - public function getProcess() { - return $this->getProcessNormalized($this->process); - } - - /** - * {@inheritdoc} - */ - public function setProcess(array $process) { - $this->process = $process; - return $this; - } - - /** - * {@inheritdoc} - */ - public function setProcessOfProperty($property, $process_of_property) { - $this->process[$property] = $process_of_property; - return $this; - } - - /** - * {@inheritdoc} - */ - public function mergeProcessOfProperty($property, array $process_of_property) { - // If we already have a process value then merge the incoming process array - //otherwise simply set it. - $current_process = $this->getProcess(); - if (isset($current_process[$property])) { - $this->process = NestedArray::mergeDeepArray([$current_process, $this->getProcessNormalized([$property => $process_of_property])], TRUE); - } - else { - $this->setProcessOfProperty($property, $process_of_property); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function getSystemOfRecord() { - return $this->systemOfRecord; - } - - /** - * {@inheritdoc} - */ - public function setSystemOfRecord($system_of_record) { - $this->systemOfRecord = $system_of_record; - return $this; - } - - /** - * {@inheritdoc} - */ - public function isTrackLastImported() { - return $this->trackLastImported; - } - - /** - * {@inheritdoc} - */ - public function setTrackLastImported($track_last_imported) { - $this->trackLastImported = (bool) $track_last_imported; - return $this; - } - - /** - * {@inheritdoc} - */ - public function getMigrationDependencies() { - return $this->migration_dependencies + ['required' => [], 'optional' => []]; - } - - /** - * {@inheritdoc} - */ - public function trustData() { - // Migrations cannot be trusted since they are often written by hand and not - // through a UI. - $this->trustedData = FALSE; - return $this; - } - - /** - * {@inheritdoc} - */ - public function calculateDependencies() { - parent::calculateDependencies(); - $this->calculatePluginDependencies($this->getSourcePlugin()); - $this->calculatePluginDependencies($this->getDestinationPlugin()); - - // Add hard dependencies on required migrations. - $dependencies = $this->getEntityManager()->getStorage($this->entityTypeId) - ->getVariantIds($this->getMigrationDependencies()['required']); - foreach ($dependencies as $dependency) { - $this->addDependency('config', $this->getEntityType()->getConfigPrefix() . '.' . $dependency); - } - - return $this; - } -} diff --git a/core/modules/migrate/src/Entity/MigrationInterface.php b/core/modules/migrate/src/Entity/MigrationInterface.php deleted file mode 100644 index a07c0ea..0000000 --- a/core/modules/migrate/src/Entity/MigrationInterface.php +++ /dev/null @@ -1,316 +0,0 @@ -handleException($e); } } - if ($high_water_property = $this->migration->get('highWaterProperty')) { + if ($high_water_property = $this->migration->getHighWaterProperty()) { $this->migration->saveHighWater($row->getSourceProperty($high_water_property['name'])); } diff --git a/core/modules/migrate/src/MigrateExecutableInterface.php b/core/modules/migrate/src/MigrateExecutableInterface.php index 71fa017..9a150f3 100644 --- a/core/modules/migrate/src/MigrateExecutableInterface.php +++ b/core/modules/migrate/src/MigrateExecutableInterface.php @@ -7,7 +7,7 @@ namespace Drupal\migrate; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; interface MigrateExecutableInterface { diff --git a/core/modules/migrate/src/MigrateTemplateStorage.php b/core/modules/migrate/src/MigrateTemplateStorage.php deleted file mode 100644 index 8467ad6..0000000 --- a/core/modules/migrate/src/MigrateTemplateStorage.php +++ /dev/null @@ -1,87 +0,0 @@ -moduleHandler = $module_handler; - $this->directory = $directory; - } - - /** - * {@inheritdoc} - */ - public function findTemplatesByTag($tag) { - $templates = $this->getAllTemplates(); - $matched_templates = []; - foreach ($templates as $template_name => $template) { - if (!empty($template['migration_tags'])) { - if (in_array($tag, $template['migration_tags'])) { - $matched_templates[$template_name] = $template; - } - } - } - return $matched_templates; - } - - /** - * {@inheritdoc} - */ - public function getTemplateByName($name) { - $templates = $this->getAllTemplates(); - return isset($templates[$name]) ? $templates[$name] : NULL; - } - - /** - * {@inheritdoc} - */ - public function getAllTemplates() { - $templates = []; - foreach ($this->moduleHandler->getModuleDirectories() as $directory) { - $full_directory = $directory . '/' . $this->directory; - if (file_exists($full_directory)) { - $files = scandir($full_directory); - foreach ($files as $file) { - if ($file[0] !== '.' && preg_match('/\.yml$/', $file)) { - $templates[basename($file, '.yml')] = Yaml::decode(file_get_contents("$full_directory/$file")); - } - } - } - } - - return $templates; - } - -} diff --git a/core/modules/migrate/src/MigrateTemplateStorageInterface.php b/core/modules/migrate/src/MigrateTemplateStorageInterface.php deleted file mode 100644 index dc9372e..0000000 --- a/core/modules/migrate/src/MigrateTemplateStorageInterface.php +++ /dev/null @@ -1,45 +0,0 @@ -builderManager = $builder_manager; - } - - /** - * {@inheritdoc} - */ - public function createMigrations(array $templates) { - /** @var \Drupal\migrate\Entity\MigrationInterface[] $migrations */ - $migrations = []; - - foreach ($templates as $template_id => $template) { - if (isset($template['builder'])) { - $variants = $this->builderManager - ->createInstance($template['builder']['plugin'], $template['builder']) - ->buildMigrations($template); - } - else { - $variants = array(Migration::create($template)); - } - - /** @var \Drupal\migrate\Entity\MigrationInterface[] $variants */ - foreach ($variants as $variant) { - $variant->set('template', $template_id); - } - $migrations = array_merge($migrations, $variants); - } - - return $migrations; - } - -} diff --git a/core/modules/migrate/src/MigrationBuilderInterface.php b/core/modules/migrate/src/MigrationBuilderInterface.php deleted file mode 100644 index 66f3078..0000000 --- a/core/modules/migrate/src/MigrationBuilderInterface.php +++ /dev/null @@ -1,27 +0,0 @@ -getVariantIds($ids); } - /** @var \Drupal\migrate\Entity\MigrationInterface[] $migrations */ + /** @var \Drupal\migrate\Plugin\MigrationInterface[] $migrations */ $migrations = parent::loadMultiple($ids); foreach ($migrations as $migration) { @@ -126,7 +126,7 @@ public function buildDependencyMigration(array $migrations, array $dynamic_ids) $requirement_graph = array(); $different = FALSE; foreach ($migrations as $migration) { - /** @var \Drupal\migrate\Entity\MigrationInterface $migration */ + /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ $id = $migration->id(); $requirements[$id] = array(); $dependency_graph[$id]['edges'] = array(); diff --git a/core/modules/migrate/src/Plugin/MigrateBuilderInterface.php b/core/modules/migrate/src/Plugin/MigrateBuilderInterface.php deleted file mode 100644 index 511af1b..0000000 --- a/core/modules/migrate/src/Plugin/MigrateBuilderInterface.php +++ /dev/null @@ -1,31 +0,0 @@ - array( + * // An array of migration IDs that must be run before this migration. + * ), + * 'optional' => array( + * // An array of migration IDs that, if they exist, must be run before + * // this migration. + * ), + * ); + * @endcode + * + * @var array + */ + protected $migration_dependencies = []; + + /** + * The migration's configuration dependencies. + * + * These store any dependencies on modules or other configuration (including + * other migrations) that must be available before the migration can be + * created. + * + * @see \Drupal\Core\Config\Entity\ConfigDependencyManager + * + * @var array + */ + protected $dependencies = []; + + /** + * The migration plugin manager for loading other migration plugins. + * + * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface + */ + protected $migrationPluginManager; + + /** + * The source plugin manager. + * + * @var \Drupal\migrate\Plugin\MigratePluginManager + */ + protected $sourcePluginManager; + + /** + * Thep process plugin manager. + * + * @var \Drupal\migrate\Plugin\MigratePluginManager + */ + protected $processPluginManager; + + /** + * The destination plugin manager. + * + * @var \Drupal\migrate\Plugin\MigrateDestinationPluginManager + */ + protected $destinationPluginManager; + + /** + * The ID map plugin manager. + * + * @var \Drupal\migrate\Plugin\MigratePluginManager + */ + protected $idMapPluginManager; + + /** + * Labels corresponding to each defined status. + * + * @var array + */ + protected $statusLabels = [ + self::STATUS_IDLE => 'Idle', + self::STATUS_IMPORTING => 'Importing', + self::STATUS_ROLLING_BACK => 'Rolling back', + self::STATUS_STOPPING => 'Stopping', + self::STATUS_DISABLED => 'Disabled', + ]; + + /** + * Constructs a Migration. + * + * @param array $configuration + * Plugin configuration. + * @param string $plugin_id + * The plugin ID. + * @param mixed $plugin_definition + * The plugin definition. + * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager + * The migration plugin manager. + * @param \Drupal\migrate\Plugin\MigratePluginManager $source_plugin_manager + * The source migration plugin manager. + * @param \Drupal\migrate\Plugin\MigratePluginManager $process_plugin_manager + * The process migration plugin manager. + * @param \Drupal\migrate\Plugin\MigrateDestinationPluginManager $destination_plugin_manager + * The destination migration plugin manager. + * @param \Drupal\migrate\Plugin\MigratePluginManager $idmap_plugin_manager + * The ID map migration plugin manager. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationPluginManagerInterface $migration_plugin_manager, MigratePluginManager $source_plugin_manager, MigratePluginManager $process_plugin_manager, MigrateDestinationPluginManager $destination_plugin_manager, MigratePluginManager $idmap_plugin_manager) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->migrationPluginManager = $migration_plugin_manager; + $this->sourcePluginManager = $source_plugin_manager; + $this->processPluginManager = $process_plugin_manager; + $this->destinationPluginManager = $destination_plugin_manager; + $this->idMapPluginManager = $idmap_plugin_manager; + + foreach ($plugin_definition as $key => $value) { + $this->$key = $value; + } + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('plugin.manager.migration'), + $container->get('plugin.manager.migrate.source'), + $container->get('plugin.manager.migrate.process'), + $container->get('plugin.manager.migrate.destination'), + $container->get('plugin.manager.migrate.id_map') + ); + } + + /** + * {@inheritdoc} + */ + public function id() { + return $this->pluginId; + } + + /** + * {@inheritdoc} + */ + public function label() { + return $this->label; + } + + /** + * Gets any arbitrary property's value. + * + * @param string $property + * The property to retrieve. + * + * @return mixed + * The value for that property, or NULL if the property does not exist. + * + * @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.x. Use + * more specific getters instead. + */ + public function get($property) { + return isset($this->$property) ? $this->$property : NULL; + } + + /** + * Retrieves the ID map plugin. + * + * @return \Drupal\migrate\Plugin\MigrateIdMapInterface + * The ID map plugin. + */ + public function getIdMapPlugin() { + return $this->idMapPlugin; + } + + /** + * {@inheritdoc} + */ + public function getSourcePlugin() { + if (!isset($this->sourcePlugin)) { + $this->sourcePlugin = $this->sourcePluginManager->createInstance($this->source['plugin'], $this->source, $this); + } + return $this->sourcePlugin; + } + + /** + * {@inheritdoc} + */ + public function getProcessPlugins(array $process = NULL) { + if (!isset($process)) { + $process = $this->getProcess(); + } + $index = serialize($process); + if (!isset($this->processPlugins[$index])) { + $this->processPlugins[$index] = array(); + foreach ($this->getProcessNormalized($process) as $property => $configurations) { + $this->processPlugins[$index][$property] = array(); + foreach ($configurations as $configuration) { + if (isset($configuration['source'])) { + $this->processPlugins[$index][$property][] = $this->processPluginManager->createInstance('get', $configuration, $this); + } + // Get is already handled. + if ($configuration['plugin'] != 'get') { + $this->processPlugins[$index][$property][] = $this->processPluginManager->createInstance($configuration['plugin'], $configuration, $this); + } + if (!$this->processPlugins[$index][$property]) { + throw new MigrateException("Invalid process configuration for $property"); + } + } + } + } + return $this->processPlugins[$index]; + } + + /** + * Resolve shorthands into a list of plugin configurations. + * + * @param array $process + * A process configuration array. + * + * @return array + * The normalized process configuration. + */ + protected function getProcessNormalized(array $process) { + $normalized_configurations = array(); + foreach ($process as $destination => $configuration) { + if (is_string($configuration)) { + $configuration = array( + 'plugin' => 'get', + 'source' => $configuration, + ); + } + if (isset($configuration['plugin'])) { + $configuration = array($configuration); + } + $normalized_configurations[$destination] = $configuration; + } + return $normalized_configurations; + } + + /** + * {@inheritdoc} + */ + public function getDestinationPlugin($stub_being_requested = FALSE) { + if ($stub_being_requested && !empty($this->destination['no_stub'])) { + throw new MigrateSkipRowException; + } + if (!isset($this->destinationPlugin)) { + $this->destinationPlugin = $this->destinationPluginManager->createInstance($this->destination['plugin'], $this->destination, $this); + } + return $this->destinationPlugin; + } + + /** + * {@inheritdoc} + */ + public function getIdMap() { + if (!isset($this->idMapPlugin)) { + $configuration = $this->idMap; + $plugin = isset($configuration['plugin']) ? $configuration['plugin'] : 'sql'; + $this->idMapPlugin = $this->idMapPluginManager->createInstance($plugin, $configuration, $this); + } + return $this->idMapPlugin; + } + + /** + * Get the high water storage object. + * + * @return \Drupal\Core\KeyValueStore\KeyValueStoreInterface + * The storage object. + */ + protected function getHighWaterStorage() { + if (!isset($this->highWaterStorage)) { + $this->highWaterStorage = \Drupal::keyValue('migrate:high_water'); + } + return $this->highWaterStorage; + } + + /** + * {@inheritdoc} + */ + public function getHighWater() { + return $this->getHighWaterStorage()->get($this->id()); + } + + /** + * {@inheritdoc} + */ + public function saveHighWater($high_water) { + $this->getHighWaterStorage()->set($this->id(), $high_water); + } + + /** + * {@inheritdoc} + */ + public function checkRequirements() { + // Check whether the current migration source and destination plugin + // requirements are met or not. + if ($this->getSourcePlugin() instanceof RequirementsInterface) { + $this->getSourcePlugin()->checkRequirements(); + } + if ($this->getDestinationPlugin() instanceof RequirementsInterface) { + $this->getDestinationPlugin()->checkRequirements(); + } + + if (empty($this->requirements)) { + // There are no requirements to check. + return; + } + /** @var \Drupal\migrate\Plugin\MigrationInterface[] $required_migrations */ + $required_migrations = $this->getMigrationPluginManager()->createInstances($this->requirements); + + $missing_migrations = array_diff($this->requirements, array_keys($required_migrations)); + // Check if the dependencies are in good shape. + foreach ($required_migrations as $migration_id => $required_migration) { + if (!$required_migration->allRowsProcessed()) { + $missing_migrations[] = $migration_id; + } + } + if ($missing_migrations) { + throw new RequirementsException('Missing migrations ' . implode(', ', $missing_migrations) . '.', ['requirements' => $missing_migrations]); + } + } + + /** + * Gets the migration plugin manager. + * + * @return \Drupal\migrate\Plugin\MigratePluginManager + * The plugin manager. + */ + protected function getMigrationPluginManager() { + return $this->migrationPluginManager; + } + + /** + * {@inheritdoc} + */ + public function setStatus($status) { + \Drupal::keyValue('migrate_status')->set($this->id(), $status); + } + + /** + * {@inheritdoc} + */ + public function getStatus() { + return \Drupal::keyValue('migrate_status')->get($this->id(), static::STATUS_IDLE); + } + + /** + * {@inheritdoc} + */ + public function getStatusLabel() { + $status = $this->getStatus(); + if (isset($this->statusLabels[$status])) { + return $this->statusLabels[$status]; + } + else { + return ''; + } + } + + /** + * {@inheritdoc} + */ + public function getInterruptionResult() { + return \Drupal::keyValue('migrate_interruption_result')->get($this->id(), static::RESULT_INCOMPLETE); + } + + /** + * {@inheritdoc} + */ + public function clearInterruptionResult() { + \Drupal::keyValue('migrate_interruption_result')->delete($this->id()); + } + + /** + * {@inheritdoc} + */ + public function interruptMigration($result) { + $this->setStatus(MigrationInterface::STATUS_STOPPING); + \Drupal::keyValue('migrate_interruption_result')->set($this->id(), $result); + } + + /** + * {@inheritdoc} + */ + public function allRowsProcessed() { + $source_count = $this->getSourcePlugin()->count(); + // If the source is uncountable, we have no way of knowing if it's + // complete, so stipulate that it is. + if ($source_count < 0) { + return TRUE; + } + $processed_count = $this->getIdMap()->processedCount(); + // We don't use == because in some circumstances (like unresolved stubs + // being created), the processed count may be higher than the available + // source rows. + return $source_count <= $processed_count; + } + + /** + * {@inheritdoc} + */ + public function set($property_name, $value) { + if ($property_name == 'source') { + // Invalidate the source plugin. + unset($this->sourcePlugin); + } + elseif ($property_name === 'destination') { + // Invalidate the destination plugin. + unset($this->destinationPlugin); + } + $this->{$property_name} = $value; + return $this; + } + + + /** + * {@inheritdoc} + */ + public function getProcess() { + return $this->getProcessNormalized($this->process); + } + + /** + * {@inheritdoc} + */ + public function setProcess(array $process) { + $this->process = $process; + return $this; + } + + /** + * {@inheritdoc} + */ + public function setProcessOfProperty($property, $process_of_property) { + $this->process[$property] = $process_of_property; + return $this; + } + + /** + * {@inheritdoc} + */ + public function mergeProcessOfProperty($property, array $process_of_property) { + // If we already have a process value then merge the incoming process array + //otherwise simply set it. + $current_process = $this->getProcess(); + if (isset($current_process[$property])) { + $this->process = NestedArray::mergeDeepArray([$current_process, $this->getProcessNormalized([$property => $process_of_property])], TRUE); + } + else { + $this->setProcessOfProperty($property, $process_of_property); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getSystemOfRecord() { + return $this->systemOfRecord; + } + + /** + * {@inheritdoc} + */ + public function setSystemOfRecord($system_of_record) { + $this->systemOfRecord = $system_of_record; + return $this; + } + + /** + * {@inheritdoc} + */ + public function isTrackLastImported() { + return $this->trackLastImported; + } + + /** + * {@inheritdoc} + */ + public function setTrackLastImported($track_last_imported) { + $this->trackLastImported = (bool) $track_last_imported; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getMigrationDependencies() { + return $this->migration_dependencies + ['required' => [], 'optional' => []]; + } + + /** + * {@inheritdoc} + */ + public function getPluginDefinition() { + $definition = []; + // While normal plugins do not change their definitions on the fly, this + // one does so accommodate for that. + foreach (parent::getPluginDefinition() as $key => $value) { + $definition[$key] = isset($this->$key) ? $this->$key : $value; + } + return $definition; + } + + /** + * {@inheritdoc} + */ + public function getDestinationConfiguration() { + return $this->destination; + } + + /** + * {@inheritdoc} + */ + public function getSourceConfiguration() { + return $this->source; + } + + /** + * {@inheritdoc} + */ + public function getHighWaterProperty() { + return $this->highWaterProperty; + } + + /** + * {@inheritdoc} + */ + public function getTrackLastImported() { + $this->trackLastImported; + } + + /** + * {@inheritdoc} + */ + public function getDestinationIds() { + $this->destinationIds; + } +} diff --git a/core/modules/migrate/src/Plugin/MigrationDeriverTrait.php b/core/modules/migrate/src/Plugin/MigrationDeriverTrait.php new file mode 100644 index 0000000..b10f19c --- /dev/null +++ b/core/modules/migrate/src/Plugin/MigrationDeriverTrait.php @@ -0,0 +1,37 @@ + [ + 'ignore_map' => TRUE, + 'plugin' => $source_plugin_id, + ], + 'destination' => [ + 'plugin' => 'null', + ], + ]; + return \Drupal::service('plugin.manager.migration')->createStubMigration($definition)->getSourcePlugin(); + } + +} diff --git a/core/modules/migrate/src/Plugin/MigrationInterface.php b/core/modules/migrate/src/Plugin/MigrationInterface.php new file mode 100644 index 0000000..d870f74 --- /dev/null +++ b/core/modules/migrate/src/Plugin/MigrationInterface.php @@ -0,0 +1,382 @@ + '\Drupal\migrate\Plugin\Migration', + ); + + /** + * The interface the plugins should implement. + * + * @var string + */ + protected $pluginInterface = 'Drupal\migrate\Plugin\MigrationInterface'; + + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + /** + * Construct a migration plugin manager. + * + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler. + * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend + * The cache backend for the definitions. + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + * The language manager. + */ + public function __construct(ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager) { + $this->factory = new ContainerFactory($this, $this->pluginInterface); + $this->alterInfo('migration_plugins'); + $this->setCacheBackend($cache_backend, 'migration_plugins', array('migration_plugins')); + $this->moduleHandler = $module_handler; + } + + /** + * {@inheritdoc} + */ + protected function getDiscovery() { + if (!isset($this->discovery)) { + $directories = array_map(function($directory) { + return [$directory . '/migration_templates', $directory . '/migrations']; + }, $this->moduleHandler->getModuleDirectories()); + + $yaml_discovery = new YamlDirectoryDiscovery($directories, 'migrate'); + $this->discovery = new ContainerDerivativeDiscoveryDecorator($yaml_discovery); + } + return $this->discovery; + } + + /** + * {@inheritdoc} + */ + public function createInstance($plugin_id, array $configuration = array()) { + $instances = $this->createInstances([$plugin_id], $configuration); + return reset($instances); + } + + /** + * {@inheritdoc} + */ + public function createInstances($migration_id, array $configuration = array()) { + if (empty($migration_id)) { + $migration_id = array_keys($this->getDefinitions()); + } + + $factory = $this->getFactory(); + $migration_ids = (array) $migration_id; + $plugin_ids = $this->expandPluginIds($migration_ids); + + $instances = []; + foreach ($plugin_ids as $plugin_id) { + $instances[$plugin_id] = $factory->createInstance($plugin_id, isset($configuration[$plugin_id]) ? $configuration[$plugin_id] : []); + } + + foreach ($instances as $migration) { + $migration->set('migration_dependencies', array_map([$this, 'expandPluginIds'], $migration->getMigrationDependencies())); + } + + // Sort the migrations based on their dependencies. + return $this->buildDependencyMigration($instances, []); + } + + /** + * Create migrations given a tag. + * + * @param string $tag + * A migration tag we want to filter by. + * + * @return array|\Drupal\migrate\Plugin\MigrationInterface[] + * An array of migration objects with the given tag. + */ + public function createInstancesByTag($tag) { + $migrations = array_filter($this->getDefinitions(), function($migration) use ($tag) { + return !empty($migration['migration_tags']) && in_array($tag, $migration['migration_tags']); + }); + return $this->createInstances(array_keys($migrations)); + } + + /** + * Expand derivative migration dependencies. + * + * We need to expand any derivative migrations. Derivative migrations are + * calculated by migration derivers such as D6NodeDeriver. This allows + * migrations to depend on the base id and then have a dependency on all + * derivative migrations. For example, d6_comment depends on d6_node but after + * we've expanded the dependencies it will depend on d6_node:page, + * d6_node:story and so on, for other derivative migrations. + * + * @return array + * An array of expanded plugin ids. + */ + protected function expandPluginIds(array $migration_ids) { + $plugin_ids = []; + foreach ($migration_ids as $id) { + $plugin_ids += preg_grep('/^' . preg_quote($id, '/') . PluginBase::DERIVATIVE_SEPARATOR . '/', array_keys($this->getDefinitions())); + if ($this->hasDefinition($id)) { + $plugin_ids[] = $id; + } + } + return $plugin_ids; + } + + + /** + * {@inheritdoc} + */ + public function buildDependencyMigration(array $migrations, array $dynamic_ids) { + // Migration dependencies can be optional or required. If an optional + // dependency does not run, the current migration is still OK to go. Both + // optional and required dependencies (if run at all) must run before the + // current migration. + $dependency_graph = []; + $required_dependency_graph = []; + foreach ($migrations as $migration) { + /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ + $id = $migration->id(); + $requirements[$id] = []; + $dependency_graph[$id]['edges'] = []; + $migration_dependencies = $migration->getMigrationDependencies(); + + if (isset($migration_dependencies['required'])) { + foreach ($migration_dependencies['required'] as $dependency) { + if (!isset($dynamic_ids[$dependency])) { + $this->addDependency($required_dependency_graph, $id, $dependency, $dynamic_ids); + } + $this->addDependency($dependency_graph, $id, $dependency, $dynamic_ids); + } + } + if (isset($migration_dependencies['optional'])) { + foreach ($migration_dependencies['optional'] as $dependency) { + $this->addDependency($dependency_graph, $id, $dependency, $dynamic_ids); + } + } + } + $dependency_graph = (new Graph($dependency_graph))->searchAndSort(); + if (!empty($migration_dependencies['optional'])) { + $required_dependency_graph = (new Graph($required_dependency_graph))->searchAndSort(); + } + else { + $required_dependency_graph = $dependency_graph; + } + $weights = []; + foreach ($migrations as $migration_id => $migration) { + // Populate a weights array to use with array_multisort() later. + $weights[] = $dependency_graph[$migration_id]['weight']; + if (!empty($required_dependency_graph[$migration_id]['paths'])) { + $migration->set('requirements', $required_dependency_graph[$migration_id]['paths']); + } + } + array_multisort($weights, SORT_DESC, SORT_NUMERIC, $migrations); + + return $migrations; + } + + /** + * Add one or more dependencies to a graph. + * + * @param array $graph + * The graph so far, passed by reference. + * @param int $id + * The migration ID. + * @param string $dependency + * The dependency string. + * @param array $dynamic_ids + * The dynamic ID mapping. + */ + protected function addDependency(array &$graph, $id, $dependency, $dynamic_ids) { + $dependencies = isset($dynamic_ids[$dependency]) ? $dynamic_ids[$dependency] : array($dependency); + if (!isset($graph[$id]['edges'])) { + $graph[$id]['edges'] = array(); + } + $graph[$id]['edges'] += array_combine($dependencies, $dependencies); + } + + /** + * {@inheritdoc} + */ + public function createStubMigration(array $definition) { + $id = isset($definition['id']) ? $definition['id'] : uniqid(); + return Migration::create(\Drupal::getContainer(), [], $id, $definition); + } + +} diff --git a/core/modules/migrate/src/Plugin/MigrationPluginManagerInterface.php b/core/modules/migrate/src/Plugin/MigrationPluginManagerInterface.php new file mode 100644 index 0000000..5a78233 --- /dev/null +++ b/core/modules/migrate/src/Plugin/MigrationPluginManagerInterface.php @@ -0,0 +1,48 @@ + uniqid(), - 'source' => $configuration, - // Since this isn't a real migration, we don't want a real destination -- - // the 'null' destination is perfect for this. - 'destination' => [ - 'plugin' => 'null', - ], - ]; - return Migration::create($values)->getSourcePlugin(); - } - -} diff --git a/core/modules/migrate/src/Plugin/migrate/destination/ComponentEntityDisplayBase.php b/core/modules/migrate/src/Plugin/migrate/destination/ComponentEntityDisplayBase.php index dfd1739..00b731f 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/ComponentEntityDisplayBase.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/ComponentEntityDisplayBase.php @@ -7,7 +7,7 @@ namespace Drupal\migrate\Plugin\migrate\destination; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; /** diff --git a/core/modules/migrate/src/Plugin/migrate/destination/Config.php b/core/modules/migrate/src/Plugin/migrate/destination/Config.php index b3c07c9..2af93b2 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/Config.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/Config.php @@ -12,7 +12,7 @@ use Drupal\Core\Entity\DependencyTrait; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -55,7 +55,7 @@ class Config extends DestinationBase implements ContainerFactoryPluginInterface, * The plugin ID for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. - * @param \Drupal\migrate\Entity\MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration entity. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The configuration factory. diff --git a/core/modules/migrate/src/Plugin/migrate/destination/DestinationBase.php b/core/modules/migrate/src/Plugin/migrate/destination/DestinationBase.php index 0a38ba8..7bc05b0 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/DestinationBase.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/DestinationBase.php @@ -8,7 +8,7 @@ namespace Drupal\migrate\Plugin\migrate\destination; use Drupal\Core\Plugin\PluginBase; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Exception\RequirementsException; use Drupal\migrate\Plugin\MigrateDestinationInterface; use Drupal\migrate\Plugin\MigrateIdMapInterface; @@ -43,7 +43,7 @@ /** * The migration. * - * @var \Drupal\migrate\Entity\MigrationInterface + * @var \Drupal\migrate\Plugin\MigrationInterface */ protected $migration; @@ -56,7 +56,7 @@ * The plugin_id for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. - * @param MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration. */ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration) { diff --git a/core/modules/migrate/src/Plugin/migrate/destination/Entity.php b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php index f1df40a..c0528b6 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/Entity.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php @@ -11,7 +11,7 @@ use Drupal\Core\Entity\DependencyTrait; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; use Symfony\Component\DependencyInjection\ContainerInterface; diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php index f23b54f..84e324d 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php @@ -13,7 +13,7 @@ use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; use Drupal\Core\TypedData\TypedDataInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\MigrateException; use Drupal\migrate\Plugin\MigrateIdMapInterface; use Drupal\migrate\Row; @@ -47,7 +47,7 @@ class EntityContentBase extends Entity { * The plugin ID for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. - * @param \Drupal\migrate\Entity\MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration entity. * @param \Drupal\Core\Entity\EntityStorageInterface $storage * The storage for this entity type. diff --git a/core/modules/migrate/src/Plugin/migrate/destination/NullDestination.php b/core/modules/migrate/src/Plugin/migrate/destination/NullDestination.php index f35c5d4..3d4166d 100644 --- a/core/modules/migrate/src/Plugin/migrate/destination/NullDestination.php +++ b/core/modules/migrate/src/Plugin/migrate/destination/NullDestination.php @@ -7,7 +7,7 @@ namespace Drupal\migrate\Plugin\migrate\destination; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; /** diff --git a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php index 4a706bf..04de53b 100644 --- a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php +++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php @@ -11,7 +11,7 @@ use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\PluginBase; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Event\MigrateIdMapMessageEvent; use Drupal\migrate\MigrateException; use Drupal\migrate\MigrateMessageInterface; @@ -83,7 +83,7 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP /** * The migration being done. * - * @var \Drupal\migrate\Entity\MigrationInterface + * @var \Drupal\migrate\Plugin\MigrationInterface */ protected $migration; @@ -154,7 +154,7 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP * The plugin ID for the migration process to do. * @param mixed $plugin_definition * The configuration for the plugin. - * @param \Drupal\migrate\Entity\MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration to do. */ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EventDispatcherInterface $event_dispatcher) { @@ -570,7 +570,7 @@ public function saveIdMapping(Row $row, array $destination_id_values, $source_ro $this->message->display(t('Could not save to map table due to missing destination id values'), 'error'); return; } - if ($this->migration->get('trackLastImported')) { + if ($this->migration->getTrackLastImported()) { $fields['last_imported'] = time(); } $keys = [static::SOURCE_IDS_HASH => $this->getSourceIDsHash($source_id_values)]; diff --git a/core/modules/migrate/src/Plugin/migrate/process/DedupeEntity.php b/core/modules/migrate/src/Plugin/migrate/process/DedupeEntity.php index 175e9c9..f79f0bd 100644 --- a/core/modules/migrate/src/Plugin/migrate/process/DedupeEntity.php +++ b/core/modules/migrate/src/Plugin/migrate/process/DedupeEntity.php @@ -9,7 +9,7 @@ use Drupal\Core\Entity\Query\QueryFactory; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** diff --git a/core/modules/migrate/src/Plugin/migrate/process/MenuLinkParent.php b/core/modules/migrate/src/Plugin/migrate/process/MenuLinkParent.php index 78fbd14..e7befec 100644 --- a/core/modules/migrate/src/Plugin/migrate/process/MenuLinkParent.php +++ b/core/modules/migrate/src/Plugin/migrate/process/MenuLinkParent.php @@ -5,14 +5,13 @@ * Contains \Drupal\migrate\Plugin\migrate\process\MenuLinkContent. */ - namespace Drupal\migrate\Plugin\migrate\process; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Menu\MenuLinkManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Url; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\MigrateSkipRowException; use Drupal\migrate\Plugin\MigrateProcessInterface; diff --git a/core/modules/migrate/src/Plugin/migrate/process/Migration.php b/core/modules/migrate/src/Plugin/migrate/process/Migration.php index 67f5a6a..7ae0fa5 100644 --- a/core/modules/migrate/src/Plugin/migrate/process/Migration.php +++ b/core/modules/migrate/src/Plugin/migrate/process/Migration.php @@ -7,12 +7,13 @@ namespace Drupal\migrate\Plugin\migrate\process; -use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\migrate\MigrateSkipProcessException; use Drupal\migrate\Plugin\MigratePluginManager; +use Drupal\migrate\Plugin\MigrationPluginManagerInterface; +use Drupal\migrate\Plugin\MigrateIdMapInterface; use Drupal\migrate\ProcessPluginBase; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\Row; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -34,18 +35,18 @@ class Migration extends ProcessPluginBase implements ContainerFactoryPluginInter protected $processPluginManager; /** - * The entity storage manager. + * The migration plugin manager. * - * @var \Drupal\Core\Entity\EntityStorageInterface + * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface */ - protected $migrationStorage; + protected $migrationPluginManager; /** * {@inheritdoc} */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, MigratePluginManager $process_plugin_manager) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, MigrationPluginManagerInterface $migration_plugin_manager, MigratePluginManager $process_plugin_manager) { parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->migrationStorage = $storage; + $this->migrationPluginManager = $migration_plugin_manager; $this->migration = $migration; $this->processPluginManager = $process_plugin_manager; } @@ -59,7 +60,7 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_id, $plugin_definition, $migration, - $container->get('entity.manager')->getStorage('migration'), + $container->get('plugin.manager.migration'), $container->get('plugin.manager.migrate.process') ); } @@ -79,10 +80,10 @@ public function transform($value, MigrateExecutableInterface $migrate_executable } $this->skipOnEmpty($value); $self = FALSE; - /** @var \Drupal\migrate\Entity\MigrationInterface[] $migrations */ - $migrations = $this->migrationStorage->loadMultiple($migration_ids); + /** @var \Drupal\migrate\Plugin\MigrationInterface[] $migrations */ $destination_ids = NULL; $source_id_values = array(); + $migrations = $this->migrationPluginManager->createInstances($migration_ids); foreach ($migrations as $migration_id => $migration) { if ($migration_id == $this->migration->id()) { $self = TRUE; @@ -120,7 +121,7 @@ public function transform($value, MigrateExecutableInterface $migrate_executable } $destination_plugin = $migration->getDestinationPlugin(TRUE); // Only keep the process necessary to produce the destination ID. - $process = $migration->get('process'); + $process = $migration->getProcess(); // We already have the source ID values but need to key them for the Row // constructor. @@ -130,7 +131,7 @@ public function transform($value, MigrateExecutableInterface $migrate_executable $values[$source_id] = $source_id_values[$migration->id()][$index]; } - $stub_row = new Row($values + $migration->get('source'), $source_ids, TRUE); + $stub_row = new Row($values + $migration->getSourceConfiguration(), $source_ids, TRUE); // Do a normal migration with the stub row. $migrate_executable->processRow($stub_row, $process); @@ -139,7 +140,11 @@ public function transform($value, MigrateExecutableInterface $migrate_executable $destination_ids = $destination_plugin->import($stub_row); } catch (\Exception $e) { - $migrate_executable->saveMessage($e->getMessage()); + $migration->getIdMap()->saveMessage($stub_row->getSourceIdValues(), $e->getMessage()); + } + + if ($destination_ids) { + $migration->getIdMap()->saveIdMapping($stub_row, $destination_ids, MigrateIdMapInterface::STATUS_NEEDS_UPDATE); } } if ($destination_ids) { diff --git a/core/modules/migrate/src/Plugin/migrate/process/Route.php b/core/modules/migrate/src/Plugin/migrate/process/Route.php index 6fd6722..3f8fa46 100644 --- a/core/modules/migrate/src/Plugin/migrate/process/Route.php +++ b/core/modules/migrate/src/Plugin/migrate/process/Route.php @@ -9,7 +9,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\Core\Path\PathValidatorInterface; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\ProcessPluginBase; diff --git a/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php b/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php index 4b429eb..2b6e910 100644 --- a/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php +++ b/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php @@ -6,7 +6,7 @@ */ namespace Drupal\migrate\Plugin\migrate\source; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; /** * Source which takes its data directly from the plugin config. diff --git a/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php b/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php index d644dc3..8f545ad 100644 --- a/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php +++ b/core/modules/migrate/src/Plugin/migrate/source/SourcePluginBase.php @@ -8,7 +8,7 @@ namespace Drupal\migrate\Plugin\migrate\source; use Drupal\Core\Plugin\PluginBase; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\MigrateException; use Drupal\migrate\MigrateSkipRowException; use Drupal\migrate\Plugin\MigrateIdMapInterface; @@ -37,7 +37,7 @@ /** * The entity migration object. * - * @var \Drupal\migrate\Entity\MigrationInterface + * @var \Drupal\migrate\Plugin\MigrationInterface */ protected $migration; @@ -153,7 +153,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition $this->idMap = $this->migration->getIdMap(); // Pull out the current highwater mark if we have a highwater property. - if ($this->highWaterProperty = $this->migration->get('highWaterProperty')) { + if ($this->highWaterProperty = $this->migration->getHighWaterProperty()) { $this->originalHighWater = $this->migration->getHighWater(); } @@ -300,7 +300,7 @@ public function next() { $row_data = $this->getIterator()->current() + $this->configuration; $this->getIterator()->next(); - $row = new Row($row_data, $this->migration->getSourcePlugin()->getIds(), $this->migration->get('destinationIds')); + $row = new Row($row_data, $this->migration->getSourcePlugin()->getIds(), $this->migration->getDestinationIds()); // Populate the source key for this row. $this->currentSourceIds = $row->getSourceIdValues(); diff --git a/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php b/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php index 93569f4..1c464a62 100644 --- a/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php +++ b/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php @@ -10,7 +10,7 @@ use Drupal\Core\Database\Database; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\State\StateInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\migrate\id_map\Sql; use Drupal\migrate\Plugin\MigrateIdMapInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -91,6 +91,9 @@ public function getDatabase() { if (isset($this->configuration['database_state_key'])) { $this->database = $this->setUpDatabase($this->state->get($this->configuration['database_state_key'])); } + elseif (($fallback_state_key = $this->state->get('migrate.fallback_state_key'))) { + $this->database = $this->setUpDatabase($this->state->get($fallback_state_key)); + } else { $this->database = $this->setUpDatabase($this->configuration); } @@ -163,7 +166,7 @@ protected function prepareQuery() { */ protected function initializeIterator() { $this->prepareQuery(); - $high_water_property = $this->migration->get('highWaterProperty'); + $high_water_property = $this->migration->getHighWaterProperty(); // Get the key values, for potential use in joining to the map table. $keys = array(); @@ -205,7 +208,7 @@ protected function initializeIterator() { $map_key = 'sourceid' . $count; $this->query->addField($alias, $map_key, "migrate_map_$map_key"); } - if ($n = count($this->migration->get('destinationIds'))) { + if ($n = count($this->migration->getDestinationIds())) { for ($count = 1; $count <= $n; $count++) { $map_key = 'destid' . $count++; $this->query->addField($alias, $map_key, "migrate_map_$map_key"); diff --git a/core/modules/migrate/src/Tests/MigrateDumpAlterInterface.php b/core/modules/migrate/src/Tests/MigrateDumpAlterInterface.php deleted file mode 100644 index 3f22657..0000000 --- a/core/modules/migrate/src/Tests/MigrateDumpAlterInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - '1', 'field1' => 'f1value1', 'field2' => 'f2value1'], - ['key' => '2', 'field1' => 'f1value2', 'field2' => 'f2value2'], - ]; - $ids = ['key' => ['type' => 'integer']]; - $config = [ - 'id' => 'sample_data', - 'migration_tags' => ['Embedded data test'], - 'source' => [ - 'plugin' => 'embedded_data', - 'data_rows' => $data_rows, - 'ids' => $ids, - ], - 'process' => [], - 'destination' => ['plugin' => 'null'], - ]; - - $migration = Migration::create($config); - $source = $migration->getSourcePlugin(); - - // Validate the plugin returns the source data that was provided. - $results = []; - /** @var Row $row */ - foreach ($source as $row) { - $data_row = $row->getSource(); - // The "data" row returned by getSource() also includes all source - // configuration - we remove it so we see only the data itself. - unset($data_row['plugin']); - unset($data_row['data_rows']); - unset($data_row['ids']); - $results[] = $data_row; - } - $this->assertIdentical($results, $data_rows); - - // Validate the public APIs. - $this->assertIdentical($source->count(), count($data_rows)); - $this->assertIdentical($source->getIds(), $ids); - $expected_fields = [ - 'key' => 'key', - 'field1' => 'field1', - 'field2' => 'field2', - ]; - $this->assertIdentical($source->fields(), $expected_fields); - } - -} diff --git a/core/modules/migrate/src/Tests/MigrateEventsTest.php b/core/modules/migrate/src/Tests/MigrateEventsTest.php deleted file mode 100644 index 6bafdaf..0000000 --- a/core/modules/migrate/src/Tests/MigrateEventsTest.php +++ /dev/null @@ -1,227 +0,0 @@ -state = \Drupal::state(); - \Drupal::service('event_dispatcher')->addListener(MigrateEvents::MAP_SAVE, - array($this, 'mapSaveEventRecorder')); - \Drupal::service('event_dispatcher')->addListener(MigrateEvents::MAP_DELETE, - array($this, 'mapDeleteEventRecorder')); - \Drupal::service('event_dispatcher')->addListener(MigrateEvents::PRE_IMPORT, - array($this, 'preImportEventRecorder')); - \Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_IMPORT, - array($this, 'postImportEventRecorder')); - \Drupal::service('event_dispatcher')->addListener(MigrateEvents::PRE_ROW_SAVE, - array($this, 'preRowSaveEventRecorder')); - \Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_ROW_SAVE, - array($this, 'postRowSaveEventRecorder')); - } - - /** - * Tests migration events. - */ - public function testMigrateEvents() { - // Run a simple little migration, which should trigger one of each event - // other than map_delete. - $config = [ - 'id' => 'sample_data', - 'migration_tags' => ['Event test'], - 'source' => [ - 'plugin' => 'embedded_data', - 'data_rows' => [ - ['data' => 'dummy value'], - ], - 'ids' => [ - 'data' => ['type' => 'string'], - ], - ], - 'process' => ['value' => 'data'], - 'destination' => ['plugin' => 'dummy'], - ]; - - $migration = Migration::create($config); - - /** @var MigrationInterface $migration */ - $executable = new MigrateExecutable($migration, new MigrateMessage()); - // As the import runs, events will be dispatched, recording the received - // information in state. - $executable->import(); - - // Validate from the recorded state that the events were received. - $event = $this->state->get('migrate_events_test.pre_import_event', []); - $this->assertIdentical($event['event_name'], MigrateEvents::PRE_IMPORT); - $this->assertIdentical($event['migration']->id(), $migration->id()); - - $event = $this->state->get('migrate_events_test.post_import_event', []); - $this->assertIdentical($event['event_name'], MigrateEvents::POST_IMPORT); - $this->assertIdentical($event['migration']->id(), $migration->id()); - - $event = $this->state->get('migrate_events_test.map_save_event', []); - $this->assertIdentical($event['event_name'], MigrateEvents::MAP_SAVE); - // Validating the last row processed. - $this->assertIdentical($event['fields']['sourceid1'], 'dummy value'); - $this->assertIdentical($event['fields']['destid1'], 'dummy value'); - $this->assertIdentical($event['fields']['source_row_status'], 0); - - $event = $this->state->get('migrate_events_test.map_delete_event', []); - $this->assertIdentical($event, []); - - $event = $this->state->get('migrate_events_test.pre_row_save_event', []); - $this->assertIdentical($event['event_name'], MigrateEvents::PRE_ROW_SAVE); - $this->assertIdentical($event['migration']->id(), $migration->id()); - // Validating the last row processed. - $this->assertIdentical($event['row']->getSourceProperty('data'), 'dummy value'); - - $event = $this->state->get('migrate_events_test.post_row_save_event', []); - $this->assertIdentical($event['event_name'], MigrateEvents::POST_ROW_SAVE); - $this->assertIdentical($event['migration']->id(), $migration->id()); - // Validating the last row processed. - $this->assertIdentical($event['row']->getSourceProperty('data'), 'dummy value'); - $this->assertIdentical($event['destination_id_values']['value'], 'dummy value'); - - // Generate a map delete event. - $migration->getIdMap()->delete(['data' => 'dummy value']); - $event = $this->state->get('migrate_events_test.map_delete_event', []); - $this->assertIdentical($event['event_name'], MigrateEvents::MAP_DELETE); - $this->assertIdentical($event['source_id'], ['data' => 'dummy value']); - } - - /** - * Reacts to map save event. - * - * @param \Drupal\Migrate\Event\MigrateMapSaveEvent $event - * The migration event. - * @param string $name - * The event name. - */ - public function mapSaveEventRecorder(MigrateMapSaveEvent $event, $name) { - $this->state->set('migrate_events_test.map_save_event', array( - 'event_name' => $name, - 'map' => $event->getMap(), - 'fields' => $event->getFields(), - )); - } - - /** - * Reacts to map delete event. - * - * @param \Drupal\Migrate\Event\MigrateMapDeleteEvent $event - * The migration event. - * @param string $name - * The event name. - */ - public function mapDeleteEventRecorder(MigrateMapDeleteEvent $event, $name) { - $this->state->set('migrate_events_test.map_delete_event', array( - 'event_name' => $name, - 'map' => $event->getMap(), - 'source_id' => $event->getSourceId(), - )); - } - - /** - * Reacts to pre-import event. - * - * @param \Drupal\Migrate\Event\MigrateImportEvent $event - * The migration event. - * @param string $name - * The event name. - */ - public function preImportEventRecorder(MigrateImportEvent $event, $name) { - $this->state->set('migrate_events_test.pre_import_event', array( - 'event_name' => $name, - 'migration' => $event->getMigration(), - )); - } - - /** - * Reacts to post-import event. - * - * @param \Drupal\Migrate\Event\MigrateImportEvent $event - * The migration event. - * @param string $name - * The event name. - */ - public function postImportEventRecorder(MigrateImportEvent $event, $name) { - $this->state->set('migrate_events_test.post_import_event', array( - 'event_name' => $name, - 'migration' => $event->getMigration(), - )); - } - - /** - * Reacts to pre-row-save event. - * - * @param \Drupal\Migrate\Event\MigratePreRowSaveEvent $event - * The migration event. - * @param string $name - * The event name. - */ - public function preRowSaveEventRecorder(MigratePreRowSaveEvent $event, $name) { - $this->state->set('migrate_events_test.pre_row_save_event', array( - 'event_name' => $name, - 'migration' => $event->getMigration(), - 'row' => $event->getRow(), - )); - } - - /** - * Reacts to post-row-save event. - * - * @param \Drupal\Migrate\Event\MigratePostRowSaveEvent $event - * The migration event. - * @param string $name - * The event name. - */ - public function postRowSaveEventRecorder(MigratePostRowSaveEvent $event, $name) { - $this->state->set('migrate_events_test.post_row_save_event', array( - 'event_name' => $name, - 'migration' => $event->getMigration(), - 'row' => $event->getRow(), - 'destination_id_values' => $event->getDestinationIdValues(), - )); - } - -} diff --git a/core/modules/migrate/src/Tests/MigrateInterruptionTest.php b/core/modules/migrate/src/Tests/MigrateInterruptionTest.php deleted file mode 100644 index 70eae49..0000000 --- a/core/modules/migrate/src/Tests/MigrateInterruptionTest.php +++ /dev/null @@ -1,90 +0,0 @@ -addListener(MigrateEvents::POST_ROW_SAVE, - array($this, 'postRowSaveEventRecorder')); - } - - /** - * Tests migration interruptions. - */ - public function testMigrateEvents() { - // Run a simple little migration, which should trigger one of each event - // other than map_delete. - $config = [ - 'id' => 'sample_data', - 'migration_tags' => ['Interruption test'], - 'source' => [ - 'plugin' => 'embedded_data', - 'data_rows' => [ - ['data' => 'dummy value'], - ['data' => 'dummy value2'], - ], - 'ids' => [ - 'data' => ['type' => 'string'], - ], - ], - 'process' => ['value' => 'data'], - 'destination' => ['plugin' => 'dummy'], - ]; - - $migration = Migration::create($config); - - /** @var MigrationInterface $migration */ - $executable = new MigrateExecutable($migration, new MigrateMessage()); - // When the import runs, the first row imported will trigger an - // interruption. - $result = $executable->import(); - - $this->assertEqual($result, MigrationInterface::RESULT_INCOMPLETE); - - // The status should have been reset to IDLE. - $this->assertEqual($migration->getStatus(), MigrationInterface::STATUS_IDLE); - } - - /** - * Reacts to post-row-save event. - * - * @param \Drupal\Migrate\Event\MigratePostRowSaveEvent $event - * The migration event. - * @param string $name - * The event name. - */ - public function postRowSaveEventRecorder(MigratePostRowSaveEvent $event, $name) { - $event->getMigration()->interruptMigration(MigrationInterface::RESULT_INCOMPLETE); - } - -} diff --git a/core/modules/migrate/src/Tests/MigrateMessageTest.php b/core/modules/migrate/src/Tests/MigrateMessageTest.php deleted file mode 100644 index e3a0aeb..0000000 --- a/core/modules/migrate/src/Tests/MigrateMessageTest.php +++ /dev/null @@ -1,133 +0,0 @@ -installConfig(['system']); - - // A simple migration, which will generate a message to the ID map because - // the concat plugin throws an exception if its source is not an array. - $config = [ - 'id' => 'sample_data', - 'migration_tags' => ['Message test'], - 'source' => [ - 'plugin' => 'embedded_data', - 'data_rows' => [ - ['name' => 'source_message', 'value' => 'a message'], - ], - 'ids' => [ - 'name' => ['type' => 'string'], - ], - ], - 'process' => [ - 'message' => [ - 'plugin' => 'concat', - 'source' => 'value', - ], - ], - 'destination' => [ - 'plugin' => 'config', - 'config_name' => 'system.maintenance', - ], - ]; - - $this->migration = Migration::create($config); - } - - /** - * Tests migration interruptions. - */ - public function testMessagesNotTeed() { - // We don't ask for messages to be teed, so don't expect any. - $executable = new MigrateExecutable($this->migration, $this); - $executable->import(); - $this->assertIdentical(count($this->messages), 0); - } - - /** - * Tests migration interruptions. - */ - public function testMessagesTeed() { - // Ask to receive any messages sent to the idmap. - \Drupal::service('event_dispatcher')->addListener(MigrateEvents::IDMAP_MESSAGE, - array($this, 'mapMessageRecorder')); - $executable = new MigrateExecutable($this->migration, $this); - $executable->import(); - $this->assertIdentical(count($this->messages), 1); - $this->assertIdentical(reset($this->messages), "source_message: 'a message' is not an array"); - } - - /** - * Reacts to map message event. - * - * @param \Drupal\Migrate\Event\MigrateIdMapMessageEvent $event - * The migration event. - * @param string $name - * The event name. - */ - public function mapMessageRecorder(MigrateIdMapMessageEvent $event, $name) { - if ($event->getLevel() == MigrationInterface::MESSAGE_NOTICE || - $event->getLevel() == MigrationInterface::MESSAGE_INFORMATIONAL) { - $type = 'status'; - } - else { - $type = 'error'; - } - $source_id_string = implode(',', $event->getSourceIdValues()); - $this->display($source_id_string . ': ' . $event->getMessage(), $type); - } - - /** - * {@inheritdoc} - */ - public function display($message, $type = 'status') { - $this->messages[] = $message; - } - -} diff --git a/core/modules/migrate/src/Tests/MigrateRollbackTest.php b/core/modules/migrate/src/Tests/MigrateRollbackTest.php deleted file mode 100644 index 9d6055c..0000000 --- a/core/modules/migrate/src/Tests/MigrateRollbackTest.php +++ /dev/null @@ -1,186 +0,0 @@ -installEntitySchema('taxonomy_vocabulary'); - $this->installEntitySchema('taxonomy_term'); - $this->installConfig(['taxonomy']); - } - - /** - * Tests rolling back configuration and content entities. - */ - public function testRollback() { - // We use vocabularies to demonstrate importing and rolling back - // configuration entities. - $vocabulary_data_rows = [ - ['id' => '1', 'name' => 'categories', 'weight' => '2'], - ['id' => '2', 'name' => 'tags', 'weight' => '1'], - ]; - $ids = ['id' => ['type' => 'integer']]; - $config = [ - 'id' => 'vocabularies', - 'migration_tags' => ['Import and rollback test'], - 'source' => [ - 'plugin' => 'embedded_data', - 'data_rows' => $vocabulary_data_rows, - 'ids' => $ids, - ], - 'process' => [ - 'vid' => 'id', - 'name' => 'name', - 'weight' => 'weight', - ], - 'destination' => ['plugin' => 'entity:taxonomy_vocabulary'], - ]; - - $vocabulary_migration = Migration::create($config); - $vocabulary_id_map = $vocabulary_migration->getIdMap(); - - $this->assertTrue($vocabulary_migration->getDestinationPlugin()->supportsRollback()); - - // Import and validate vocabulary config entities were created. - $vocabulary_executable = new MigrateExecutable($vocabulary_migration, $this); - $vocabulary_executable->import(); - foreach ($vocabulary_data_rows as $row) { - /** @var Vocabulary $vocabulary */ - $vocabulary = Vocabulary::load($row['id']); - $this->assertTrue($vocabulary); - $map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]); - $this->assertNotNull($map_row['destid1']); - } - - // We use taxonomy terms to demonstrate importing and rolling back content - // entities. - $term_data_rows = [ - ['id' => '1', 'vocab' => '1', 'name' => 'music'], - ['id' => '2', 'vocab' => '2', 'name' => 'Bach'], - ['id' => '3', 'vocab' => '2', 'name' => 'Beethoven'], - ]; - $ids = ['id' => ['type' => 'integer']]; - $config = [ - 'id' => 'terms', - 'migration_tags' => ['Import and rollback test'], - 'source' => [ - 'plugin' => 'embedded_data', - 'data_rows' => $term_data_rows, - 'ids' => $ids, - ], - 'process' => [ - 'tid' => 'id', - 'vid' => 'vocab', - 'name' => 'name', - ], - 'destination' => ['plugin' => 'entity:taxonomy_term'], - 'migration_dependencies' => ['required' => ['vocabularies']], - ]; - - $term_migration = Migration::create($config); - $term_id_map = $term_migration->getIdMap(); - - $this->assertTrue($term_migration->getDestinationPlugin()->supportsRollback()); - - // Pre-create a term, to make sure it isn't deleted on rollback. - $preserved_term_ids[] = 1; - $new_term = Term::create(['tid' => 1, 'vid' => 1, 'name' => 'music']); - $new_term->save(); - - // Import and validate term entities were created. - $term_executable = new MigrateExecutable($term_migration, $this); - $term_executable->import(); - // Also explicitly mark one row to be preserved on rollback. - $preserved_term_ids[] = 2; - $map_row = $term_id_map->getRowBySource(['id' => 2]); - $dummy_row = new Row(['id' => 2], $ids); - $term_id_map->saveIdMapping($dummy_row, [$map_row['destid1']], - $map_row['source_row_status'], MigrateIdMapInterface::ROLLBACK_PRESERVE); - - foreach ($term_data_rows as $row) { - /** @var Term $term */ - $term = Term::load($row['id']); - $this->assertTrue($term); - $map_row = $term_id_map->getRowBySource(['id' => $row['id']]); - $this->assertNotNull($map_row['destid1']); - } - - // Rollback and verify the entities are gone. - $term_executable->rollback(); - foreach ($term_data_rows as $row) { - $term = Term::load($row['id']); - if (in_array($row['id'], $preserved_term_ids)) { - $this->assertNotNull($term); - } - else { - $this->assertNull($term); - } - $map_row = $term_id_map->getRowBySource(['id' => $row['id']]); - $this->assertFalse($map_row); - } - $vocabulary_executable->rollback(); - foreach ($vocabulary_data_rows as $row) { - $term = Vocabulary::load($row['id']); - $this->assertNull($term); - $map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]); - $this->assertFalse($map_row); - } - - // Test that simple configuration is not rollbackable. - $term_setting_rows = [ - ['id' => 1, 'override_selector' => '0', 'terms_per_page_admin' => '10'], - ]; - $ids = ['id' => ['type' => 'integer']]; - $config = [ - 'id' => 'taxonomy_settings', - 'migration_tags' => ['Import and rollback test'], - 'source' => [ - 'plugin' => 'embedded_data', - 'data_rows' => $term_setting_rows, - 'ids' => $ids, - ], - 'process' => [ - 'override_selector' => 'override_selector', - 'terms_per_page_admin' => 'terms_per_page_admin', - ], - 'destination' => [ - 'plugin' => 'config', - 'config_name' => 'taxonomy.settings', - ], - 'migration_dependencies' => ['required' => ['vocabularies']], - ]; - - $settings_migration = Migration::create($config); - $this->assertFalse($settings_migration->getDestinationPlugin()->supportsRollback()); - } - -} diff --git a/core/modules/migrate/src/Tests/MigrateSkipRowTest.php b/core/modules/migrate/src/Tests/MigrateSkipRowTest.php deleted file mode 100644 index 93c1a7c..0000000 --- a/core/modules/migrate/src/Tests/MigrateSkipRowTest.php +++ /dev/null @@ -1,74 +0,0 @@ - 'sample_data', - 'migration_tags' => ['prepare_row test'], - 'source' => [ - 'plugin' => 'embedded_data', - 'data_rows' => [ - ['id' => '1', 'data' => 'skip_and_record'], - ['id' => '2', 'data' => 'skip_and_dont_record'], - ], - 'ids' => [ - 'id' => ['type' => 'string'], - ], - ], - 'process' => ['value' => 'data'], - 'destination' => [ - 'plugin' => 'config', - 'config_name' => 'migrate_test.settings', - ], - 'load' => ['plugin' => 'null'], - ]; - - $migration = Migration::create($config); - - $executable = new MigrateExecutable($migration, new MigrateMessage()); - $result = $executable->import(); - $this->assertEqual($result, MigrationInterface::RESULT_COMPLETED); - - $id_map_plugin = $migration->getIdMap(); - // The first row is recorded in the map as ignored. - $map_row = $id_map_plugin->getRowBySource(['id' => 1]); - $this->assertEqual(MigrateIdMapInterface::STATUS_IGNORED, $map_row['source_row_status']); - // The second row is not recorded in the map. - $map_row = $id_map_plugin->getRowBySource(['id' => 2]); - $this->assertFalse($map_row); - - } - -} diff --git a/core/modules/migrate/src/Tests/MigrateStatusTest.php b/core/modules/migrate/src/Tests/MigrateStatusTest.php deleted file mode 100644 index bdfcf74..0000000 --- a/core/modules/migrate/src/Tests/MigrateStatusTest.php +++ /dev/null @@ -1,56 +0,0 @@ - 'migration_status_test', - 'migration_tags' => ['Testing'], - 'source' => ['plugin' => 'empty'], - 'destination' => [ - 'plugin' => 'config', - 'config_name' => 'migrate_test.settings', - ], - 'process' => ['foo' => 'bar'], - ]; - $migration = Migration::create($configuration); - $migration->save(); - - // Default status is idle. - $status = $migration->getStatus(); - $this->assertIdentical($status, MigrationInterface::STATUS_IDLE); - - // Test setting and retrieving all known status values. - $status_list = array( - MigrationInterface::STATUS_IDLE, - MigrationInterface::STATUS_IMPORTING, - MigrationInterface::STATUS_ROLLING_BACK, - MigrationInterface::STATUS_STOPPING, - MigrationInterface::STATUS_DISABLED, - ); - foreach ($status_list as $status) { - $migration->setStatus($status); - $this->assertIdentical($migration->getStatus(), $status); - } - } - -} diff --git a/core/modules/migrate/src/Tests/MigrateTestBase.php b/core/modules/migrate/src/Tests/MigrateTestBase.php deleted file mode 100644 index e9012e3..0000000 --- a/core/modules/migrate/src/Tests/MigrateTestBase.php +++ /dev/null @@ -1,226 +0,0 @@ -createMigrationConnection(); - $this->sourceDatabase = Database::getConnection('default', 'migrate'); - } - - /** - * Changes the database connection to the prefixed one. - * - * @todo Remove when we don't use global. https://www.drupal.org/node/2552791 - */ - private function createMigrationConnection() { - // If the backup already exists, something went terribly wrong. - // This case is possible, because database connection info is a static - // global state construct on the Database class, which at least persists - // for all test methods executed in one PHP process. - if (Database::getConnectionInfo('simpletest_original_migrate')) { - throw new \RuntimeException("Bad Database connection state: 'simpletest_original_migrate' connection key already exists. Broken test?"); - } - - // Clone the current connection and replace the current prefix. - $connection_info = Database::getConnectionInfo('migrate'); - if ($connection_info) { - Database::renameConnection('migrate', 'simpletest_original_migrate'); - } - $connection_info = Database::getConnectionInfo('default'); - foreach ($connection_info as $target => $value) { - $prefix = is_array($value['prefix']) ? $value['prefix']['default'] : $value['prefix']; - // Simpletest uses 7 character prefixes at most so this can't cause - // collisions. - $connection_info[$target]['prefix']['default'] = $prefix . '0'; - - // Add the original simpletest prefix so SQLite can attach its database. - // @see \Drupal\Core\Database\Driver\sqlite\Connection::init() - $connection_info[$target]['prefix'][$value['prefix']['default']] = $value['prefix']['default']; - } - Database::addConnectionInfo('migrate', 'default', $connection_info['default']); - } - - /** - * {@inheritdoc} - */ - protected function tearDown() { - $this->cleanupMigrateConnection(); - parent::tearDown(); - $this->databaseDumpFiles = []; - $this->collectMessages = FALSE; - unset($this->migration, $this->migrateMessages); - } - - /** - * Cleans up the test migrate connection. - * - * @todo Remove when we don't use global. https://www.drupal.org/node/2552791 - */ - private function cleanupMigrateConnection() { - Database::removeConnection('migrate'); - $original_connection_info = Database::getConnectionInfo('simpletest_original_migrate'); - if ($original_connection_info) { - Database::renameConnection('simpletest_original_migrate', 'migrate'); - } - } - - /** - * Prepare any dependent migrations. - * - * @param array $id_mappings - * A list of ID mappings keyed by migration IDs. Each ID mapping is a list - * of two arrays, the first are source IDs and the second are destination - * IDs. - */ - protected function prepareMigrations(array $id_mappings) { - foreach ($id_mappings as $migration_id => $data) { - // Use loadMultiple() here in order to load all variants. - foreach (Migration::loadMultiple([$migration_id]) as $migration) { - $id_map = $migration->getIdMap(); - $id_map->setMessage($this); - $source_ids = $migration->getSourcePlugin()->getIds(); - foreach ($data as $id_mapping) { - $row = new Row(array_combine(array_keys($source_ids), $id_mapping[0]), $source_ids); - $id_map->saveIdMapping($row, $id_mapping[1]); - } - } - } - } - - /** - * Executes a single migration. - * - * @param string|\Drupal\migrate\Entity\MigrationInterface $migration - * The migration to execute, or its ID. - */ - protected function executeMigration($migration) { - if (is_string($migration)) { - $this->migration = Migration::load($migration); - } - else { - $this->migration = $migration; - } - if ($this instanceof MigrateDumpAlterInterface) { - static::migrateDumpAlter($this); - } - (new MigrateExecutable($this->migration, $this))->import(); - } - - /** - * Executes a set of migrations in dependency order. - * - * @param string[] $ids - * Array of migration IDs, in any order. - */ - protected function executeMigrations(array $ids) { - $migrations = Migration::loadMultiple($ids); - array_walk($migrations, [$this, 'executeMigration']); - } - - /** - * {@inheritdoc} - */ - public function display($message, $type = 'status') { - if ($this->collectMessages) { - $this->migrateMessages[$type][] = $message; - } - else { - $this->assert($type == 'status', $message, 'migrate'); - } - } - - /** - * Start collecting messages and erase previous messages. - */ - public function startCollectingMessages() { - $this->collectMessages = TRUE; - $this->migrateMessages = array(); - } - - /** - * Stop collecting messages. - */ - public function stopCollectingMessages() { - $this->collectMessages = FALSE; - } - - /** - * Records a failure in the map table of a specific migration. - * - * This is done in order to test scenarios which require a failed row. - * - * @param string|\Drupal\migrate\Entity\MigrationInterface $migration - * The migration entity, or its ID. - * @param array $row - * The raw source row which "failed". - * @param int $status - * (optional) The failure status. Should be one of the - * MigrateIdMapInterface::STATUS_* constants. Defaults to - * MigrateIdMapInterface::STATUS_FAILED. - */ - protected function mockFailure($migration, array $row, $status = MigrateIdMapInterface::STATUS_FAILED) { - if (is_string($migration)) { - $migration = Migration::load($migration); - } - /** @var \Drupal\migrate\Entity\MigrationInterface $migration */ - $destination = array_map(function() { return NULL; }, $migration->getDestinationPlugin()->getIds()); - $row = new Row($row, $migration->getSourcePlugin()->getIds()); - $migration->getIdMap()->saveIdMapping($row, $destination, $status); - } - -} diff --git a/core/modules/migrate/src/Tests/MigrationTest.php b/core/modules/migrate/src/Tests/MigrationTest.php deleted file mode 100644 index 6511903..0000000 --- a/core/modules/migrate/src/Tests/MigrationTest.php +++ /dev/null @@ -1,50 +0,0 @@ - ['plugin' => 'empty'], - 'destination' => ['plugin' => 'entity:entity_view_mode'], - ]); - $this->assertEqual('empty', $migration->getSourcePlugin()->getPluginId()); - $this->assertEqual('entity:entity_view_mode', $migration->getDestinationPlugin()->getPluginId()); - - // Test the source plugin is invalidated. - $migration->set('source', ['plugin' => 'd6_field']); - $this->assertEqual('d6_field', $migration->getSourcePlugin()->getPluginId()); - - // Test the destination plugin is invalidated. - $migration->set('destination', ['plugin' => 'null']); - $this->assertEqual('null', $migration->getDestinationPlugin()->getPluginId()); - } - -} diff --git a/core/modules/migrate/src/Tests/SqlBaseTest.php b/core/modules/migrate/src/Tests/SqlBaseTest.php deleted file mode 100644 index 0fd15a6..0000000 --- a/core/modules/migrate/src/Tests/SqlBaseTest.php +++ /dev/null @@ -1,138 +0,0 @@ -setConfiguration([]); - $this->assertIdentical($sql_base->getDatabase()->getTarget(), 'default'); - $this->assertIdentical($sql_base->getDatabase()->getKey(), 'migrate'); - - $target = 'test_db_target'; - $key = 'test_migrate_connection'; - $config = array('target' => $target, 'key' => $key); - $sql_base->setConfiguration($config); - Database::addConnectionInfo($key, $target, Database::getConnectionInfo('default')['default']); - - // Validate we have injected our custom key and target. - $this->assertIdentical($sql_base->getDatabase()->getTarget(), $target); - $this->assertIdentical($sql_base->getDatabase()->getKey(), $key); - - // Now test we can have SqlBase create the connection from an info array. - $sql_base = new TestSqlBase(); - - $target = 'test_db_target2'; - $key = 'test_migrate_connection2'; - $database = Database::getConnectionInfo('default')['default']; - $config = array('target' => $target, 'key' => $key, 'database' => $database); - $sql_base->setConfiguration($config); - - // Call getDatabase() to get the connection defined. - $sql_base->getDatabase(); - - // Validate the connection has been created with the right values. - $this->assertIdentical(Database::getConnectionInfo($key)[$target], $database); - - // Now, test this all works when using state to store db info. - $target = 'test_state_db_target'; - $key = 'test_state_migrate_connection'; - $config = ['target' => $target, 'key' => $key]; - $database_state_key = 'migrate_sql_base_test'; - \Drupal::state()->set($database_state_key, $config); - $sql_base->setConfiguration(['database_state_key' => $database_state_key]); - Database::addConnectionInfo($key, $target, Database::getConnectionInfo('default')['default']); - - // Validate we have injected our custom key and target. - $this->assertIdentical($sql_base->getDatabase()->getTarget(), $target); - $this->assertIdentical($sql_base->getDatabase()->getKey(), $key); - - // Now test we can have SqlBase create the connection from an info array. - $sql_base = new TestSqlBase(); - - $target = 'test_state_db_target2'; - $key = 'test_state_migrate_connection2'; - $database = Database::getConnectionInfo('default')['default']; - $config = ['target' => $target, 'key' => $key, 'database' => $database]; - $database_state_key = 'migrate_sql_base_test2'; - \Drupal::state()->set($database_state_key, $config); - $sql_base->setConfiguration(['database_state_key' => $database_state_key]); - - // Call getDatabase() to get the connection defined. - $sql_base->getDatabase(); - - // Validate the connection has been created with the right values. - $this->assertIdentical(Database::getConnectionInfo($key)[$target], $database); - } - -} - -namespace Drupal\migrate\Plugin\migrate\source; - -/** - * A dummy source to help with testing SqlBase. - * - * @package Drupal\migrate\Plugin\migrate\source - */ -class TestSqlBase extends SqlBase { - - /** - * Overrides the constructor so we can create one easily. - */ - public function __construct() { - $this->state = \Drupal::state(); - } - - /** - * Gets the database without caching it. - */ - public function getDatabase() { - $this->database = NULL; - return parent::getDatabase(); - } - - /** - * Allows us to set the configuration from a test. - * - * @param array $config - * The config array. - */ - public function setConfiguration($config) { - $this->configuration = $config; - } - - /** - * {@inheritdoc} - */ - public function getIds() {} - - /** - * {@inheritdoc} - */ - public function fields() {} - - /** - * {@inheritdoc} - */ - public function query() {} - -} diff --git a/core/modules/migrate/src/Tests/TemplateTest.php b/core/modules/migrate/src/Tests/TemplateTest.php deleted file mode 100644 index 44400e9..0000000 --- a/core/modules/migrate/src/Tests/TemplateTest.php +++ /dev/null @@ -1,78 +0,0 @@ -findTemplatesByTag("Template Test"); - $expected_url = [ - 'id' => 'url_template', - 'label' => 'Template test - url', - 'migration_tags' => ['Template Test'], - 'source' => ['plugin' => 'empty'], - 'process' => ['src' => 'foobar'], - 'destination' => ['plugin' => 'url_alias'], - ]; - $expected_node = [ - 'id' => 'node_template', - 'label' => 'Template test - node', - 'migration_tags' => ['Template Test'], - 'source' => ['plugin' => 'empty'], - 'process' => ['src' => 'barfoo'], - 'destination' => ['plugin' => 'entity:node'], - ]; - $this->assertIdentical($migration_templates['migrate.migration.url_template'], $expected_url); - $this->assertIdentical($migration_templates['migrate.migration.node_template'], $expected_node); - $this->assertFalse(isset($migration_templates['migrate.migration.other_template'])); - } - - /** - * Tests retrieving a template by name. - */ - public function testGetTemplateByName() { - /** @var \Drupal\migrate\MigrateTemplateStorageInterface $template_storage */ - $template_storage = \Drupal::service('migrate.template_storage'); - - $expected_url = [ - 'id' => 'url_template', - 'label' => 'Template test - url', - 'migration_tags' => ['Template Test'], - 'source' => ['plugin' => 'empty'], - 'process' => ['src' => 'foobar'], - 'destination' => ['plugin' => 'url_alias'], - ]; - $expected_node = [ - 'id' => 'node_template', - 'label' => 'Template test - node', - 'migration_tags' => ['Template Test'], - 'source' => ['plugin' => 'empty'], - 'process' => ['src' => 'barfoo'], - 'destination' => ['plugin' => 'entity:node'], - ]; - $this->assertIdentical($template_storage->getTemplateByName('migrate.migration.url_template'), $expected_url); - $this->assertIdentical($template_storage->getTemplateByName('migrate.migration.node_template'), $expected_node); - $this->assertNull($template_storage->getTemplateByName('migrate.migration.dne')); - } - -} diff --git a/core/modules/migrate/tests/modules/migrate_events_test/src/Plugin/migrate/destination/DummyDestination.php b/core/modules/migrate/tests/modules/migrate_events_test/src/Plugin/migrate/destination/DummyDestination.php index 984560b..8d20eb5 100644 --- a/core/modules/migrate/tests/modules/migrate_events_test/src/Plugin/migrate/destination/DummyDestination.php +++ b/core/modules/migrate/tests/modules/migrate_events_test/src/Plugin/migrate/destination/DummyDestination.php @@ -7,7 +7,7 @@ namespace Drupal\migrate_events_test\Plugin\migrate\destination; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\migrate\destination\DestinationBase; use Drupal\migrate\Row; diff --git a/core/modules/migrate/tests/modules/migrate_prepare_row_test/migrate_prepare_row_test.module b/core/modules/migrate/tests/modules/migrate_prepare_row_test/migrate_prepare_row_test.module index 9d9bd02..66f1bd9 100644 --- a/core/modules/migrate/tests/modules/migrate_prepare_row_test/migrate_prepare_row_test.module +++ b/core/modules/migrate/tests/modules/migrate_prepare_row_test/migrate_prepare_row_test.module @@ -6,7 +6,7 @@ * handling. */ -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\MigrateSkipRowException; use Drupal\migrate\Plugin\MigrateSourceInterface; use Drupal\migrate\Row; diff --git a/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.url_template.yml b/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.url_template.yml index fd82119..0204046 100644 --- a/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.url_template.yml +++ b/core/modules/migrate/tests/modules/template_test/migration_templates/migrate.migration.url_template.yml @@ -1,5 +1,5 @@ id: url_template -label: Template test - url +label: Template test - URL migration_tags: - Template Test source: diff --git a/core/modules/migrate/tests/src/Kernel/Entity/MigrationTest.php b/core/modules/migrate/tests/src/Kernel/Entity/MigrationTest.php deleted file mode 100644 index fd37ac4..0000000 --- a/core/modules/migrate/tests/src/Kernel/Entity/MigrationTest.php +++ /dev/null @@ -1,76 +0,0 @@ - 'd6_node', - 'd6_node__page' => 'd6_node', - 'd6_variables' => 'd6_variables', - ]; - - foreach ($fixture_migrations as $id => $template) { - $values = [ - 'id' => $id, - 'template' => $template, - 'source' => [ - 'plugin' => 'empty', - ], - 'destination' => [ - 'plugin' => 'null', - ], - 'migration_tags' => [] - ]; - Migration::create($values)->save(); - } - - $values = [ - 'migration_dependencies' => [ - 'required' => [ - 'd6_node:*', - 'd6_variables' - ] - ], - 'source' => [ - 'plugin' => 'empty', - ], - 'destination' => [ - 'plugin' => 'null', - ], - ]; - - $migration = new Migration($values, 'migration'); - $expected = [ - 'migrate.migration.d6_node__article', - 'migrate.migration.d6_node__page', - 'migrate.migration.d6_variables' - ]; - $migration->calculateDependencies(); - $this->assertEquals($expected, $migration->getDependencies()['config']); - } - -} diff --git a/core/modules/migrate/tests/src/Kernel/MigrateDumpAlterInterface.php b/core/modules/migrate/tests/src/Kernel/MigrateDumpAlterInterface.php new file mode 100644 index 0000000..4d006c9 --- /dev/null +++ b/core/modules/migrate/tests/src/Kernel/MigrateDumpAlterInterface.php @@ -0,0 +1,27 @@ + '1', 'field1' => 'f1value1', 'field2' => 'f2value1'], + ['key' => '2', 'field1' => 'f1value2', 'field2' => 'f2value2'], + ]; + $ids = ['key' => ['type' => 'integer']]; + $definition = [ + 'migration_tags' => ['Embedded data test'], + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => $data_rows, + 'ids' => $ids, + ], + 'process' => [], + 'destination' => ['plugin' => 'null'], + ]; + + $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); + $source = $migration->getSourcePlugin(); + + // Validate the plugin returns the source data that was provided. + $results = []; + /** @var \Drupal\migrate\Row $row */ + foreach ($source as $row) { + $data_row = $row->getSource(); + // The "data" row returned by getSource() also includes all source + // configuration - we remove it so we see only the data itself. + unset($data_row['plugin']); + unset($data_row['data_rows']); + unset($data_row['ids']); + $results[] = $data_row; + } + $this->assertIdentical($results, $data_rows); + + // Validate the public APIs. + $this->assertIdentical($source->count(), count($data_rows)); + $this->assertIdentical($source->getIds(), $ids); + $expected_fields = [ + 'key' => 'key', + 'field1' => 'field1', + 'field2' => 'field2', + ]; + $this->assertIdentical($source->fields(), $expected_fields); + } + +} diff --git a/core/modules/migrate/tests/src/Kernel/MigrateEventsTest.php b/core/modules/migrate/tests/src/Kernel/MigrateEventsTest.php new file mode 100644 index 0000000..de8ed0a --- /dev/null +++ b/core/modules/migrate/tests/src/Kernel/MigrateEventsTest.php @@ -0,0 +1,223 @@ +state = \Drupal::state(); + \Drupal::service('event_dispatcher')->addListener(MigrateEvents::MAP_SAVE, + array($this, 'mapSaveEventRecorder')); + \Drupal::service('event_dispatcher')->addListener(MigrateEvents::MAP_DELETE, + array($this, 'mapDeleteEventRecorder')); + \Drupal::service('event_dispatcher')->addListener(MigrateEvents::PRE_IMPORT, + array($this, 'preImportEventRecorder')); + \Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_IMPORT, + array($this, 'postImportEventRecorder')); + \Drupal::service('event_dispatcher')->addListener(MigrateEvents::PRE_ROW_SAVE, + array($this, 'preRowSaveEventRecorder')); + \Drupal::service('event_dispatcher')->addListener(MigrateEvents::POST_ROW_SAVE, + array($this, 'postRowSaveEventRecorder')); + } + + /** + * Tests migration events. + */ + public function testMigrateEvents() { + // Run a simple little migration, which should trigger one of each event + // other than map_delete. + $definition = [ + 'migration_tags' => ['Event test'], + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => [ + ['data' => 'dummy value'], + ], + 'ids' => [ + 'data' => ['type' => 'string'], + ], + ], + 'process' => ['value' => 'data'], + 'destination' => ['plugin' => 'dummy'], + ]; + + $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); + + $executable = new MigrateExecutable($migration, new MigrateMessage()); + // As the import runs, events will be dispatched, recording the received + // information in state. + $executable->import(); + + // Validate from the recorded state that the events were received. + $event = $this->state->get('migrate_events_test.pre_import_event', []); + $this->assertIdentical($event['event_name'], MigrateEvents::PRE_IMPORT); + $this->assertIdentical($event['migration']->id(), $migration->id()); + + $event = $this->state->get('migrate_events_test.post_import_event', []); + $this->assertIdentical($event['event_name'], MigrateEvents::POST_IMPORT); + $this->assertIdentical($event['migration']->id(), $migration->id()); + + $event = $this->state->get('migrate_events_test.map_save_event', []); + $this->assertIdentical($event['event_name'], MigrateEvents::MAP_SAVE); + // Validating the last row processed. + $this->assertIdentical($event['fields']['sourceid1'], 'dummy value'); + $this->assertIdentical($event['fields']['destid1'], 'dummy value'); + $this->assertIdentical($event['fields']['source_row_status'], 0); + + $event = $this->state->get('migrate_events_test.map_delete_event', []); + $this->assertIdentical($event, []); + + $event = $this->state->get('migrate_events_test.pre_row_save_event', []); + $this->assertIdentical($event['event_name'], MigrateEvents::PRE_ROW_SAVE); + $this->assertIdentical($event['migration']->id(), $migration->id()); + // Validating the last row processed. + $this->assertIdentical($event['row']->getSourceProperty('data'), 'dummy value'); + + $event = $this->state->get('migrate_events_test.post_row_save_event', []); + $this->assertIdentical($event['event_name'], MigrateEvents::POST_ROW_SAVE); + $this->assertIdentical($event['migration']->id(), $migration->id()); + // Validating the last row processed. + $this->assertIdentical($event['row']->getSourceProperty('data'), 'dummy value'); + $this->assertIdentical($event['destination_id_values']['value'], 'dummy value'); + + // Generate a map delete event. + $migration->getIdMap()->delete(['data' => 'dummy value']); + $event = $this->state->get('migrate_events_test.map_delete_event', []); + $this->assertIdentical($event['event_name'], MigrateEvents::MAP_DELETE); + $this->assertIdentical($event['source_id'], ['data' => 'dummy value']); + } + + /** + * Reacts to map save event. + * + * @param \Drupal\Migrate\Event\MigrateMapSaveEvent $event + * The migration event. + * @param string $name + * The event name. + */ + public function mapSaveEventRecorder(MigrateMapSaveEvent $event, $name) { + $this->state->set('migrate_events_test.map_save_event', array( + 'event_name' => $name, + 'map' => $event->getMap(), + 'fields' => $event->getFields(), + )); + } + + /** + * Reacts to map delete event. + * + * @param \Drupal\Migrate\Event\MigrateMapDeleteEvent $event + * The migration event. + * @param string $name + * The event name. + */ + public function mapDeleteEventRecorder(MigrateMapDeleteEvent $event, $name) { + $this->state->set('migrate_events_test.map_delete_event', array( + 'event_name' => $name, + 'map' => $event->getMap(), + 'source_id' => $event->getSourceId(), + )); + } + + /** + * Reacts to pre-import event. + * + * @param \Drupal\Migrate\Event\MigrateImportEvent $event + * The migration event. + * @param string $name + * The event name. + */ + public function preImportEventRecorder(MigrateImportEvent $event, $name) { + $this->state->set('migrate_events_test.pre_import_event', array( + 'event_name' => $name, + 'migration' => $event->getMigration(), + )); + } + + /** + * Reacts to post-import event. + * + * @param \Drupal\Migrate\Event\MigrateImportEvent $event + * The migration event. + * @param string $name + * The event name. + */ + public function postImportEventRecorder(MigrateImportEvent $event, $name) { + $this->state->set('migrate_events_test.post_import_event', array( + 'event_name' => $name, + 'migration' => $event->getMigration(), + )); + } + + /** + * Reacts to pre-row-save event. + * + * @param \Drupal\Migrate\Event\MigratePreRowSaveEvent $event + * The migration event. + * @param string $name + * The event name. + */ + public function preRowSaveEventRecorder(MigratePreRowSaveEvent $event, $name) { + $this->state->set('migrate_events_test.pre_row_save_event', array( + 'event_name' => $name, + 'migration' => $event->getMigration(), + 'row' => $event->getRow(), + )); + } + + /** + * Reacts to post-row-save event. + * + * @param \Drupal\Migrate\Event\MigratePostRowSaveEvent $event + * The migration event. + * @param string $name + * The event name. + */ + public function postRowSaveEventRecorder(MigratePostRowSaveEvent $event, $name) { + $this->state->set('migrate_events_test.post_row_save_event', array( + 'event_name' => $name, + 'migration' => $event->getMigration(), + 'row' => $event->getRow(), + 'destination_id_values' => $event->getDestinationIdValues(), + )); + } + +} diff --git a/core/modules/migrate/tests/src/Kernel/MigrateInterruptionTest.php b/core/modules/migrate/tests/src/Kernel/MigrateInterruptionTest.php new file mode 100644 index 0000000..d62bccd --- /dev/null +++ b/core/modules/migrate/tests/src/Kernel/MigrateInterruptionTest.php @@ -0,0 +1,87 @@ +addListener(MigrateEvents::POST_ROW_SAVE, + array($this, 'postRowSaveEventRecorder')); + } + + /** + * Tests migration interruptions. + */ + public function testMigrateEvents() { + // Run a simple little migration, which should trigger one of each event + // other than map_delete. + $definition = [ + 'migration_tags' => ['Interruption test'], + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => [ + ['data' => 'dummy value'], + ['data' => 'dummy value2'], + ], + 'ids' => [ + 'data' => ['type' => 'string'], + ], + ], + 'process' => ['value' => 'data'], + 'destination' => ['plugin' => 'dummy'], + ]; + + $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); + + $executable = new MigrateExecutable($migration, new MigrateMessage()); + // When the import runs, the first row imported will trigger an + // interruption. + $result = $executable->import(); + + $this->assertEqual($result, MigrationInterface::RESULT_INCOMPLETE); + + // The status should have been reset to IDLE. + $this->assertEqual($migration->getStatus(), MigrationInterface::STATUS_IDLE); + } + + /** + * Reacts to post-row-save event. + * + * @param \Drupal\Migrate\Event\MigratePostRowSaveEvent $event + * The migration event. + * @param string $name + * The event name. + */ + public function postRowSaveEventRecorder(MigratePostRowSaveEvent $event, $name) { + $event->getMigration()->interruptMigration(MigrationInterface::RESULT_INCOMPLETE); + } + +} diff --git a/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php b/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php new file mode 100644 index 0000000..fbe777b --- /dev/null +++ b/core/modules/migrate/tests/src/Kernel/MigrateMessageTest.php @@ -0,0 +1,131 @@ +installConfig(['system']); + + // A simple migration, which will generate a message to the ID map because + // the concat plugin throws an exception if its source is not an array. + $definition = [ + 'migration_tags' => ['Message test'], + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => [ + ['name' => 'source_message', 'value' => 'a message'], + ], + 'ids' => [ + 'name' => ['type' => 'string'], + ], + ], + 'process' => [ + 'message' => [ + 'plugin' => 'concat', + 'source' => 'value', + ], + ], + 'destination' => [ + 'plugin' => 'config', + 'config_name' => 'system.maintenance', + ], + ]; + + $this->migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); + } + + /** + * Tests migration interruptions. + */ + public function testMessagesNotTeed() { + // We don't ask for messages to be teed, so don't expect any. + $executable = new MigrateExecutable($this->migration, $this); + $executable->import(); + $this->assertIdentical(count($this->messages), 0); + } + + /** + * Tests migration interruptions. + */ + public function testMessagesTeed() { + // Ask to receive any messages sent to the idmap. + \Drupal::service('event_dispatcher')->addListener(MigrateEvents::IDMAP_MESSAGE, + array($this, 'mapMessageRecorder')); + $executable = new MigrateExecutable($this->migration, $this); + $executable->import(); + $this->assertIdentical(count($this->messages), 1); + $this->assertIdentical(reset($this->messages), "source_message: 'a message' is not an array"); + } + + /** + * Reacts to map message event. + * + * @param \Drupal\Migrate\Event\MigrateIdMapMessageEvent $event + * The migration event. + * @param string $name + * The event name. + */ + public function mapMessageRecorder(MigrateIdMapMessageEvent $event, $name) { + if ($event->getLevel() == MigrationInterface::MESSAGE_NOTICE || + $event->getLevel() == MigrationInterface::MESSAGE_INFORMATIONAL) { + $type = 'status'; + } + else { + $type = 'error'; + } + $source_id_string = implode(',', $event->getSourceIdValues()); + $this->display($source_id_string . ': ' . $event->getMessage(), $type); + } + + /** + * {@inheritdoc} + */ + public function display($message, $type = 'status') { + $this->messages[] = $message; + } + +} diff --git a/core/modules/migrate/tests/src/Kernel/MigrateRollbackTest.php b/core/modules/migrate/tests/src/Kernel/MigrateRollbackTest.php new file mode 100644 index 0000000..955f2af --- /dev/null +++ b/core/modules/migrate/tests/src/Kernel/MigrateRollbackTest.php @@ -0,0 +1,186 @@ +installEntitySchema('taxonomy_vocabulary'); + $this->installEntitySchema('taxonomy_term'); + $this->installConfig(['taxonomy']); + } + + /** + * Tests rolling back configuration and content entities. + */ + public function testRollback() { + // We use vocabularies to demonstrate importing and rolling back + // configuration entities. + $vocabulary_data_rows = [ + ['id' => '1', 'name' => 'categories', 'weight' => '2'], + ['id' => '2', 'name' => 'tags', 'weight' => '1'], + ]; + $ids = ['id' => ['type' => 'integer']]; + $definition = [ + 'id' => 'vocabularies', + 'migration_tags' => ['Import and rollback test'], + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => $vocabulary_data_rows, + 'ids' => $ids, + ], + 'process' => [ + 'vid' => 'id', + 'name' => 'name', + 'weight' => 'weight', + ], + 'destination' => ['plugin' => 'entity:taxonomy_vocabulary'], + ]; + + $vocabulary_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); + $vocabulary_id_map = $vocabulary_migration->getIdMap(); + + $this->assertTrue($vocabulary_migration->getDestinationPlugin()->supportsRollback()); + + // Import and validate vocabulary config entities were created. + $vocabulary_executable = new MigrateExecutable($vocabulary_migration, $this); + $vocabulary_executable->import(); + foreach ($vocabulary_data_rows as $row) { + /** @var Vocabulary $vocabulary */ + $vocabulary = Vocabulary::load($row['id']); + $this->assertTrue($vocabulary); + $map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]); + $this->assertNotNull($map_row['destid1']); + } + + // We use taxonomy terms to demonstrate importing and rolling back content + // entities. + $term_data_rows = [ + ['id' => '1', 'vocab' => '1', 'name' => 'music'], + ['id' => '2', 'vocab' => '2', 'name' => 'Bach'], + ['id' => '3', 'vocab' => '2', 'name' => 'Beethoven'], + ]; + $ids = ['id' => ['type' => 'integer']]; + $definition = [ + 'id' => 'terms', + 'migration_tags' => ['Import and rollback test'], + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => $term_data_rows, + 'ids' => $ids, + ], + 'process' => [ + 'tid' => 'id', + 'vid' => 'vocab', + 'name' => 'name', + ], + 'destination' => ['plugin' => 'entity:taxonomy_term'], + 'migration_dependencies' => ['required' => ['vocabularies']], + ]; + + $term_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); + $term_id_map = $term_migration->getIdMap(); + + $this->assertTrue($term_migration->getDestinationPlugin()->supportsRollback()); + + // Pre-create a term, to make sure it isn't deleted on rollback. + $preserved_term_ids[] = 1; + $new_term = Term::create(['tid' => 1, 'vid' => 1, 'name' => 'music']); + $new_term->save(); + + // Import and validate term entities were created. + $term_executable = new MigrateExecutable($term_migration, $this); + $term_executable->import(); + // Also explicitly mark one row to be preserved on rollback. + $preserved_term_ids[] = 2; + $map_row = $term_id_map->getRowBySource(['id' => 2]); + $dummy_row = new Row(['id' => 2], $ids); + $term_id_map->saveIdMapping($dummy_row, [$map_row['destid1']], + $map_row['source_row_status'], MigrateIdMapInterface::ROLLBACK_PRESERVE); + + foreach ($term_data_rows as $row) { + /** @var Term $term */ + $term = Term::load($row['id']); + $this->assertTrue($term); + $map_row = $term_id_map->getRowBySource(['id' => $row['id']]); + $this->assertNotNull($map_row['destid1']); + } + + // Rollback and verify the entities are gone. + $term_executable->rollback(); + foreach ($term_data_rows as $row) { + $term = Term::load($row['id']); + if (in_array($row['id'], $preserved_term_ids)) { + $this->assertNotNull($term); + } + else { + $this->assertNull($term); + } + $map_row = $term_id_map->getRowBySource(['id' => $row['id']]); + $this->assertFalse($map_row); + } + $vocabulary_executable->rollback(); + foreach ($vocabulary_data_rows as $row) { + $term = Vocabulary::load($row['id']); + $this->assertNull($term); + $map_row = $vocabulary_id_map->getRowBySource(['id' => $row['id']]); + $this->assertFalse($map_row); + } + + // Test that simple configuration is not rollbackable. + $term_setting_rows = [ + ['id' => 1, 'override_selector' => '0', 'terms_per_page_admin' => '10'], + ]; + $ids = ['id' => ['type' => 'integer']]; + $definition = [ + 'id' => 'taxonomy_settings', + 'migration_tags' => ['Import and rollback test'], + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => $term_setting_rows, + 'ids' => $ids, + ], + 'process' => [ + 'override_selector' => 'override_selector', + 'terms_per_page_admin' => 'terms_per_page_admin', + ], + 'destination' => [ + 'plugin' => 'config', + 'config_name' => 'taxonomy.settings', + ], + 'migration_dependencies' => ['required' => ['vocabularies']], + ]; + + $settings_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); + $this->assertFalse($settings_migration->getDestinationPlugin()->supportsRollback()); + } + +} diff --git a/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php b/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php new file mode 100644 index 0000000..16c0bdc --- /dev/null +++ b/core/modules/migrate/tests/src/Kernel/MigrateSkipRowTest.php @@ -0,0 +1,72 @@ + ['prepare_row test'], + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => [ + ['id' => '1', 'data' => 'skip_and_record'], + ['id' => '2', 'data' => 'skip_and_dont_record'], + ], + 'ids' => [ + 'id' => ['type' => 'string'], + ], + ], + 'process' => ['value' => 'data'], + 'destination' => [ + 'plugin' => 'config', + 'config_name' => 'migrate_test.settings', + ], + 'load' => ['plugin' => 'null'], + ]; + + $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); + + $executable = new MigrateExecutable($migration, new MigrateMessage()); + $result = $executable->import(); + $this->assertEqual($result, MigrationInterface::RESULT_COMPLETED); + + $id_map_plugin = $migration->getIdMap(); + // The first row is recorded in the map as ignored. + $map_row = $id_map_plugin->getRowBySource(['id' => 1]); + $this->assertEqual(MigrateIdMapInterface::STATUS_IGNORED, $map_row['source_row_status']); + // The second row is not recorded in the map. + $map_row = $id_map_plugin->getRowBySource(['id' => 2]); + $this->assertFalse($map_row); + + } + +} diff --git a/core/modules/migrate/tests/src/Kernel/MigrateStatusTest.php b/core/modules/migrate/tests/src/Kernel/MigrateStatusTest.php new file mode 100644 index 0000000..23eddf0 --- /dev/null +++ b/core/modules/migrate/tests/src/Kernel/MigrateStatusTest.php @@ -0,0 +1,54 @@ + 'migration_status_test', + 'migration_tags' => ['Testing'], + 'source' => ['plugin' => 'empty'], + 'destination' => [ + 'plugin' => 'config', + 'config_name' => 'migrate_test.settings', + ], + 'process' => ['foo' => 'bar'], + ]; + $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); + + // Default status is idle. + $status = $migration->getStatus(); + $this->assertIdentical($status, MigrationInterface::STATUS_IDLE); + + // Test setting and retrieving all known status values. + $status_list = array( + MigrationInterface::STATUS_IDLE, + MigrationInterface::STATUS_IMPORTING, + MigrationInterface::STATUS_ROLLING_BACK, + MigrationInterface::STATUS_STOPPING, + MigrationInterface::STATUS_DISABLED, + ); + foreach ($status_list as $status) { + $migration->setStatus($status); + $this->assertIdentical($migration->getStatus(), $status); + } + } + +} diff --git a/core/modules/migrate/tests/src/Kernel/MigrateTestBase.php b/core/modules/migrate/tests/src/Kernel/MigrateTestBase.php new file mode 100644 index 0000000..a599a45 --- /dev/null +++ b/core/modules/migrate/tests/src/Kernel/MigrateTestBase.php @@ -0,0 +1,242 @@ +createMigrationConnection(); + $this->sourceDatabase = Database::getConnection('default', 'migrate'); + } + + /** + * Changes the database connection to the prefixed one. + * + * @todo Remove when we don't use global. https://www.drupal.org/node/2552791 + */ + private function createMigrationConnection() { + // If the backup already exists, something went terribly wrong. + // This case is possible, because database connection info is a static + // global state construct on the Database class, which at least persists + // for all test methods executed in one PHP process. + if (Database::getConnectionInfo('simpletest_original_migrate')) { + throw new \RuntimeException("Bad Database connection state: 'simpletest_original_migrate' connection key already exists. Broken test?"); + } + + // Clone the current connection and replace the current prefix. + $connection_info = Database::getConnectionInfo('migrate'); + if ($connection_info) { + Database::renameConnection('migrate', 'simpletest_original_migrate'); + } + $connection_info = Database::getConnectionInfo('default'); + foreach ($connection_info as $target => $value) { + $prefix = is_array($value['prefix']) ? $value['prefix']['default'] : $value['prefix']; + // Simpletest uses 7 character prefixes at most so this can't cause + // collisions. + $connection_info[$target]['prefix']['default'] = $prefix . '0'; + + // Add the original simpletest prefix so SQLite can attach its database. + // @see \Drupal\Core\Database\Driver\sqlite\Connection::init() + $connection_info[$target]['prefix'][$value['prefix']['default']] = $value['prefix']['default']; + } + Database::addConnectionInfo('migrate', 'default', $connection_info['default']); + } + + /** + * {@inheritdoc} + */ + protected function tearDown() { + $this->cleanupMigrateConnection(); + parent::tearDown(); + $this->databaseDumpFiles = []; + $this->collectMessages = FALSE; + unset($this->migration, $this->migrateMessages); + } + + /** + * Cleans up the test migrate connection. + * + * @todo Remove when we don't use global. https://www.drupal.org/node/2552791 + */ + private function cleanupMigrateConnection() { + Database::removeConnection('migrate'); + $original_connection_info = Database::getConnectionInfo('simpletest_original_migrate'); + if ($original_connection_info) { + Database::renameConnection('simpletest_original_migrate', 'migrate'); + } + } + + /** + * Prepare any dependent migrations. + * + * @param array $id_mappings + * A list of ID mappings keyed by migration IDs. Each ID mapping is a list + * of two arrays, the first are source IDs and the second are destination + * IDs. + */ + protected function prepareMigrations(array $id_mappings) { + $manager = $this->container->get('plugin.manager.migration'); + foreach ($id_mappings as $migration_id => $data) { + foreach ($manager->createInstances($migration_id) as $migration) { + $id_map = $migration->getIdMap(); + $id_map->setMessage($this); + $source_ids = $migration->getSourcePlugin()->getIds(); + foreach ($data as $id_mapping) { + $row = new Row(array_combine(array_keys($source_ids), $id_mapping[0]), $source_ids); + $id_map->saveIdMapping($row, $id_mapping[1]); + } + } + } + } + + /** + * Executes a single migration. + * + * @param string|\Drupal\migrate\Plugin\MigrationInterface $migration + * The migration to execute, or its ID. + */ + protected function executeMigration($migration) { + if (is_string($migration)) { + $this->migration = $this->getMigration($migration); + } + else { + $this->migration = $migration; + } + if ($this instanceof MigrateDumpAlterInterface) { + static::migrateDumpAlter($this); + } + (new MigrateExecutable($this->migration, $this))->import(); + } + + /** + * Executes a set of migrations in dependency order. + * + * @param string[] $ids + * Array of migration IDs, in any order. + */ + protected function executeMigrations(array $ids) { + $manager = $this->container->get('plugin.manager.migration'); + array_walk($ids, function ($id) use ($manager) { + // This is possibly a base plugin ID and we want to run all derivatives. + $instances = $manager->createInstances($id); + array_walk($instances, [$this, 'executeMigration']); + }); + } + + /** + * {@inheritdoc} + */ + public function display($message, $type = 'status') { + if ($this->collectMessages) { + $this->migrateMessages[$type][] = $message; + } + else { + $this->assert($type == 'status', $message, 'migrate'); + } + } + + /** + * Start collecting messages and erase previous messages. + */ + public function startCollectingMessages() { + $this->collectMessages = TRUE; + $this->migrateMessages = array(); + } + + /** + * Stop collecting messages. + */ + public function stopCollectingMessages() { + $this->collectMessages = FALSE; + } + + /** + * Records a failure in the map table of a specific migration. + * + * This is done in order to test scenarios which require a failed row. + * + * @param string|\Drupal\migrate\Plugin\MigrationInterface $migration + * The migration entity, or its ID. + * @param array $row + * The raw source row which "failed". + * @param int $status + * (optional) The failure status. Should be one of the + * MigrateIdMapInterface::STATUS_* constants. Defaults to + * MigrateIdMapInterface::STATUS_FAILED. + */ + protected function mockFailure($migration, array $row, $status = MigrateIdMapInterface::STATUS_FAILED) { + if (is_string($migration)) { + $migration = $this->getMigration($migration); + } + /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ + $destination = array_map(function() { return NULL; }, $migration->getDestinationPlugin()->getIds()); + $row = new Row($row, $migration->getSourcePlugin()->getIds()); + $migration->getIdMap()->saveIdMapping($row, $destination, $status); + } + + /** + * Gets the migration plugin. + * + * @param $plugin_id + * The plugin ID of the migration to get. + * + * @return \Drupal\migrate\Plugin\Migration + * The migration plugin. + */ + protected function getMigration($plugin_id) { + return $this->container->get('plugin.manager.migration')->createInstance($plugin_id); + } + +} diff --git a/core/modules/migrate/tests/src/Kernel/MigrationTest.php b/core/modules/migrate/tests/src/Kernel/MigrationTest.php new file mode 100644 index 0000000..26cea66 --- /dev/null +++ b/core/modules/migrate/tests/src/Kernel/MigrationTest.php @@ -0,0 +1,50 @@ +createStubMigration([ + 'source' => ['plugin' => 'empty'], + 'destination' => ['plugin' => 'entity:entity_view_mode'], + ]); + $this->assertEqual('empty', $migration->getSourcePlugin()->getPluginId()); + $this->assertEqual('entity:entity_view_mode', $migration->getDestinationPlugin()->getPluginId()); + + // Test the source plugin is invalidated. + $migration->set('source', ['plugin' => 'd6_field']); + $this->assertEqual('d6_field', $migration->getSourcePlugin()->getPluginId()); + + // Test the destination plugin is invalidated. + $migration->set('destination', ['plugin' => 'null']); + $this->assertEqual('null', $migration->getDestinationPlugin()->getPluginId()); + } + +} diff --git a/core/modules/migrate/tests/src/Kernel/Plugin/MigrationTest.php b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationTest.php new file mode 100644 index 0000000..9975700 --- /dev/null +++ b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationTest.php @@ -0,0 +1,35 @@ +createStubMigration([]); + $this->assertEquals([], $migration->getProcessPlugins([])); + } + +} diff --git a/core/modules/migrate/tests/src/Kernel/SqlBaseTest.php b/core/modules/migrate/tests/src/Kernel/SqlBaseTest.php new file mode 100644 index 0000000..dc4b32c --- /dev/null +++ b/core/modules/migrate/tests/src/Kernel/SqlBaseTest.php @@ -0,0 +1,139 @@ +setConfiguration([]); + $this->assertIdentical($sql_base->getDatabase()->getTarget(), 'default'); + $this->assertIdentical($sql_base->getDatabase()->getKey(), 'migrate'); + + $target = 'test_db_target'; + $key = 'test_migrate_connection'; + $config = array('target' => $target, 'key' => $key); + $sql_base->setConfiguration($config); + Database::addConnectionInfo($key, $target, Database::getConnectionInfo('default')['default']); + + // Validate we have injected our custom key and target. + $this->assertIdentical($sql_base->getDatabase()->getTarget(), $target); + $this->assertIdentical($sql_base->getDatabase()->getKey(), $key); + + // Now test we can have SqlBase create the connection from an info array. + $sql_base = new TestSqlBase(); + + $target = 'test_db_target2'; + $key = 'test_migrate_connection2'; + $database = Database::getConnectionInfo('default')['default']; + $config = array('target' => $target, 'key' => $key, 'database' => $database); + $sql_base->setConfiguration($config); + + // Call getDatabase() to get the connection defined. + $sql_base->getDatabase(); + + // Validate the connection has been created with the right values. + $this->assertIdentical(Database::getConnectionInfo($key)[$target], $database); + + // Now, test this all works when using state to store db info. + $target = 'test_state_db_target'; + $key = 'test_state_migrate_connection'; + $config = ['target' => $target, 'key' => $key]; + $database_state_key = 'migrate_sql_base_test'; + \Drupal::state()->set($database_state_key, $config); + $sql_base->setConfiguration(['database_state_key' => $database_state_key]); + Database::addConnectionInfo($key, $target, Database::getConnectionInfo('default')['default']); + + // Validate we have injected our custom key and target. + $this->assertIdentical($sql_base->getDatabase()->getTarget(), $target); + $this->assertIdentical($sql_base->getDatabase()->getKey(), $key); + + // Now test we can have SqlBase create the connection from an info array. + $sql_base = new TestSqlBase(); + + $target = 'test_state_db_target2'; + $key = 'test_state_migrate_connection2'; + $database = Database::getConnectionInfo('default')['default']; + $config = ['target' => $target, 'key' => $key, 'database' => $database]; + $database_state_key = 'migrate_sql_base_test2'; + \Drupal::state()->set($database_state_key, $config); + $sql_base->setConfiguration(['database_state_key' => $database_state_key]); + + // Call getDatabase() to get the connection defined. + $sql_base->getDatabase(); + + // Validate the connection has been created with the right values. + $this->assertIdentical(Database::getConnectionInfo($key)[$target], $database); + } + +} + +namespace Drupal\migrate\Plugin\migrate\source; + +/** + * A dummy source to help with testing SqlBase. + * + * @package Drupal\migrate\Plugin\migrate\source + */ +class TestSqlBase extends SqlBase { + + /** + * Overrides the constructor so we can create one easily. + */ + public function __construct() { + $this->state = \Drupal::state(); + } + + /** + * Gets the database without caching it. + */ + public function getDatabase() { + $this->database = NULL; + return parent::getDatabase(); + } + + /** + * Allows us to set the configuration from a test. + * + * @param array $config + * The config array. + */ + public function setConfiguration($config) { + $this->configuration = $config; + } + + /** + * {@inheritdoc} + */ + public function getIds() {} + + /** + * {@inheritdoc} + */ + public function fields() {} + + /** + * {@inheritdoc} + */ + public function query() {} + +} diff --git a/core/modules/migrate/tests/src/Unit/Entity/MigrationTest.php b/core/modules/migrate/tests/src/Unit/Entity/MigrationTest.php deleted file mode 100644 index 73ab716..0000000 --- a/core/modules/migrate/tests/src/Unit/Entity/MigrationTest.php +++ /dev/null @@ -1,31 +0,0 @@ -assertEquals([], $migration->getProcessPlugins([])); - } - -} diff --git a/core/modules/migrate/tests/src/Unit/MigrateExecutableMemoryExceededTest.php b/core/modules/migrate/tests/src/Unit/MigrateExecutableMemoryExceededTest.php index d0c1604..cbe62b7 100644 --- a/core/modules/migrate/tests/src/Unit/MigrateExecutableMemoryExceededTest.php +++ b/core/modules/migrate/tests/src/Unit/MigrateExecutableMemoryExceededTest.php @@ -17,7 +17,7 @@ class MigrateExecutableMemoryExceededTest extends MigrateTestCase { /** * The mocked migration entity. * - * @var \Drupal\migrate\Entity\MigrationInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\migrate\Plugin\MigrationInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $migration; diff --git a/core/modules/migrate/tests/src/Unit/MigrateExecutableTest.php b/core/modules/migrate/tests/src/Unit/MigrateExecutableTest.php index 8335e1f..bfc0fd0 100644 --- a/core/modules/migrate/tests/src/Unit/MigrateExecutableTest.php +++ b/core/modules/migrate/tests/src/Unit/MigrateExecutableTest.php @@ -8,7 +8,7 @@ namespace Drupal\Tests\migrate\Unit; use Drupal\Component\Utility\Html; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\MigrateIdMapInterface; use Drupal\migrate\MigrateException; use Drupal\migrate\Row; @@ -22,7 +22,7 @@ class MigrateExecutableTest extends MigrateTestCase { /** * The mocked migration entity. * - * @var \Drupal\migrate\Entity\MigrationInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\migrate\Plugin\MigrationInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $migration; diff --git a/core/modules/migrate/tests/src/Unit/MigrateSourceTest.php b/core/modules/migrate/tests/src/Unit/MigrateSourceTest.php index c071b15..65edd55 100644 --- a/core/modules/migrate/tests/src/Unit/MigrateSourceTest.php +++ b/core/modules/migrate/tests/src/Unit/MigrateSourceTest.php @@ -49,7 +49,7 @@ class MigrateSourceTest extends MigrateTestCase { /** * The migration entity. * - * @var \Drupal\migrate\Entity\Migration + * @var \Drupal\migrate\Plugin\MigrationInterface */ protected $migration; @@ -371,7 +371,7 @@ public function testPrepareRowPrepareException() { /** * Gets a mock executable for the test. * - * @param \Drupal\migrate\Entity\MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration entity. * * @return \Drupal\migrate\MigrateExecutable diff --git a/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php index 48be830..5f57607 100644 --- a/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php +++ b/core/modules/migrate/tests/src/Unit/MigrateSqlIdMapTest.php @@ -8,7 +8,7 @@ namespace Drupal\Tests\migrate\Unit; use Drupal\Core\Database\Driver\sqlite\Connection; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\MigrateException; use Drupal\migrate\Plugin\MigrateIdMapInterface; use Drupal\migrate\Row; diff --git a/core/modules/migrate/tests/src/Unit/MigrateTestCase.php b/core/modules/migrate/tests/src/Unit/MigrateTestCase.php index c9e4a97..f5b6de3 100644 --- a/core/modules/migrate/tests/src/Unit/MigrateTestCase.php +++ b/core/modules/migrate/tests/src/Unit/MigrateTestCase.php @@ -9,7 +9,7 @@ use Drupal\Core\Database\Driver\sqlite\Connection; use Drupal\Core\DependencyInjection\ContainerBuilder; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\Tests\UnitTestCase; /** @@ -34,18 +34,18 @@ /** * Local store for mocking setStatus()/getStatus(). * - * @var \Drupal\migrate\Entity\MigrationInterface::STATUS_* + * @var \Drupal\migrate\Plugin\MigrationInterface::STATUS_* */ protected $migrationStatus = MigrationInterface::STATUS_IDLE; /** * Retrieves a mocked migration. * - * @return \Drupal\migrate\Entity\MigrationInterface|\PHPUnit_Framework_MockObject_MockObject + * @return \Drupal\migrate\Plugin\MigrationInterface|\PHPUnit_Framework_MockObject_MockObject * The mocked migration. */ protected function getMigration() { - $this->migrationConfiguration += ['migrationClass' => 'Drupal\migrate\Entity\Migration']; + $this->migrationConfiguration += ['migrationClass' => 'Drupal\migrate\Plugin\Migration']; $this->idMap = $this->getMock('Drupal\migrate\Plugin\MigrateIdMapInterface'); $this->idMap @@ -83,9 +83,9 @@ protected function getMigration() { $configuration = &$this->migrationConfiguration; - $migration->method('get') - ->willReturnCallback(function ($argument) use (&$configuration) { - return isset($configuration[$argument]) ? $configuration[$argument] : ''; + $migration->method('getHighWaterProperty') + ->willReturnCallback(function () use ($configuration) { + return isset($configuration['highWaterProperty']) ? $configuration['highWaterProperty'] : ''; }); $migration->method('set') diff --git a/core/modules/migrate/tests/src/Unit/MigrationTest.php b/core/modules/migrate/tests/src/Unit/MigrationTest.php index 2693dec..57ff64c 100644 --- a/core/modules/migrate/tests/src/Unit/MigrationTest.php +++ b/core/modules/migrate/tests/src/Unit/MigrationTest.php @@ -7,16 +7,18 @@ namespace Drupal\Tests\migrate\Unit; -use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\migrate\Entity\Migration; +use Drupal\migrate\Plugin\MigrationInterface; +use Drupal\migrate\Plugin\Migration; use Drupal\migrate\Exception\RequirementsException; use Drupal\migrate\Plugin\MigrateDestinationInterface; use Drupal\migrate\Plugin\MigrateSourceInterface; +use Drupal\migrate\Plugin\MigrationPluginManagerInterface; use Drupal\migrate\Plugin\RequirementsInterface; use Drupal\Tests\UnitTestCase; /** - * @coversDefaultClass \Drupal\migrate\Entity\Migration + * @coversDefaultClass \Drupal\migrate\Plugin\Migration + * * @group Migration */ class MigrationTest extends UnitTestCase { @@ -84,16 +86,16 @@ public function testRequirementsForMigrations() { $migration->setSourcePlugin($source_plugin); $migration->setDestinationPlugin($destination_plugin); - $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); - $migration->setEntityManager($entity_manager); + $plugin_manager = $this->getMock('Drupal\migrate\Plugin\MigrationPluginManagerInterface'); + $migration->setMigrationPluginManager($plugin_manager); // We setup the requirements that test_a doesn't exist and test_c is not // completed yet. $migration->setRequirements(['test_a', 'test_b', 'test_c', 'test_d']); - $migration_b = $this->getMock('Drupal\migrate\Entity\MigrationInterface'); - $migration_c = $this->getMock('Drupal\migrate\Entity\MigrationInterface'); - $migration_d = $this->getMock('Drupal\migrate\Entity\MigrationInterface'); + $migration_b = $this->getMock(MigrationInterface::class); + $migration_c = $this->getMock(MigrationInterface::class); + $migration_d = $this->getMock(MigrationInterface::class); $migration_b->expects($this->once()) ->method('allRowsProcessed') @@ -105,15 +107,10 @@ public function testRequirementsForMigrations() { ->method('allRowsProcessed') ->willReturn(TRUE); - $migration_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface'); - $migration_storage->expects($this->once()) - ->method('loadMultiple') + $plugin_manager->expects($this->once()) + ->method('createInstances') ->with(['test_a', 'test_b', 'test_c', 'test_d']) ->willReturn(['test_b' => $migration_b, 'test_c' => $migration_c, 'test_d' => $migration_d]); - $entity_manager->expects($this->once()) - ->method('getStorage') - ->with('migration') - ->willReturn($migration_storage); $migration->checkRequirements(); } @@ -162,13 +159,13 @@ public function setDestinationPlugin(MigrateDestinationInterface $destination_pl } /** - * Sets the entity manager service. + * Sets the plugin manager service. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager service. + * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $plugin_manager + * The plugin manager service. */ - public function setEntityManager(EntityManagerInterface $entity_manager) { - $this->entityManager = $entity_manager; + public function setMigrationPluginManager(MigrationPluginManagerInterface $plugin_manager) { + $this->migrationPluginManager = $plugin_manager; } } diff --git a/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php b/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php index 6d511fa..d08e035 100644 --- a/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php +++ b/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php @@ -11,7 +11,7 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\migrate\destination\EntityContentBase; use Drupal\migrate\Plugin\MigrateIdMapInterface; use Drupal\migrate\Row; @@ -26,7 +26,7 @@ class EntityContentBaseTest extends UnitTestCase { /** - * @var \Drupal\migrate\Entity\MigrationInterface + * @var \Drupal\migrate\Plugin\MigrationInterface */ protected $migration; diff --git a/core/modules/migrate/tests/src/Unit/SqlBaseTest.php b/core/modules/migrate/tests/src/Unit/SqlBaseTest.php index 936d00f..b7dc5ed 100644 --- a/core/modules/migrate/tests/src/Unit/SqlBaseTest.php +++ b/core/modules/migrate/tests/src/Unit/SqlBaseTest.php @@ -7,6 +7,7 @@ namespace Drupal\Tests\migrate\Unit; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\migrate\source\SqlBase; use Drupal\Tests\UnitTestCase; @@ -61,7 +62,7 @@ public function testMapJoinable($expected_result, $id_map_is_sql, $with_id_map, ->willReturn($idmap_connection); // Setup a migration entity. - $migration = $this->getMock('Drupal\migrate\Entity\MigrationInterface'); + $migration = $this->getMock(MigrationInterface::class); $migration->expects($with_id_map ? $this->once() : $this->never()) ->method('getIdMap') ->willReturn($id_map_is_sql ? $sql : NULL); diff --git a/core/modules/migrate/tests/src/Unit/TestSqlIdMap.php b/core/modules/migrate/tests/src/Unit/TestSqlIdMap.php index de730f4..309865b 100644 --- a/core/modules/migrate/tests/src/Unit/TestSqlIdMap.php +++ b/core/modules/migrate/tests/src/Unit/TestSqlIdMap.php @@ -8,7 +8,7 @@ namespace Drupal\Tests\migrate\Unit; use Drupal\Core\Database\Connection; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\MigrateException; use Drupal\migrate\Plugin\migrate\id_map\Sql; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -29,7 +29,7 @@ class TestSqlIdMap extends Sql implements \Iterator { * The plugin ID for the migration process to do. * @param mixed $plugin_definition * The configuration for the plugin. - * @param \Drupal\migrate\Entity\MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration to do. * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher * The event dispatcher service. diff --git a/core/modules/migrate/tests/src/Unit/destination/ConfigTest.php b/core/modules/migrate/tests/src/Unit/destination/ConfigTest.php index f5e1c33..f990ccb 100644 --- a/core/modules/migrate/tests/src/Unit/destination/ConfigTest.php +++ b/core/modules/migrate/tests/src/Unit/destination/ConfigTest.php @@ -7,6 +7,7 @@ namespace Drupal\Tests\migrate\Unit\destination; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\migrate\destination\Config; use Drupal\Tests\UnitTestCase; @@ -23,7 +24,7 @@ public function testImport() { $source = array( 'test' => 'x', ); - $migration = $this->getMockBuilder('Drupal\migrate\Entity\Migration') + $migration = $this->getMockBuilder('Drupal\migrate\Plugin\Migration') ->disableOriginalConstructor() ->getMock(); $config = $this->getMockBuilder('Drupal\Core\Config\Config') @@ -73,7 +74,7 @@ public function testLanguageImport() { $source = array( 'langcode' => 'mi', ); - $migration = $this->getMockBuilder('Drupal\migrate\Entity\Migration') + $migration = $this->getMockBuilder(MigrationInterface::class) ->disableOriginalConstructor() ->getMock(); $config = $this->getMockBuilder('Drupal\Core\Config\Config') diff --git a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php index ec3d0f2..f1e288e 100644 --- a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php +++ b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php @@ -9,6 +9,7 @@ use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\migrate\destination\EntityRevision as RealEntityRevision; use Drupal\migrate\Row; use Drupal\Tests\UnitTestCase; @@ -22,7 +23,7 @@ class EntityRevisionTest extends UnitTestCase { /** - * @var \Drupal\migrate\Entity\MigrationInterface + * @var \Drupal\migrate\Plugin\MigrationInterface */ protected $migration; @@ -45,7 +46,7 @@ protected function setUp() { parent::setUp(); // Setup mocks to be used when creating a revision destination. - $this->migration = $this->prophesize('\Drupal\migrate\Entity\MigrationInterface'); + $this->migration = $this->prophesize(MigrationInterface::class); $this->storage = $this->prophesize('\Drupal\Core\Entity\EntityStorageInterface'); $this->entityManager = $this->prophesize('\Drupal\Core\Entity\EntityManagerInterface'); $this->fieldTypeManager = $this->prophesize('\Drupal\Core\Field\FieldTypePluginManagerInterface'); diff --git a/core/modules/migrate/tests/src/Unit/process/MigrationTest.php b/core/modules/migrate/tests/src/Unit/process/MigrationTest.php index 03d7bd9..65f62da 100644 --- a/core/modules/migrate/tests/src/Unit/process/MigrationTest.php +++ b/core/modules/migrate/tests/src/Unit/process/MigrationTest.php @@ -7,14 +7,13 @@ namespace Drupal\Tests\migrate\Unit\process; -use Drupal\Core\Entity\EntityStorageInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\migrate\process\Migration; -use Drupal\migrate\Plugin\migrate\source\SourcePluginBase; use Drupal\migrate\Plugin\MigrateDestinationInterface; use Drupal\migrate\Plugin\MigrateIdMapInterface; use Drupal\migrate\Plugin\MigratePluginManager; use Drupal\migrate\Plugin\MigrateSourceInterface; +use Drupal\migrate\Plugin\MigrationPluginManagerInterface; use Prophecy\Argument; /** @@ -27,26 +26,28 @@ class MigrationTest extends MigrateProcessTestCase { * @covers ::transform */ public function testTransformWithStubSkipping() { - $migration_entity = $this->prophesize(MigrationInterface::class); - $migration_storage = $this->prophesize(EntityStorageInterface::class); + $migration_plugin = $this->prophesize(MigrationInterface::class); + $migration_plugin_manager = $this->prophesize(MigrationPluginManagerInterface::class); $process_plugin_manager = $this->prophesize(MigratePluginManager::class); $destination_id_map = $this->prophesize(MigrateIdMapInterface::class); $destination_migration = $this->prophesize(MigrationInterface::class); $destination_migration->getIdMap()->willReturn($destination_id_map->reveal()); - $migration_storage->loadMultiple(['destination_migration']) - ->willReturn(['destination_migration' => $destination_migration->reveal()]); $destination_id_map->lookupDestinationId([1])->willReturn(NULL); + // Ensure the migration plugin manager returns our migration. + $migration_plugin_manager->createInstances(Argument::exact(['destination_migration'])) + ->willReturn(['destination_migration' => $destination_migration->reveal()]); + $configuration = [ 'no_stub' => TRUE, 'migration' => 'destination_migration', ]; - $migration_entity->id()->willReturn('actual_migration'); + $migration_plugin->id()->willReturn('actual_migration'); $destination_migration->getDestinationPlugin(TRUE)->shouldNotBeCalled(); - $migration = new Migration($configuration, '', [], $migration_entity->reveal(), $migration_storage->reveal(), $process_plugin_manager->reveal()); + $migration = new Migration($configuration, '', [], $migration_plugin->reveal(), $migration_plugin_manager->reveal(), $process_plugin_manager->reveal()); $result = $migration->transform(1, $this->migrateExecutable, $this->row, ''); $this->assertNull($result); } @@ -55,27 +56,28 @@ public function testTransformWithStubSkipping() { * @covers ::transform */ public function testTransformWithStubbing() { - $migration_entity = $this->prophesize(MigrationInterface::class); - $migration_storage = $this->prophesize(EntityStorageInterface::class); + $migration_plugin = $this->prophesize(MigrationInterface::class); + $migration_plugin_manager = $this->prophesize(MigrationPluginManagerInterface::class); $process_plugin_manager = $this->prophesize(MigratePluginManager::class); $destination_id_map = $this->prophesize(MigrateIdMapInterface::class); - $destination_migration = $this->prophesize(MigrationInterface::class); + $destination_migration = $this->prophesize('Drupal\migrate\Plugin\Migration'); $destination_migration->getIdMap()->willReturn($destination_id_map->reveal()); - $migration_storage->loadMultiple(['destination_migration']) + $migration_plugin_manager->createInstances(['destination_migration']) ->willReturn(['destination_migration' => $destination_migration->reveal()]); $destination_id_map->lookupDestinationId([1])->willReturn(NULL); + $destination_id_map->saveIdMapping(Argument::any(), Argument::any(), MigrateIdMapInterface::STATUS_NEEDS_UPDATE)->willReturn(NULL); $configuration = [ 'no_stub' => FALSE, 'migration' => 'destination_migration', ]; - $migration_entity->id()->willReturn('actual_migration'); + $migration_plugin->id()->willReturn('actual_migration'); $destination_migration->id()->willReturn('destination_migration'); $destination_migration->getDestinationPlugin(TRUE)->shouldBeCalled(); - $destination_migration->get('process')->willReturn([]); - $destination_migration->get('source')->willReturn([]); + $destination_migration->getProcess()->willReturn([]); + $destination_migration->getSourceConfiguration()->willReturn([]); $source_plugin = $this->prophesize(MigrateSourceInterface::class); $source_plugin->getIds()->willReturn(['nid']); @@ -84,7 +86,7 @@ public function testTransformWithStubbing() { $destination_plugin->import(Argument::any())->willReturn([2]); $destination_migration->getDestinationPlugin(TRUE)->willReturn($destination_plugin->reveal()); - $migration = new Migration($configuration, '', [], $migration_entity->reveal(), $migration_storage->reveal(), $process_plugin_manager->reveal()); + $migration = new Migration($configuration, '', [], $migration_plugin->reveal(), $migration_plugin_manager->reveal(), $process_plugin_manager->reveal()); $result = $migration->transform(1, $this->migrateExecutable, $this->row, ''); $this->assertEquals(2, $result); } diff --git a/core/modules/migrate/tests/src/Unit/process/SkipOnEmptyTest.php b/core/modules/migrate/tests/src/Unit/process/SkipOnEmptyTest.php index 3a0d08c..beea773 100644 --- a/core/modules/migrate/tests/src/Unit/process/SkipOnEmptyTest.php +++ b/core/modules/migrate/tests/src/Unit/process/SkipOnEmptyTest.php @@ -5,7 +5,6 @@ * Contains \Drupal\Tests\migrate\Unit\process\SkipOnEmptyTest. */ - namespace Drupal\Tests\migrate\Unit\process; use Drupal\migrate\Plugin\migrate\process\SkipOnEmpty; diff --git a/core/modules/migrate_drupal/config/schema/migrate_drupal.source.schema.yml b/core/modules/migrate_drupal/config/schema/migrate_drupal.source.schema.yml deleted file mode 100644 index 96f5e65..0000000 --- a/core/modules/migrate_drupal/config/schema/migrate_drupal.source.schema.yml +++ /dev/null @@ -1,192 +0,0 @@ -# Schema for the migrate_drupal source plugins. - -migrate.source.variable: - type: migrate_source_sql - label: 'Variable' - mapping: - variables: - type: sequence - label: 'Variables' - sequence: - type: string - label: 'Variable' - constants: - type: mapping - label: 'Constants' - mapping: - entity_type: - type: string - label: 'Entity type' - id: - type: string - label: 'ID' - label: - type: label - label: 'Label' - description: - type: text - label: 'Description' - path: - type: string - label: 'Path' - plugin: - type: string - label: 'Plugin' - status: - type: boolean - label: 'Status' - slash: - type: string - label: 'Slash' - -migrate.source.variable_multirow: - type: migrate_source_sql - label: 'Drupal variable multirow' - mapping: - variables: - type: sequence - label: 'Variables' - sequence: - type: string - label: 'Variable' - -migrate_entity_constant: - type: mapping - mapping: - entity_type: - type: string - label: 'Entity type' - bundle: - type: string - label: 'Bundle' - label: - type: label - label: 'Label' - id: - type: string - label: 'ID' - target_entity_type: - type: string - label: 'Target entity type' - view_mode: - type: string - label: 'View mode' - form_mode: - type: string - label: 'Form mode' - field_name: - type: string - label: 'Field name' - empty: - type: sequence - label: 'Empty' - sequence: - type: string - label: 'Empty' - name: - type: string - label: 'Name' - preview: - type: integer - label: 'Preview' - create_body: - type: boolean - label: 'create body' - required: - type: boolean - label: 'Required' - type: - type: string - label: 'Type' - cardinality: - type: integer - label: 'Cardinality' - parent: - type: integer - label: 'Parent' - langcode: - type: string - label: 'Language code' - third_party_settings: - type: sequence - label: 'Settings' - sequence: - type: ignore - label: 'Settings' - settings: - type: sequence - label: 'Settings' - sequence: - type: ignore - label: 'Settings' - options: - type: mapping - label: 'Options' - mapping: - label: - type: string - label: 'label' - type: - type: string - label: 'Type' - weight: - type: integer - label: 'Weight' - settings: - type: sequence - label: 'Settings' - sequence: - type: string - label: 'Settings' - selection_handler: - type: string - label: 'Entity reference selection handler' - auto_create: - type: boolean - label: 'Entity reference selection setting: Auto-create new entities' - status: - type: boolean - label: 'Status' - -migrate.source.md_empty: - type: migrate.source.empty - label: 'Empty source for migrate_drupal migrations' - -migrate.source.i18n_variable: - type: migrate_source_sql - label: 'i18n Variable' - mapping: - variables: - type: sequence - label: 'Variables' - sequence: - type: string - label: 'Variable' - constants: - type: mapping - label: 'Constants' - mapping: - entity_type: - type: string - label: 'Entity type' - id: - type: string - label: 'ID' - label: - type: label - label: 'Label' - description: - type: text - label: 'Description' - path: - type: string - label: 'Path' - plugin: - type: string - label: 'Plugin' - status: - type: boolean - label: 'Status' - slash: - type: string - label: 'Slash' diff --git a/core/modules/migrate_drupal/migrate_drupal.module b/core/modules/migrate_drupal/migrate_drupal.module index f118954..dc3ab4e 100644 --- a/core/modules/migrate_drupal/migrate_drupal.module +++ b/core/modules/migrate_drupal/migrate_drupal.module @@ -5,7 +5,12 @@ * Provides migration from other Drupal sites. */ +use Drupal\Core\Database\DatabaseExceptionWrapper; use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\migrate\Exception\RequirementsException; +use Drupal\migrate\MigrateExecutable; +use Drupal\migrate\MigrateMessage; +use Drupal\migrate\Plugin\RequirementsInterface; /** * Implements hook_help(). @@ -19,3 +24,53 @@ function migrate_drupal_help($route_name, RouteMatchInterface $route_match) { return $output; } } + +/** + * Implements hook_migration_plugins_alter(). + */ +function migrate_drupal_migration_plugins_alter(&$definitions) { + // This is why the deriver can't do this: the 'd6_taxonomy_vocabulary' + // definition is not available to the deriver as it is running inside + // getDefinitions(). + if (isset($definitions['d6_taxonomy_vocabulary'])) { + $vocabulary_migration_definition = [ + 'source' => [ + 'ignore_map' => TRUE, + 'plugin' => 'd6_taxonomy_vocabulary', + ], + 'destination' => [ + 'plugin' => 'null', + ], + ]; + $vocabulary_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($vocabulary_migration_definition); + + try { + $source_plugin = $vocabulary_migration->getSourcePlugin(); + if ($source_plugin instanceof RequirementsInterface) { + $source_plugin->checkRequirements(); + } + $executable = new MigrateExecutable($vocabulary_migration, new MigrateMessage()); + $process = ['vid' => $definitions['d6_taxonomy_vocabulary']['process']['vid']]; + foreach ($source_plugin as $row) { + $executable->processRow($row, $process); + $source_vid = $row->getSourceProperty('vid'); + $plugin_ids = ['d6_term_node:' . $source_vid, 'd6_term_node_revision:' . $source_vid]; + foreach ($plugin_ids as $plugin_id) { + if (isset($definitions[$plugin_id])) { + $definitions[$plugin_id]['process'][$row->getDestinationProperty('vid')] = 'tid'; + } + } + } + } + catch (RequirementsException $e) { + // This code currently runs whenever the definitions are being loaded and + // if you have a Drupal 7 source site then the requirements will not be + // met for the d6_taxonomy_vocabulary migration. + } + catch (DatabaseExceptionWrapper $e) { + // When the definitions are loaded it is possible the tables will not + // exist. + } + + } +} diff --git a/core/modules/migrate_drupal/src/MigrationCreationTrait.php b/core/modules/migrate_drupal/src/MigrationCreationTrait.php new file mode 100644 index 0000000..8528ae7 --- /dev/null +++ b/core/modules/migrate_drupal/src/MigrationCreationTrait.php @@ -0,0 +1,178 @@ +select('system', 's', [ + 'fetch' => \PDO::FETCH_ASSOC, + ]) + ->fields('s') + ->execute(); + foreach ($results as $result) { + $system_data[$result['type']][$result['name']] = $result; + } + } + catch (\Exception $e) { + // The table might not exist for example in tests. + } + return $system_data; + } + + /** + * Creates the necessary state entries for SqlBase::getDatabase() to work. + * + * The state entities created here have to exist before migration plugin + * instances are created so that derivers such as + * \Drupal\taxonomy\Plugin\migrate\D6TermNodeDeriver can access the source + * database. + * + * @param array $database + * The source database settings. + * @param string $drupal_version + * The Drupal version. + * + * @see \Drupal\migrate\Plugin\migrate\source\SqlBase::getDatabase() + */ + protected function createDatabaseStateSettings(array $database, $drupal_version) { + $database_state['key'] = 'upgrade'; + $database_state['database'] = $database; + $database_state_key = 'migrate_drupal_' . $drupal_version; + \Drupal::state()->set($database_state_key, $database_state); + \Drupal::state()->set('migrate.fallback_state_key', $database_state_key); + } + + /** + * Gets the migrations for import. + * + * @param string $database_state_key + * The state key. + * @param int $drupal_version + * The version of Drupal we're getting the migrations for. + * + * @return \Drupal\migrate\Plugin\MigrationInterface[] + * The migrations for import. + */ + protected function getMigrations($database_state_key, $drupal_version) { + $version_tag = 'Drupal ' . $drupal_version; + $plugin_manager = \Drupal::service('plugin.manager.migration'); + /** @var \Drupal\migrate\Plugin\Migration[] $all_migrations */ + $all_migrations = $plugin_manager->createInstancesByTag($version_tag); + $migrations = []; + foreach ($all_migrations as $migration) { + try { + // @todo https://drupal.org/node/2681867 We should be able to validate + // the entire migration at this point. + $source_plugin = $migration->getSourcePlugin(); + if ($source_plugin instanceof RequirementsInterface) { + $source_plugin->checkRequirements(); + } + $destination_plugin = $migration->getDestinationPlugin(); + if ($destination_plugin instanceof RequirementsInterface) { + $destination_plugin->checkRequirements(); + } + $migrations[] = $migration; + } + catch (RequirementsException $e) { + // Migrations which are not applicable given the source and destination + // site configurations (e.g., what modules are enabled) will be silently + // ignored. + } + } + + return $migrations; + } + + /** + * Determines what version of Drupal the source database contains. + * + * @param \Drupal\Core\Database\Connection $connection + * The database connection object. + * + * @return int|FALSE + * An integer representing the major branch of Drupal core (e.g. '6' for + * Drupal 6.x), or FALSE if no valid version is matched. + */ + protected function getLegacyDrupalVersion(Connection $connection) { + // Don't assume because a table of that name exists, that it has the columns + // we're querying. Catch exceptions and report that the source database is + // not Drupal. + // Drupal 5/6/7 can be detected by the schema_version in the system table. + if ($connection->schema()->tableExists('system')) { + try { + $version_string = $connection + ->query('SELECT schema_version FROM {system} WHERE name = :module', [':module' => 'system']) + ->fetchField(); + if ($version_string && $version_string[0] == '1') { + if ((int) $version_string >= 1000) { + $version_string = '5'; + } + else { + $version_string = FALSE; + } + } + } + catch (\PDOException $e) { + $version_string = FALSE; + } + } + // For Drupal 8 (and we're predicting beyond) the schema version is in the + // key_value store. + elseif ($connection->schema()->tableExists('key_value')) { + $result = $connection + ->query("SELECT value FROM {key_value} WHERE collection = :system_schema and name = :module", [':system_schema' => 'system.schema', ':module' => 'system']) + ->fetchField(); + $version_string = unserialize($result); + } + else { + $version_string = FALSE; + } + + return $version_string ? substr($version_string, 0, 1) : FALSE; + } + +} diff --git a/core/modules/migrate_drupal/src/Plugin/MigrateCckFieldInterface.php b/core/modules/migrate_drupal/src/Plugin/MigrateCckFieldInterface.php index dbe499d..b69c422 100644 --- a/core/modules/migrate_drupal/src/Plugin/MigrateCckFieldInterface.php +++ b/core/modules/migrate_drupal/src/Plugin/MigrateCckFieldInterface.php @@ -8,7 +8,7 @@ namespace Drupal\migrate_drupal\Plugin; use Drupal\Component\Plugin\PluginInspectionInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; /** @@ -19,7 +19,7 @@ /** * Apply any custom processing to the field migration. * - * @param \Drupal\migrate\Entity\MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration entity. */ public function processField(MigrationInterface $migration); @@ -27,7 +27,7 @@ public function processField(MigrationInterface $migration); /** * Apply any custom processing to the field instance migration. * - * @param \Drupal\migrate\Entity\MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration entity. */ public function processFieldInstance(MigrationInterface $migration); @@ -35,7 +35,7 @@ public function processFieldInstance(MigrationInterface $migration); /** * Apply any custom processing to the field widget migration. * - * @param \Drupal\migrate\Entity\MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration entity. */ public function processFieldWidget(MigrationInterface $migration); @@ -43,7 +43,7 @@ public function processFieldWidget(MigrationInterface $migration); /** * Apply any custom processing to the field formatter migration. * - * @param \Drupal\migrate\Entity\MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration entity. */ public function processFieldFormatter(MigrationInterface $migration); @@ -69,7 +69,7 @@ public function getFieldWidgetMap(); /** * Apply any custom processing to the cck bundle migrations. * - * @param \Drupal\migrate\Entity\MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration entity. * @param string $field_name * The field name we're processing the value for. diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/CckMigration.php b/core/modules/migrate_drupal/src/Plugin/migrate/CckMigration.php new file mode 100644 index 0000000..6f3ba30 --- /dev/null +++ b/core/modules/migrate_drupal/src/Plugin/migrate/CckMigration.php @@ -0,0 +1,121 @@ +cckPluginManager = $cck_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('plugin.manager.migrate.cckfield'), + $container->get('plugin.manager.migration'), + $container->get('plugin.manager.migrate.source'), + $container->get('plugin.manager.migrate.process'), + $container->get('plugin.manager.migrate.destination'), + $container->get('plugin.manager.migrate.id_map') + ); + } + + /** + * {@inheritdoc} + */ + public function getProcess() { + if (!$this->init) { + $this->init = TRUE; + $source_plugin = $this->migrationPluginManager->createInstance($this->pluginId)->getSourcePlugin(); + if ($source_plugin instanceof RequirementsInterface) { + try { + $source_plugin->checkRequirements(); + } + catch (RequirementsException $e) { + // Kill the rest of the method. + $source_plugin = []; + } + } + foreach ($source_plugin as $row) { + $field_type = $row->getSourceProperty('type'); + if (!isset($this->processedFieldTypes[$field_type]) && $this->cckPluginManager->hasDefinition($field_type)) { + $this->processedFieldTypes[$field_type] = TRUE; + // Allow the cckfield plugin to alter the migration as necessary so + // that it knows how to handle fields of this type. + if (!isset($this->cckPluginCache[$field_type])) { + $this->cckPluginCache[$field_type] = $this->cckPluginManager->createInstance($field_type, [], $this); + } + call_user_func([$this->cckPluginCache[$field_type], $this->pluginDefinition['cck_plugin_method']], $this); + } + } + } + return parent::getProcess(); + } + +} diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/builder/CckBuilder.php b/core/modules/migrate_drupal/src/Plugin/migrate/builder/CckBuilder.php deleted file mode 100644 index 50b1fcd..0000000 --- a/core/modules/migrate_drupal/src/Plugin/migrate/builder/CckBuilder.php +++ /dev/null @@ -1,82 +0,0 @@ -cckPluginManager = $cck_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('plugin.manager.migrate.cckfield') - ); - } - - /** - * Gets a cckfield plugin instance. - * - * @param string $field_type - * The field type (plugin ID). - * @param \Drupal\migrate\Entity\MigrationInterface|NULL $migration - * The migration, if any. - * - * @return \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface - * The cckfield plugin instance. - */ - protected function getCckPlugin($field_type, MigrationInterface $migration = NULL) { - if (empty($this->cckPluginCache[$field_type])) { - $this->cckPluginCache[$field_type] = $this->cckPluginManager->createInstance($field_type, [], $migration); - } - return $this->cckPluginCache[$field_type]; - } - -} diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/builder/d6/CckMigration.php b/core/modules/migrate_drupal/src/Plugin/migrate/builder/d6/CckMigration.php deleted file mode 100644 index 30b739e..0000000 --- a/core/modules/migrate_drupal/src/Plugin/migrate/builder/d6/CckMigration.php +++ /dev/null @@ -1,68 +0,0 @@ -getSourcePlugin(); - // The source plugin will throw RequirementsException if CCK is not enabled, - // in which case there is nothing else for us to do. - if ($source_plugin instanceof RequirementsInterface) { - try { - $source_plugin->checkRequirements(); - } - catch (RequirementsException $e) { - return [$migration]; - } - } - - // Loop through every field that will be migrated. - foreach ($source_plugin as $field) { - $field_type = $field->getSourceProperty('type'); - - // Each field type should only be processed once. - if (in_array($field_type, $this->processedFieldTypes)) { - continue; - } - // Only process the current field type if a relevant cckfield plugin - // exists. - elseif ($this->cckPluginManager->hasDefinition($field_type)) { - $this->processedFieldTypes[] = $field_type; - // Allow the cckfield plugin to alter the migration as necessary so that - // it knows how to handle fields of this type. - $this->cckPluginManager - ->createInstance($field_type, [], $migration) - ->{$this->configuration['cck_plugin_method']}($migration); - } - } - - return [$migration]; - } - -} diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/cckfield/CckFieldPluginBase.php b/core/modules/migrate_drupal/src/Plugin/migrate/cckfield/CckFieldPluginBase.php index 7667c7f..587f789 100644 --- a/core/modules/migrate_drupal/src/Plugin/migrate/cckfield/CckFieldPluginBase.php +++ b/core/modules/migrate_drupal/src/Plugin/migrate/cckfield/CckFieldPluginBase.php @@ -8,7 +8,7 @@ namespace Drupal\migrate_drupal\Plugin\migrate\cckfield; use Drupal\Core\Plugin\PluginBase; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; use Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface; diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/destination/EntityFieldStorageConfig.php b/core/modules/migrate_drupal/src/Plugin/migrate/destination/EntityFieldStorageConfig.php index 50b004c..8756dd1 100644 --- a/core/modules/migrate_drupal/src/Plugin/migrate/destination/EntityFieldStorageConfig.php +++ b/core/modules/migrate_drupal/src/Plugin/migrate/destination/EntityFieldStorageConfig.php @@ -10,7 +10,7 @@ use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\migrate\destination\EntityFieldStorageConfig as BaseEntityFieldStorageConfig; /** @@ -38,7 +38,7 @@ class EntityFieldStorageConfig extends BaseEntityFieldStorageConfig { * The plugin_id for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. - * @param MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration. * @param EntityStorageInterface $storage * The storage for this entity type. @@ -75,7 +75,7 @@ public function calculateDependencies() { $this->dependencies = parent::calculateDependencies(); // Add a dependency on the module that provides the field type using the // source plugin configuration. - $source_configuration = $this->migration->get('source'); + $source_configuration = $this->migration->getSourceConfiguration(); if (isset($source_configuration['constants']['type'])) { $field_type = $this->fieldTypePluginManager->getDefinition($source_configuration['constants']['type']); $this->addDependency('module', $field_type['provider']); diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php index bb7f452..75dd5d4 100644 --- a/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php +++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php @@ -12,7 +12,7 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\State\StateInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Exception\RequirementsException; use Drupal\migrate\Plugin\migrate\source\SqlBase; use Drupal\migrate\Plugin\RequirementsInterface; diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/EmptySource.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/EmptySource.php index 60a8843..33f73ed 100644 --- a/core/modules/migrate_drupal/src/Plugin/migrate/source/EmptySource.php +++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/EmptySource.php @@ -10,7 +10,7 @@ use Drupal\Component\Plugin\DependentPluginInterface; use Drupal\Core\Entity\DependencyTrait; use Symfony\Component\DependencyInjection\ContainerInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Plugin\migrate\source\EmptySource as BaseEmptySource; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/Variable.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/Variable.php index 4273846..1c2ee2b 100644 --- a/core/modules/migrate_drupal/src/Plugin/migrate/source/Variable.php +++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/Variable.php @@ -9,7 +9,7 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\State\StateInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; /** * Drupal variable source from database. diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/i18nVariable.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/i18nVariable.php index ca51840..ec6cc28 100644 --- a/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/i18nVariable.php +++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/d6/i18nVariable.php @@ -9,7 +9,7 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\State\StateInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase; /** diff --git a/core/modules/migrate_drupal/src/Tests/MigrateDrupalTestBase.php b/core/modules/migrate_drupal/src/Tests/MigrateDrupalTestBase.php deleted file mode 100644 index bb24447..0000000 --- a/core/modules/migrate_drupal/src/Tests/MigrateDrupalTestBase.php +++ /dev/null @@ -1,76 +0,0 @@ -installEntitySchema('user'); - $this->installConfig(['migrate_drupal', 'system']); - } - - /** - * Loads a database fixture into the source database connection. - * - * @param string $path - * Path to the dump file. - */ - protected function loadFixture($path) { - $default_db = Database::getConnection()->getKey(); - Database::setActiveConnection($this->sourceDatabase->getKey()); - - if (substr($path, -3) == '.gz') { - $path = 'compress.zlib://' . $path; - } - require $path; - - Database::setActiveConnection($default_db); - } - - /** - * Turn all the migration templates for the specified drupal version into - * real migration entities so we can test them. - * - * @param string $version - * Drupal version as provided in migration_tags - e.g., 'Drupal 6'. - */ - protected function installMigrations($version) { - $migration_templates = \Drupal::service('migrate.template_storage')->findTemplatesByTag($version); - $migrations = \Drupal::service('migrate.migration_builder')->createMigrations($migration_templates); - foreach ($migrations as $migration) { - try { - $migration->save(); - } - catch (PluginNotFoundException $e) { - // Migrations requiring modules not enabled will throw an exception. - // Ignoring this exception is equivalent to placing config in the - // optional subdirectory - the migrations we require for the test will - // be successfully saved. - } - } - } - -} diff --git a/core/modules/migrate_drupal/src/Tests/StubTestTrait.php b/core/modules/migrate_drupal/src/Tests/StubTestTrait.php index 1e028bf..ef240ff 100644 --- a/core/modules/migrate_drupal/src/Tests/StubTestTrait.php +++ b/core/modules/migrate_drupal/src/Tests/StubTestTrait.php @@ -6,7 +6,7 @@ */ namespace Drupal\migrate_drupal\Tests; -use Drupal\migrate\Entity\Migration; + use Drupal\migrate\Row; /** @@ -45,14 +45,13 @@ protected function performStubTest($entity_type_id) { */ protected function createStub($entity_type_id) { // Create a dummy migration to pass to the destination plugin. - $config = [ - 'id' => 'dummy', + $definition = [ 'migration_tags' => ['Stub test'], 'source' => ['plugin' => 'empty'], 'process' => [], 'destination' => ['plugin' => 'entity:' . $entity_type_id], ]; - $migration = Migration::create($config); + $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); $destination_plugin = $migration->getDestinationPlugin(TRUE); $stub_row = new Row([], [], TRUE); $destination_ids = $destination_plugin->import($stub_row); diff --git a/core/modules/migrate_drupal/src/Tests/d6/CckMigrationBuilderTest.php b/core/modules/migrate_drupal/src/Tests/d6/CckMigrationBuilderTest.php deleted file mode 100644 index fd51c17..0000000 --- a/core/modules/migrate_drupal/src/Tests/d6/CckMigrationBuilderTest.php +++ /dev/null @@ -1,51 +0,0 @@ -update('system') - ->fields(array('status' => 0)) - ->condition('name', 'content') - ->condition('type', 'module') - ->execute(); - $database->schema()->dropTable('content_node_field'); - $database->schema()->dropTable('content_node_field_instance'); - } - - /** - * Tests that the CckMigration builder performs a requirements check on the - * source plugin. - */ - public function testRequirementCheck() { - $template = \Drupal::service('migrate.template_storage') - ->getTemplateByName('d6_field'); - // Without the requirements check, this will throw a \PDOException because - // the CCK tables do not exist. - \Drupal::service('migrate.migration_builder')->createMigrations([$template]); - } - -} diff --git a/core/modules/migrate_drupal/src/Tests/d6/EntityContentBaseTest.php b/core/modules/migrate_drupal/src/Tests/d6/EntityContentBaseTest.php deleted file mode 100644 index 5420fec..0000000 --- a/core/modules/migrate_drupal/src/Tests/d6/EntityContentBaseTest.php +++ /dev/null @@ -1,93 +0,0 @@ - 'signature', - 'entity_type' => 'user', - 'type' => 'text_long', - ])->save(); - - FieldConfig::create([ - 'field_name' => 'signature', - 'entity_type' => 'user', - 'bundle' => 'user', - ])->save(); - - User::create([ - 'uid' => 2, - 'name' => 'Ford Prefect', - 'mail' => 'ford.prefect@localhost', - 'signature' => array( - array( - 'value' => 'Bring a towel.', - 'format' => 'filtered_html', - ), - ), - 'init' => 'proto@zo.an', - ])->save(); - - $this->executeMigrations(['d6_filter_format', 'd6_user_role']); - } - - /** - * Tests overwriting all mapped properties in the destination entity (default - * behavior). - */ - public function testOverwriteAllMappedProperties() { - $this->executeMigration('d6_user'); - /** @var \Drupal\user\UserInterface $account */ - $account = User::load(2); - $this->assertIdentical('john.doe', $account->label()); - $this->assertIdentical('john.doe@example.com', $account->getEmail()); - $this->assertIdentical('doe@example.com', $account->getInitialEmail()); - } - - /** - * Tests overwriting selected properties in the destination entity, specified - * in the destination configuration. - */ - public function testOverwriteProperties() { - // Execute the migration in migrate_overwrite_test, which documents how - // property overwrites work. - $this->executeMigration('users'); - - /** @var \Drupal\user\UserInterface $account */ - $account = User::load(2); - $this->assertIdentical('john.doe', $account->label()); - $this->assertIdentical('john.doe@example.com', $account->getEmail()); - $this->assertIdentical('The answer is 42.', $account->signature->value); - // This value is not overwritten because it's not listed in - // overwrite_properties. - $this->assertIdentical('proto@zo.an', $account->getInitialEmail()); - } - -} diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateDrupal6TestBase.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateDrupal6TestBase.php deleted file mode 100644 index 1d8f5f4..0000000 --- a/core/modules/migrate_drupal/src/Tests/d6/MigrateDrupal6TestBase.php +++ /dev/null @@ -1,130 +0,0 @@ -loadFixture( __DIR__ . '/../../../tests/fixtures/drupal6.php'); - $this->installMigrations('Drupal 6'); - } - - /** - * Executes all user migrations. - * - * @param bool $include_pictures - * If TRUE, migrates user pictures. - */ - protected function migrateUsers($include_pictures = TRUE) { - $this->executeMigrations(['d6_filter_format', 'd6_user_role']); - - if ($include_pictures) { - $this->installEntitySchema('file'); - $this->executeMigrations([ - 'd6_file', - 'd6_user_picture_file', - 'user_picture_field', - 'user_picture_field_instance', - 'user_picture_entity_display', - 'user_picture_entity_form_display', - ]); - } - else { - // These are optional dependencies of d6_user, but we don't need them if - // we're not migrating user pictures. - Migration::load('d6_user_picture_file')->delete(); - Migration::load('user_picture_entity_display')->delete(); - Migration::load('user_picture_entity_form_display')->delete(); - } - - $this->executeMigration('d6_user'); - } - - /** - * Migrates node types. - */ - protected function migrateContentTypes() { - $this->installConfig(['node']); - $this->executeMigration('d6_node_type'); - } - - /** - * Executes all field migrations. - */ - protected function migrateFields() { - $this->migrateContentTypes(); - $this->executeMigrations([ - 'd6_field', - 'd6_field_instance', - 'd6_field_instance_widget_settings', - 'd6_view_modes', - 'd6_field_formatter_settings', - 'd6_upload_field', - 'd6_upload_field_instance', - ]); - } - - /** - * Executes all content migrations. - * - * @param bool $include_revisions - * If TRUE, migrates node revisions. - */ - protected function migrateContent($include_revisions = FALSE) { - $this->migrateUsers(FALSE); - $this->migrateFields(); - - $this->installEntitySchema('node'); - $this->executeMigrations(['d6_node_settings', 'd6_node:*']); - - if ($include_revisions) { - $this->executeMigrations(['d6_node_revision:*']); - } - } - - /** - * Executes all taxonomy migrations. - */ - protected function migrateTaxonomy() { - $this->migrateContentTypes(); - $this->installEntitySchema('taxonomy_term'); - $this->executeMigrations([ - 'd6_taxonomy_vocabulary', - 'd6_vocabulary_field', - 'd6_vocabulary_field_instance', - 'd6_vocabulary_entity_display', - 'd6_vocabulary_entity_form_display', - 'd6_taxonomy_term', - ]); - } - -} diff --git a/core/modules/migrate_drupal/src/Tests/d7/MigrateDrupal7TestBase.php b/core/modules/migrate_drupal/src/Tests/d7/MigrateDrupal7TestBase.php deleted file mode 100644 index 63e8e67..0000000 --- a/core/modules/migrate_drupal/src/Tests/d7/MigrateDrupal7TestBase.php +++ /dev/null @@ -1,26 +0,0 @@ -loadFixture(__DIR__ . '/../../../tests/fixtures/drupal7.php'); - $this->installMigrations('Drupal 7'); - } - -} diff --git a/core/modules/migrate_drupal/src/Tests/dependencies/MigrateDependenciesTest.php b/core/modules/migrate_drupal/src/Tests/dependencies/MigrateDependenciesTest.php deleted file mode 100644 index 028fb90..0000000 --- a/core/modules/migrate_drupal/src/Tests/dependencies/MigrateDependenciesTest.php +++ /dev/null @@ -1,79 +0,0 @@ -assertIdentical(array_keys($migrations), $expected_order); - $expected_requirements = array( - // d6_comment depends on d6_node:*, which the storage controller expands - // into every variant of d6_node created by the MigrationBuilder. - 'd6_node__article', - 'd6_node__company', - 'd6_node__employee', - 'd6_node__event', - 'd6_node__page', - 'd6_node__sponsor', - 'd6_node__story', - 'd6_node__test_event', - 'd6_node__test_page', - 'd6_node__test_planet', - 'd6_node__test_story', - 'd6_node_type', - 'd6_node_settings', - 'd6_filter_format', - 'd6_user', - 'd6_comment_type', - 'd6_comment_entity_display', - 'd6_comment_entity_form_display', - ); - // Migration dependencies for comment include dependencies for node - // migration as well. - $actual_requirements = $migrations['d6_comment']->get('requirements'); - $this->assertIdentical(count($actual_requirements), count($expected_requirements)); - foreach ($expected_requirements as $requirement) { - $this->assertIdentical($actual_requirements[$requirement], $requirement); - } - } - - /** - * Tests dependencies on the migration of aggregator feeds & items. - */ - public function testAggregatorMigrateDependencies() { - /** @var \Drupal\migrate\entity\Migration $migration */ - $migration = Migration::load('d6_aggregator_item'); - $executable = new MigrateExecutable($migration, $this); - $this->startCollectingMessages(); - $executable->import(); - $this->assertEqual($this->migrateMessages['error'], array(SafeMarkup::format('Migration @id did not meet the requirements. Missing migrations d6_aggregator_feed. requirements: d6_aggregator_feed.', array('@id' => $migration->id())))); - $this->collectMessages = FALSE; - } - -} diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal6.php b/core/modules/migrate_drupal/tests/fixtures/drupal6.php index 8f88300..1ca67a5 100644 --- a/core/modules/migrate_drupal/tests/fixtures/drupal6.php +++ b/core/modules/migrate_drupal/tests/fixtures/drupal6.php @@ -8038,7363 +8038,18620 @@ 'mysql_character_set' => 'utf8', )); -$connection->schema()->createTable('imagecache_action', array( - 'fields' => array( - 'actionid' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'size' => 'normal', - 'unsigned' => TRUE, - ), - 'presetid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'size' => 'normal', - 'default' => '0', - 'unsigned' => TRUE, - ), - 'weight' => array( - 'type' => 'int', - 'not null' => TRUE, - 'size' => 'normal', - 'default' => '0', - ), - 'module' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => '255', - ), - 'action' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => '255', - ), - 'data' => array( - 'type' => 'text', - 'not null' => TRUE, - 'size' => 'normal', - ), - ), - 'primary key' => array( - 'actionid', - ), - 'mysql_character_set' => 'utf8', -)); - -$connection->insert('imagecache_action') +$connection->insert('history') ->fields(array( - 'actionid', - 'presetid', - 'weight', - 'module', - 'action', - 'data', -)) -->values(array( - 'actionid' => '3', - 'presetid' => '1', - 'weight' => '0', - 'module' => 'imagecache', - 'action' => 'imagecache_scale_and_crop', - 'data' => 'a:2:{s:5:"width";s:4:"100%";s:6:"height";s:4:"100%";}', -)) -->values(array( - 'actionid' => '4', - 'presetid' => '2', - 'weight' => '0', - 'module' => 'imagecache', - 'action' => 'imagecache_crop', - 'data' => 'a:4:{s:5:"width";s:3:"555";s:6:"height";s:4:"5555";s:7:"xoffset";s:6:"center";s:7:"yoffset";s:6:"center";}', + 'uid', + 'nid', + 'timestamp', )) ->values(array( - 'actionid' => '5', - 'presetid' => '2', - 'weight' => '0', - 'module' => 'imagecache', - 'action' => 'imagecache_resize', - 'data' => 'a:2:{s:5:"width";s:3:"55%";s:6:"height";s:3:"55%";}', + 'uid' => '1', + 'nid' => '3', + 'timestamp' => '1457654737', )) ->values(array( - 'actionid' => '6', - 'presetid' => '2', - 'weight' => '0', - 'module' => 'imagecache', - 'action' => 'imagecache_rotate', - 'data' => 'a:3:{s:7:"degrees";s:2:"55";s:6:"random";i:0;s:7:"bgcolor";s:0:"";}', + 'uid' => '1', + 'nid' => '9', + 'timestamp' => '1457655127', )) ->execute(); -$connection->schema()->createTable('imagecache_preset', array( +$connection->schema()->createTable('i18n_blocks', array( 'fields' => array( - 'presetid' => array( + 'ibid' => array( 'type' => 'serial', 'not null' => TRUE, 'size' => 'normal', 'unsigned' => TRUE, ), - 'presetname' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => '255', - ), - ), - 'primary key' => array( - 'presetid', - ), - 'mysql_character_set' => 'utf8', -)); - -$connection->insert('imagecache_preset') -->fields(array( - 'presetid', - 'presetname', -)) -->values(array( - 'presetid' => '1', - 'presetname' => 'slackjaw_boys', -)) -->values(array( - 'presetid' => '2', - 'presetname' => 'big_blue_cheese', -)) -->execute(); - -$connection->schema()->createTable('languages', array( - 'fields' => array( - 'language' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => '12', - 'default' => '', - ), - 'name' => array( + 'module' => array( 'type' => 'varchar', 'not null' => TRUE, 'length' => '64', - 'default' => '', ), - 'native' => array( + 'delta' => array( 'type' => 'varchar', 'not null' => TRUE, - 'length' => '64', - 'default' => '', - ), - 'direction' => array( - 'type' => 'int', - 'not null' => TRUE, - 'size' => 'normal', - 'default' => '0', - ), - 'enabled' => array( - 'type' => 'int', - 'not null' => TRUE, - 'size' => 'normal', - 'default' => '0', - ), - 'plurals' => array( - 'type' => 'int', - 'not null' => TRUE, - 'size' => 'normal', + 'length' => '32', 'default' => '0', ), - 'formula' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => '128', - 'default' => '', - ), - 'domain' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => '128', - 'default' => '', - ), - 'prefix' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => '128', - 'default' => '', - ), - 'weight' => array( + 'type' => array( 'type' => 'int', 'not null' => TRUE, 'size' => 'normal', 'default' => '0', ), - 'javascript' => array( + 'language' => array( 'type' => 'varchar', 'not null' => TRUE, - 'length' => '32', + 'length' => '12', 'default' => '', ), ), 'primary key' => array( - 'language', - ), - 'indexes' => array( - 'list' => array( - 'weight', - 'name', - ), + 'ibid', ), 'mysql_character_set' => 'utf8', )); -$connection->insert('languages') -->fields(array( - 'language', - 'name', - 'native', - 'direction', - 'enabled', - 'plurals', - 'formula', - 'domain', - 'prefix', - 'weight', - 'javascript', -)) -->values(array( - 'language' => 'en', - 'name' => 'English', - 'native' => 'English', - 'direction' => '0', - 'enabled' => '1', - 'plurals' => '0', - 'formula' => '', - 'domain' => '', - 'prefix' => '', - 'weight' => '0', - 'javascript' => '', -)) -->values(array( - 'language' => 'fr', - 'name' => 'French', - 'native' => 'Français', - 'direction' => '0', - 'enabled' => '1', - 'plurals' => '2', - 'formula' => '($n>1)', - 'domain' => '', - 'prefix' => 'fr', - 'weight' => '0', - 'javascript' => '', -)) -->execute(); - -$connection->schema()->createTable('locales_source', array( +$connection->schema()->createTable('i18n_strings', array( 'fields' => array( 'lid' => array( - 'type' => 'serial', + 'type' => 'int', 'not null' => TRUE, 'size' => 'normal', + 'default' => '0', ), - 'location' => array( + 'objectid' => array( 'type' => 'varchar', 'not null' => TRUE, 'length' => '255', 'default' => '', ), - 'textgroup' => array( + 'type' => array( 'type' => 'varchar', 'not null' => TRUE, 'length' => '255', - 'default' => 'default', + 'default' => '', ), - 'source' => array( - 'type' => 'blob', + 'property' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '255', + 'default' => '', + ), + 'objectindex' => array( + 'type' => 'int', 'not null' => TRUE, 'size' => 'normal', + 'default' => '0', ), - 'version' => array( - 'type' => 'varchar', + 'format' => array( + 'type' => 'int', 'not null' => TRUE, - 'length' => '20', - 'default' => 'none', + 'size' => 'normal', + 'default' => '0', ), ), 'primary key' => array( 'lid', ), - 'indexes' => array( - 'source' => array( - array( - 'source', - '30', - ), - ), - ), 'mysql_character_set' => 'utf8', )); -$connection->insert('locales_source') +$connection->insert('i18n_strings') ->fields(array( 'lid', - 'location', - 'textgroup', - 'source', - 'version', + 'objectid', + 'type', + 'property', + 'objectindex', + 'format', )) ->values(array( - 'lid' => '1', - 'location' => 'misc/drupal.js', - 'textgroup' => 'default', - 'source' => 'Unspecified error', - 'version' => 'none', + 'lid' => '504', + 'objectid' => 'profile_color', + 'type' => 'field', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '2', - 'location' => 'misc/drupal.js', - 'textgroup' => 'default', - 'source' => 'An error occurred. \n@uri\n@text', - 'version' => 'none', + 'lid' => '505', + 'objectid' => 'profile_color', + 'type' => 'field', + 'property' => 'explanation', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '3', - 'location' => 'misc/drupal.js', - 'textgroup' => 'default', - 'source' => 'An error occurred. \n@uri\n(no information available).', - 'version' => 'none', + 'lid' => '506', + 'objectid' => '', + 'type' => 'category', + 'property' => '', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '4', - 'location' => 'misc/drupal.js', - 'textgroup' => 'default', - 'source' => 'An HTTP error @status occurred. \n@uri', - 'version' => 'none', + 'lid' => '507', + 'objectid' => 'profile_biography', + 'type' => 'field', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '5', - 'location' => 'content.module:21', - 'textgroup' => 'default', - 'source' => 'The content module, a required component of the Content Construction Kit (CCK), allows administrators to associate custom fields with content types. In Drupal, content types are used to define the characteristics of a post, including the title and description of the fields displayed on its add and edit pages. Using the content module (and the other helper modules included in CCK), custom fields beyond the default "Title" and "Body" may be added. CCK features are accessible through tabs on the content types administration page. (See the node module help page for more information about content types.)', - 'version' => 'none', + 'lid' => '508', + 'objectid' => 'profile_biography', + 'type' => 'field', + 'property' => 'explanation', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '6', - 'location' => 'content.module:22', - 'textgroup' => 'default', - 'source' => 'When adding a custom field to a content type, you determine its type (whether it will contain text, numbers, or references to other objects) and how it will be displayed (either as a text field or area, a select box, checkbox, radio button, or autocompleting field). A field may have multiple values (i.e., a "person" may have multiple e-mail addresses) or a single value (i.e., an "employee" has a single employee identification number). As you add and edit fields, CCK automatically adjusts the structure of the database as necessary. CCK also provides a number of other features, including intelligent caching for your custom data, an import and export facility for content type definitions, and integration with other contributed modules.', - 'version' => 'none', + 'lid' => '509', + 'objectid' => 'profile_sell_address', + 'type' => 'field', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '7', - 'location' => 'content.module:23', - 'textgroup' => 'default', - 'source' => 'Custom field types are provided by a set of optional modules included with CCK (each module provides a different type). The modules page allows you to enable or disable CCK components. A default installation of CCK includes:', - 'version' => 'none', + 'lid' => '510', + 'objectid' => 'profile_sell_address', + 'type' => 'field', + 'property' => 'explanation', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '8', - 'location' => 'content.module:25', - 'textgroup' => 'default', - 'source' => 'number, which adds numeric field types, in integer, decimal or floating point form. You may define a set of allowed inputs, or specify an allowable range of values. A variety of common formats for displaying numeric data are available.', - 'version' => 'none', + 'lid' => '511', + 'objectid' => '', + 'type' => 'category', + 'property' => '', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '9', - 'location' => 'content.module:26', - 'textgroup' => 'default', - 'source' => "text, which adds text field types. A text field may contain plain text only, or optionally, may use Drupal's input format filters to securely manage rich text input. Text input fields may be either a single line (text field), multiple lines (text area), or for greater input control, a select box, checkbox, or radio buttons. If desired, CCK can validate the input to a set of allowed values.", - 'version' => 'none', + 'lid' => '512', + 'objectid' => 'profile_sold_to', + 'type' => 'field', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '10', - 'location' => 'content.module:27', - 'textgroup' => 'default', - 'source' => 'nodereference, which creates custom references between Drupal nodes. By adding a nodereference field and two different content types, for instance, you can easily create complex parent/child relationships between data (multiple "employee" nodes may contain a nodereference field linking to an "employer" node).', - 'version' => 'none', + 'lid' => '513', + 'objectid' => 'profile_sold_to', + 'type' => 'field', + 'property' => 'explanation', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '11', - 'location' => 'content.module:28', - 'textgroup' => 'default', - 'source' => "userreference, which creates custom references to your sites' user accounts. By adding a userreference field, you can create complex relationships between your site's users and posts. To track user involvement in a post beyond Drupal's standard Authored by field, for instance, add a userreference field named \"Edited by\" to a content type to store a link to an editor's user account page.", - 'version' => 'none', + 'lid' => '514', + 'objectid' => 'profile_sold_to', + 'type' => 'field', + 'property' => 'options', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '12', - 'location' => 'content.module:29', - 'textgroup' => 'default', - 'source' => 'fieldgroup, which creates collapsible fieldsets to hold a group of related fields. A fieldset may either be open or closed by default. The order of your fieldsets, and the order of fields within a fieldset, is managed via a drag-and-drop interface provided by content module.', - 'version' => 'none', + 'lid' => '515', + 'objectid' => '', + 'type' => 'category', + 'property' => '', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '13', - 'location' => 'content.module:31', - 'textgroup' => 'default', - 'source' => 'For more information, see the online handbook entry for CCK or the CCK project page.', - 'version' => 'none', + 'lid' => '516', + 'objectid' => 'profile_bands', + 'type' => 'field', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '14', - 'location' => 'theme/theme.inc:111', - 'textgroup' => 'default', - 'source' => "Configure how this content type's fields and field labels should be displayed when it's viewed in teaser and full-page mode.", - 'version' => 'none', + 'lid' => '517', + 'objectid' => 'profile_bands', + 'type' => 'field', + 'property' => 'explanation', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '15', - 'location' => 'theme/theme.inc:114', - 'textgroup' => 'default', - 'source' => "Configure how this content type's fields should be displayed when it's rendered in the following contexts.", - 'version' => 'none', + 'lid' => '518', + 'objectid' => 'profile_birthdate', + 'type' => 'field', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '16', - 'location' => 'content.module:48', - 'textgroup' => 'default', - 'source' => 'Control the order of fields in the input form.', - 'version' => 'none', + 'lid' => '519', + 'objectid' => 'profile_birthdate', + 'type' => 'field', + 'property' => 'explanation', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '17', - 'location' => 'content.module:486, modules/content_multigroup/content_multigroup.module:1422', - 'textgroup' => 'default', - 'source' => 'This field is required.', - 'version' => 'none', + 'lid' => '520', + 'objectid' => 'profile_love_migrations', + 'type' => 'field', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '18', - 'location' => 'content.module:490', - 'textgroup' => 'default', - 'source' => '!title: !required', - 'version' => 'none', + 'lid' => '521', + 'objectid' => 'profile_love_migrations', + 'type' => 'field', + 'property' => 'explanation', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '19', - 'location' => 'content.module:493, modules/content_multigroup/content_multigroup.module:1434', - 'textgroup' => 'default', - 'source' => 'Order', - 'version' => 'none', + 'lid' => '522', + 'objectid' => 'profile_blog', + 'type' => 'field', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '20', - 'location' => 'content.module:1640', - 'textgroup' => 'default', - 'source' => 'RSS Item', - 'version' => 'none', + 'lid' => '523', + 'objectid' => 'profile_blog', + 'type' => 'field', + 'property' => 'explanation', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '21', - 'location' => 'content.module:1897', - 'textgroup' => 'default', - 'source' => 'Search Index', - 'version' => 'none', + 'lid' => '524', + 'objectid' => '1', + 'type' => 'block', + 'property' => 'title', + 'objectindex' => '1', + 'format' => '0', )) ->values(array( - 'lid' => '22', - 'location' => 'content.module:1901', - 'textgroup' => 'default', - 'source' => 'Search Result', - 'version' => 'none', + 'lid' => '525', + 'objectid' => '1', + 'type' => 'block', + 'property' => 'body', + 'objectindex' => '1', + 'format' => '2', )) ->values(array( - 'lid' => '23', - 'location' => 'content.module:2363', - 'textgroup' => 'default', - 'source' => 'Language', - 'version' => 'none', + 'lid' => '526', + 'objectid' => '2', + 'type' => 'block', + 'property' => 'title', + 'objectindex' => '2', + 'format' => '0', )) ->values(array( - 'lid' => '24', - 'location' => 'content.module:2377', - 'textgroup' => 'default', - 'source' => 'Taxonomy', - 'version' => 'none', + 'lid' => '527', + 'objectid' => '2', + 'type' => 'block', + 'property' => 'body', + 'objectindex' => '2', + 'format' => '2', )) ->values(array( - 'lid' => '25', - 'location' => 'content.module:2408', - 'textgroup' => 'default', - 'source' => 'File attachments', - 'version' => 'none', + 'lid' => '528', + 'objectid' => '4', + 'type' => 'vocabulary', + 'property' => 'name', + 'objectindex' => '4', + 'format' => '0', )) ->values(array( - 'lid' => '26', - 'location' => 'content.module:600', - 'textgroup' => 'default', - 'source' => 'Updating field type %type with module %module.', - 'version' => 'none', + 'lid' => '529', + 'objectid' => '1', + 'type' => 'vocabulary', + 'property' => 'name', + 'objectindex' => '1', + 'format' => '0', )) ->values(array( - 'lid' => '27', - 'location' => 'content.module:607', - 'textgroup' => 'default', - 'source' => 'Updating widget type %type with module %module.', - 'version' => 'none', + 'lid' => '530', + 'objectid' => '2', + 'type' => 'vocabulary', + 'property' => 'name', + 'objectindex' => '2', + 'format' => '0', )) ->values(array( - 'lid' => '28', - 'location' => 'content.module:63', - 'textgroup' => 'default', - 'source' => 'Use PHP input for field settings (dangerous - grant with care)', - 'version' => 'none', + 'lid' => '531', + 'objectid' => '3', + 'type' => 'vocabulary', + 'property' => 'name', + 'objectindex' => '3', + 'format' => '0', )) ->values(array( - 'lid' => '29', - 'location' => 'content.module:104', - 'textgroup' => 'default', - 'source' => 'Manage fields', - 'version' => 'none', + 'lid' => '532', + 'objectid' => '5', + 'type' => 'vocabulary', + 'property' => 'name', + 'objectindex' => '5', + 'format' => '0', )) ->values(array( - 'lid' => '30', - 'location' => 'content.module:113', - 'textgroup' => 'default', - 'source' => 'Display fields', - 'version' => 'none', + 'lid' => '533', + 'objectid' => 'article', + 'type' => 'type', + 'property' => 'name', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '31', - 'location' => 'content.module:143', - 'textgroup' => 'default', - 'source' => 'General', - 'version' => 'none', + 'lid' => '534', + 'objectid' => 'article', + 'type' => 'type', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '32', - 'location' => 'content.module:149', - 'textgroup' => 'default', - 'source' => 'Advanced', - 'version' => 'none', + 'lid' => '535', + 'objectid' => 'article', + 'type' => 'type', + 'property' => 'body', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '33', - 'location' => 'content.module:144', - 'textgroup' => 'default', - 'source' => 'Remove field', - 'version' => 'none', + 'lid' => '536', + 'objectid' => 'article', + 'type' => 'type', + 'property' => 'description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '34', - 'location' => 'content.info:0, includes/content.rules.inc:19;200, includes/views/content.views.inc:178;254', - 'textgroup' => 'default', - 'source' => 'Content', - 'version' => 'none', + 'lid' => '537', + 'objectid' => 'company', + 'type' => 'type', + 'property' => 'name', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '35', - 'location' => 'content.info:0', - 'textgroup' => 'default', - 'source' => 'Allows administrators to define new content types.', - 'version' => 'none', + 'lid' => '538', + 'objectid' => 'company', + 'type' => 'type', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '36', - 'location' => 'content.info:0, modules/content_copy/content_copy.info:0, modules/content_multigroup/content_multigroup.info:0, modules/content_permissions/content_permissions.info:0', - 'textgroup' => 'default', - 'source' => 'CCK', - 'version' => 'none', + 'lid' => '539', + 'objectid' => 'company', + 'type' => 'type', + 'property' => 'body', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '37', - 'location' => 'modules/text/text.module:41, modules/text/text.info:0', - 'textgroup' => 'default', - 'source' => 'Text', - 'version' => 'none', + 'lid' => '540', + 'objectid' => 'company', + 'type' => 'type', + 'property' => 'description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '38', - 'location' => 'examples/example_field.php:178', - 'textgroup' => 'default', - 'source' => 'The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database and it must match the field storage type, %type. The label is optional and the key will be used as the label if no label is specified.', - 'version' => 'none', + 'lid' => '541', + 'objectid' => 'employee', + 'type' => 'type', + 'property' => 'name', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '39', - 'location' => 'examples/example_field.php:484', - 'textgroup' => 'default', - 'source' => 'Text area', - 'version' => 'none', + 'lid' => '542', + 'objectid' => 'employee', + 'type' => 'type', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '40', - 'location' => 'includes/content.admin.inc:148;164;790, modules/fieldgroup/fieldgroup.module:236', - 'textgroup' => 'default', - 'source' => 'Remove', - 'version' => 'none', + 'lid' => '543', + 'objectid' => 'employee', + 'type' => 'type', + 'property' => 'body', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '41', - 'location' => 'content.module:1868', - 'textgroup' => 'default', - 'source' => 'Basic', - 'version' => 'none', + 'lid' => '544', + 'objectid' => 'employee', + 'type' => 'type', + 'property' => 'description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '42', - 'location' => 'content.module:1635 modules/nodereference/nodereference.module:215', - 'textgroup' => 'default', - 'source' => 'Teaser', - 'version' => 'none', + 'lid' => '545', + 'objectid' => 'sponsor', + 'type' => 'type', + 'property' => 'name', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '43', - 'location' => 'content.module:1636 modules/nodereference/nodereference.module:210', - 'textgroup' => 'default', - 'source' => 'Full node', - 'version' => 'none', + 'lid' => '546', + 'objectid' => 'sponsor', + 'type' => 'type', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '44', - 'location' => 'content.module:1881;1884', - 'textgroup' => 'default', - 'source' => 'RSS', - 'version' => 'none', + 'lid' => '547', + 'objectid' => 'sponsor', + 'type' => 'type', + 'property' => 'body', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '45', - 'location' => 'content.module:1894', - 'textgroup' => 'default', - 'source' => 'Search', - 'version' => 'none', + 'lid' => '548', + 'objectid' => 'sponsor', + 'type' => 'type', + 'property' => 'description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '46', - 'location' => 'content.module:2349;2356', - 'textgroup' => 'default', - 'source' => 'Node module form.', - 'version' => 'none', + 'lid' => '549', + 'objectid' => 'story', + 'type' => 'type', + 'property' => 'name', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '47', - 'location' => 'content.module:2364', - 'textgroup' => 'default', - 'source' => 'Locale module form.', - 'version' => 'none', + 'lid' => '550', + 'objectid' => 'story', + 'type' => 'type', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '48', - 'location' => 'content.module:2370', - 'textgroup' => 'default', - 'source' => 'Menu settings', - 'version' => 'none', + 'lid' => '551', + 'objectid' => 'story', + 'type' => 'type', + 'property' => 'body', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '49', - 'location' => 'content.module:2371', - 'textgroup' => 'default', - 'source' => 'Menu module form.', - 'version' => 'none', + 'lid' => '552', + 'objectid' => 'story', + 'type' => 'type', + 'property' => 'description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '50', - 'location' => 'content.module:2378', - 'textgroup' => 'default', - 'source' => 'Taxonomy module form.', - 'version' => 'none', + 'lid' => '553', + 'objectid' => 'test_event', + 'type' => 'type', + 'property' => 'name', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '51', - 'location' => 'content.module:2384', - 'textgroup' => 'default', - 'source' => 'Book', - 'version' => 'none', + 'lid' => '554', + 'objectid' => 'test_event', + 'type' => 'type', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '52', - 'location' => 'content.module:2385', - 'textgroup' => 'default', - 'source' => 'Book module form.', - 'version' => 'none', + 'lid' => '555', + 'objectid' => 'test_event', + 'type' => 'type', + 'property' => 'body', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '53', - 'location' => 'content.module:2391', - 'textgroup' => 'default', - 'source' => 'Poll title', - 'version' => 'none', + 'lid' => '556', + 'objectid' => 'test_event', + 'type' => 'type', + 'property' => 'description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '54', - 'location' => 'content.module:2392', - 'textgroup' => 'default', - 'source' => 'Poll module title.', - 'version' => 'none', + 'lid' => '558', + 'objectid' => 'test_page', + 'type' => 'type', + 'property' => 'name', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '55', - 'location' => 'content.module:2396', - 'textgroup' => 'default', - 'source' => 'Poll choices', - 'version' => 'none', + 'lid' => '559', + 'objectid' => 'test_page', + 'type' => 'type', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '56', - 'location' => 'content.module:2397', - 'textgroup' => 'default', - 'source' => 'Poll module choices.', - 'version' => 'none', + 'lid' => '560', + 'objectid' => 'test_page', + 'type' => 'type', + 'property' => 'body', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '57', - 'location' => 'content.module:2401', - 'textgroup' => 'default', - 'source' => 'Poll settings', - 'version' => 'none', + 'lid' => '561', + 'objectid' => 'test_page', + 'type' => 'type', + 'property' => 'description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '58', - 'location' => 'content.module:2402', - 'textgroup' => 'default', - 'source' => 'Poll module settings.', - 'version' => 'none', + 'lid' => '562', + 'objectid' => 'test_planet', + 'type' => 'type', + 'property' => 'name', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '59', - 'location' => 'content.module:2409', - 'textgroup' => 'default', - 'source' => 'Upload module form.', - 'version' => 'none', + 'lid' => '563', + 'objectid' => 'test_planet', + 'type' => 'type', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '60', - 'location' => 'content.module:557;564;0 includes/content.crud.inc:591;629', - 'textgroup' => 'default', - 'source' => 'content', - 'version' => 'none', + 'lid' => '564', + 'objectid' => 'test_planet', + 'type' => 'type', + 'property' => 'body', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '61', - 'location' => 'content.module:103 modules/content_copy/content_copy.module:125', - 'textgroup' => 'default', - 'source' => 'Fields', - 'version' => 'none', + 'lid' => '565', + 'objectid' => 'test_planet', + 'type' => 'type', + 'property' => 'description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '62', - 'location' => 'content.install:236', - 'textgroup' => 'default', - 'source' => "Updates for CCK-related modules are not run until the modules are enabled on the administer modules page. When you enable them, you'll need to return to update.php and run the remaining updates.", - 'version' => 'none', + 'lid' => '566', + 'objectid' => 'test_story', + 'type' => 'type', + 'property' => 'name', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '63', - 'location' => 'content.install:239', - 'textgroup' => 'default', - 'source' => '!module.module has updates but cannot be updated because content.module is not enabled.
                          If and when content.module is enabled, you will need to re-run the update script. You will continue to see this message until the module is enabled and updates are run.', - 'version' => 'none', + 'lid' => '567', + 'objectid' => 'test_story', + 'type' => 'type', + 'property' => 'title', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '64', - 'location' => 'content.install:244', - 'textgroup' => 'default', - 'source' => '!module.module has updates and is available in the modules folder but is not enabled.
                          If and when it is enabled, you will need to re-run the update script. You will continue to see this message until the module is enabled and updates are run.', - 'version' => 'none', + 'lid' => '568', + 'objectid' => 'test_story', + 'type' => 'type', + 'property' => 'body', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '65', - 'location' => 'content.install:251', - 'textgroup' => 'default', - 'source' => 'Some updates are still pending. Please return to update.php and run the remaining updates.', - 'version' => 'none', + 'lid' => '569', + 'objectid' => 'test_story', + 'type' => 'type', + 'property' => 'description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '66', - 'location' => '(duplicate) content.install:10', - 'textgroup' => 'default', - 'source' => 'CCK - No Views integration', - 'version' => 'none', + 'lid' => '570', + 'objectid' => 'story-field_test_exclude_unset', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '67', - 'location' => '(duplicate) content.install:11', - 'textgroup' => 'default', - 'source' => 'CCK integration with Views module requires Views 6.x-2.0-rc2 or greater.', - 'version' => 'none', + 'lid' => '571', + 'objectid' => 'story-field_test_exclude_unset', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '68', - 'location' => 'includes/content.admin.inc:16;212', - 'textgroup' => 'default', - 'source' => 'Name', - 'version' => 'none', + 'lid' => '572', + 'objectid' => 'story-field_test_two', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '69', - 'location' => 'includes/content.admin.inc:16;212', - 'textgroup' => 'default', - 'source' => 'Type', - 'version' => 'none', + 'lid' => '573', + 'objectid' => 'story-field_test_two', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '70', - 'location' => 'includes/content.admin.inc:16 modules/fieldgroup/fieldgroup.module:156', - 'textgroup' => 'default', - 'source' => 'Description', - 'version' => 'none', + 'lid' => '574', + 'objectid' => 'story-field_test', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '71', - 'location' => 'includes/content.admin.inc:16;212', - 'textgroup' => 'default', - 'source' => 'Operations', - 'version' => 'none', + 'lid' => '575', + 'objectid' => 'story-field_test', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '72', - 'location' => 'includes/content.admin.inc:30', - 'textgroup' => 'default', - 'source' => 'edit', - 'version' => 'none', + 'lid' => '576', + 'objectid' => 'story-field_test_three', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '73', - 'location' => 'includes/content.admin.inc:34', - 'textgroup' => 'default', - 'source' => 'manage fields', - 'version' => 'none', + 'lid' => '577', + 'objectid' => 'story-field_test_three', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '74', - 'location' => 'includes/content.admin.inc:37', - 'textgroup' => 'default', - 'source' => 'delete', - 'version' => 'none', + 'lid' => '578', + 'objectid' => 'story-field_test_four', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '75', - 'location' => 'includes/content.admin.inc:48', - 'textgroup' => 'default', - 'source' => 'No content types available.', - 'version' => 'none', + 'lid' => '579', + 'objectid' => 'story-field_test_four', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '76', - 'location' => 'includes/content.admin.inc:55', - 'textgroup' => 'default', - 'source' => '» Add a new content type', - 'version' => 'none', + 'lid' => '580', + 'objectid' => 'story-field_test_identical1', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '77', - 'location' => 'includes/content.admin.inc:64;602;856', - 'textgroup' => 'default', - 'source' => 'Field name', - 'version' => 'none', + 'lid' => '581', + 'objectid' => 'story-field_test_identical1', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '78', - 'location' => 'includes/content.admin.inc:64;632;643;862', - 'textgroup' => 'default', - 'source' => 'Field type', - 'version' => 'none', + 'lid' => '582', + 'objectid' => 'story-field_test_identical2', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '79', - 'location' => 'includes/content.admin.inc:64', - 'textgroup' => 'default', - 'source' => 'Used in', - 'version' => 'none', + 'lid' => '583', + 'objectid' => 'story-field_test_identical2', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '80', - 'location' => 'includes/content.admin.inc:71', - 'textgroup' => 'default', - 'source' => '@field_name (Locked)', - 'version' => 'none', + 'lid' => '584', + 'objectid' => 'story-field_test_email', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '81', - 'location' => 'includes/content.admin.inc:87', - 'textgroup' => 'default', - 'source' => 'No fields have been defined for any content type yet.', - 'version' => 'none', + 'lid' => '585', + 'objectid' => 'story-field_test_email', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '82', - 'location' => 'includes/content.admin.inc:106', - 'textgroup' => 'default', - 'source' => 'This content type has inactive fields. Inactive fields are not included in lists of available fields until their modules are enabled.', - 'version' => 'none', + 'lid' => '586', + 'objectid' => 'story-field_test_link', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '83', - 'location' => 'includes/content.admin.inc:108', - 'textgroup' => 'default', - 'source' => '!field (!field_name) is an inactive !field_type field that uses a !widget_type widget.', - 'version' => 'none', + 'lid' => '587', + 'objectid' => 'story-field_test_link', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '84', - 'location' => 'includes/content.admin.inc:147;163', - 'textgroup' => 'default', - 'source' => 'Configure', - 'version' => 'none', + 'lid' => '588', + 'objectid' => 'story-field_test_filefield', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '85', - 'location' => 'includes/content.admin.inc:181', - 'textgroup' => 'default', - 'source' => 'Locked', - 'version' => 'none', + 'lid' => '589', + 'objectid' => 'story-field_test_filefield', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '86', - 'location' => 'includes/content.admin.inc:237', - 'textgroup' => 'default', - 'source' => '- Select a field type -', - 'version' => 'none', + 'lid' => '590', + 'objectid' => 'story-field_test_imagefield', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '87', - 'location' => 'includes/content.admin.inc:238', - 'textgroup' => 'default', - 'source' => '- Select a widget -', - 'version' => 'none', + 'lid' => '591', + 'objectid' => 'story-field_test_imagefield', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '88', - 'location' => 'includes/content.admin.inc:212;413;623;850, modules/fieldgroup/fieldgroup.module:121', - 'textgroup' => 'default', - 'source' => 'Label', - 'version' => 'none', + 'lid' => '592', + 'objectid' => 'story-field_test_date', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '89', - 'location' => 'includes/content.admin.inc:253', - 'textgroup' => 'default', - 'source' => 'Field name (a-z, 0-9, _)', - 'version' => 'none', + 'lid' => '593', + 'objectid' => 'story-field_test_date', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '90', - 'location' => 'includes/content.admin.inc:258', - 'textgroup' => 'default', - 'source' => 'Type of data to store.', - 'version' => 'none', + 'lid' => '594', + 'objectid' => 'story-field_test_datestamp', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '91', - 'location' => 'includes/content.admin.inc:263;295', - 'textgroup' => 'default', - 'source' => 'Form element to edit the data.', - 'version' => 'none', + 'lid' => '595', + 'objectid' => 'story-field_test_datestamp', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '92', - 'location' => 'includes/content.admin.inc:279', - 'textgroup' => 'default', - 'source' => '- Select an existing field -', - 'version' => 'none', + 'lid' => '596', + 'objectid' => 'story-field_test_datetime', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '93', - 'location' => 'includes/content.admin.inc:290', - 'textgroup' => 'default', - 'source' => 'Field to share', - 'version' => 'none', + 'lid' => '597', + 'objectid' => 'story-field_test_datetime', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '94', - 'location' => 'includes/content.admin.inc:324', - 'textgroup' => 'default', - 'source' => 'Group name (a-z, 0-9, _)', - 'version' => 'none', + 'lid' => '598', + 'objectid' => 'story-field_test_phone', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '95', - 'location' => 'includes/content.admin.inc:201;402, modules/fieldgroup/fieldgroup.module:110;363', - 'textgroup' => 'default', - 'source' => 'Save', - 'version' => 'none', + 'lid' => '599', + 'objectid' => 'story-field_test_phone', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '96', - 'location' => 'includes/content.admin.inc:365', - 'textgroup' => 'default', - 'source' => 'Add new field: you need to provide a label.', - 'version' => 'none', + 'lid' => '600', + 'objectid' => 'story-field_test_decimal_radio_buttons', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '97', - 'location' => 'includes/content.admin.inc:370', - 'textgroup' => 'default', - 'source' => 'Add new field: you need to provide a field name.', - 'version' => 'none', + 'lid' => '601', + 'objectid' => 'story-field_test_decimal_radio_buttons', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '98', - 'location' => 'includes/content.admin.inc:384', - 'textgroup' => 'default', - 'source' => 'Add new field: the field name %field_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', - 'version' => 'none', + 'lid' => '602', + 'objectid' => 'field_test_decimal_radio_buttons', + 'type' => 'field', + 'property' => 'option_1.2', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '99', - 'location' => 'includes/content.admin.inc:387', - 'textgroup' => 'default', - 'source' => "Add new field: the field name %field_name is too long. The name is limited to 32 characters, including the 'field_' prefix.", - 'version' => 'none', + 'lid' => '603', + 'objectid' => 'field_test_decimal_radio_buttons', + 'type' => 'field', + 'property' => 'option_2.1', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '100', - 'location' => 'includes/content.admin.inc:391', - 'textgroup' => 'default', - 'source' => "Add new field: the name 'field_instance' is a reserved name.", - 'version' => 'none', + 'lid' => '604', + 'objectid' => 'story-field_test_float_single_checkbox', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '101', - 'location' => 'includes/content.admin.inc:403', - 'textgroup' => 'default', - 'source' => 'Add new field: the field name %field_name already exists.', - 'version' => 'none', + 'lid' => '605', + 'objectid' => 'story-field_test_float_single_checkbox', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '102', - 'location' => 'includes/content.admin.inc:409', - 'textgroup' => 'default', - 'source' => 'Add new field: you need to select a field type.', - 'version' => 'none', + 'lid' => '606', + 'objectid' => 'field_test_float_single_checkbox', + 'type' => 'field', + 'property' => 'option_3.142', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '103', - 'location' => 'includes/content.admin.inc:414', - 'textgroup' => 'default', - 'source' => 'Add new field: you need to select a widget.', - 'version' => 'none', + 'lid' => '607', + 'objectid' => 'field_test_float_single_checkbox', + 'type' => 'field', + 'property' => 'option_1.234', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '104', - 'location' => 'includes/content.admin.inc:420', - 'textgroup' => 'default', - 'source' => 'Add new field: invalid widget.', - 'version' => 'none', + 'lid' => '608', + 'objectid' => 'story-field_test_integer_selectlist', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '105', - 'location' => 'includes/content.admin.inc:441', - 'textgroup' => 'default', - 'source' => 'Add existing field: you need to provide a label.', - 'version' => 'none', + 'lid' => '609', + 'objectid' => 'story-field_test_integer_selectlist', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '106', - 'location' => 'includes/content.admin.inc:446', - 'textgroup' => 'default', - 'source' => 'Add existing field: you need to select a field.', - 'version' => 'none', + 'lid' => '610', + 'objectid' => 'field_test_integer_selectlist', + 'type' => 'field', + 'property' => 'option_1234', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '107', - 'location' => 'includes/content.admin.inc:451', - 'textgroup' => 'default', - 'source' => 'Add existing field: you need to select a widget.', - 'version' => 'none', + 'lid' => '611', + 'objectid' => 'field_test_integer_selectlist', + 'type' => 'field', + 'property' => 'option_2341', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '108', - 'location' => 'includes/content.admin.inc:457', - 'textgroup' => 'default', - 'source' => 'Add existing field: invalid widget.', - 'version' => 'none', + 'lid' => '612', + 'objectid' => 'field_test_integer_selectlist', + 'type' => 'field', + 'property' => 'option_3412', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '109', - 'location' => 'includes/content.admin.inc:745', - 'textgroup' => 'default', - 'source' => 'There was a problem creating field %label.', - 'version' => 'none', + 'lid' => '613', + 'objectid' => 'field_test_integer_selectlist', + 'type' => 'field', + 'property' => 'option_4123', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '110', - 'location' => 'includes/content.admin.inc:518', - 'textgroup' => 'default', - 'source' => 'The field %label cannot be added to a content type because it is locked.', - 'version' => 'none', + 'lid' => '614', + 'objectid' => 'story-field_test_text_single_checkbox', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '111', - 'location' => 'includes/content.admin.inc:550', - 'textgroup' => 'default', - 'source' => 'There was a problem adding field %label.', - 'version' => 'none', + 'lid' => '615', + 'objectid' => 'story-field_test_text_single_checkbox', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '112', - 'location' => 'includes/content.admin.inc:571', - 'textgroup' => 'default', - 'source' => 'There are no fields configured for this content type. You can add new fields on the Manage fields page.', - 'version' => 'none', + 'lid' => '616', + 'objectid' => 'field_test_text_single_checkbox', + 'type' => 'field', + 'property' => 'option_0', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '113', - 'location' => 'includes/content.admin.inc:319;360', - 'textgroup' => 'default', - 'source' => 'Above', - 'version' => 'none', + 'lid' => '617', + 'objectid' => 'field_test_text_single_checkbox', + 'type' => 'field', + 'property' => 'option_1', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '114', - 'location' => 'includes/content.admin.inc:320', - 'textgroup' => 'default', - 'source' => 'Inline', - 'version' => 'none', + 'lid' => '618', + 'objectid' => 'story-field_test_text_single_checkbox2', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '115', - 'location' => 'includes/content.admin.inc:618;661', - 'textgroup' => 'default', - 'source' => 'Include', - 'version' => 'none', + 'lid' => '619', + 'objectid' => 'story-field_test_text_single_checkbox2', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '116', - 'location' => 'includes/content.admin.inc:618;661, theme/content-admin-display-overview-form.tpl.php:17', - 'textgroup' => 'default', - 'source' => 'Exclude', - 'version' => 'none', + 'lid' => '620', + 'objectid' => 'field_test_text_single_checkbox2', + 'type' => 'field', + 'property' => 'option_Off', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '117', - 'location' => 'includes/content.admin.inc:364', - 'textgroup' => 'default', - 'source' => 'no styling', - 'version' => 'none', + 'lid' => '621', + 'objectid' => 'field_test_text_single_checkbox2', + 'type' => 'field', + 'property' => 'option_Hello', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '118', - 'location' => 'includes/content.admin.inc:365', - 'textgroup' => 'default', - 'source' => 'simple', - 'version' => 'none', + 'lid' => '622', + 'objectid' => 'test_page-field_test', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '119', - 'location' => 'includes/content.admin.inc:366', - 'textgroup' => 'default', - 'source' => 'fieldset', - 'version' => 'none', + 'lid' => '623', + 'objectid' => 'test_page-field_test', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '120', - 'location' => 'includes/content.admin.inc:367', - 'textgroup' => 'default', - 'source' => 'fieldset - collapsible', - 'version' => 'none', + 'lid' => '624', + 'objectid' => 'test_planet-field_multivalue', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '121', - 'location' => 'includes/content.admin.inc:368', - 'textgroup' => 'default', - 'source' => 'fieldset - collapsed', - 'version' => 'none', + 'lid' => '625', + 'objectid' => 'test_planet-field_multivalue', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '122', - 'location' => 'includes/content.admin.inc:460', - 'textgroup' => 'default', - 'source' => 'Your settings have been saved.', - 'version' => 'none', + 'lid' => '626', + 'objectid' => 'test_planet-field_test_text_single_checkbox', + 'type' => 'field', + 'property' => 'widget_label', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '123', - 'location' => 'includes/content.admin.inc:760', - 'textgroup' => 'default', - 'source' => '@type: @field (@label)', - 'version' => 'none', + 'lid' => '627', + 'objectid' => 'test_planet-field_test_text_single_checkbox', + 'type' => 'field', + 'property' => 'widget_description', + 'objectindex' => '0', + 'format' => '0', )) ->values(array( - 'lid' => '124', - 'location' => 'includes/content.admin.inc:597', - 'textgroup' => 'default', - 'source' => 'Edit basic information', - 'version' => 'none', + 'lid' => '633', + 'objectid' => '140', + 'type' => 'item', + 'property' => 'title', + 'objectindex' => '140', + 'format' => '0', )) ->values(array( - 'lid' => '125', - 'location' => 'includes/content.admin.inc:792', - 'textgroup' => 'default', - 'source' => 'The machine-readable name of the field. This name cannot be changed.', - 'version' => 'none', + 'lid' => '634', + 'objectid' => '139', + 'type' => 'item', + 'property' => 'title', + 'objectindex' => '139', + 'format' => '0', )) ->values(array( - 'lid' => '126', - 'location' => 'includes/content.admin.inc:626', - 'textgroup' => 'default', - 'source' => 'A human-readable name to be used as the label for this field in the %type content type.', - 'version' => 'none', + 'lid' => '635', + 'objectid' => '139', + 'type' => 'item', + 'property' => 'description', + 'objectindex' => '139', + 'format' => '0', +)) +->execute(); + +$connection->schema()->createTable('i18n_variable', array( + 'fields' => array( + 'name' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'language' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '12', + 'default' => '', + ), + 'value' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'big', + ), + ), + 'primary key' => array( + 'name', + 'language', + ), + 'mysql_character_set' => 'utf8', +)); + +$connection->insert('i18n_variable') +->fields(array( + 'name', + 'language', + 'value', )) ->values(array( - 'lid' => '127', - 'location' => 'includes/content.admin.inc:646', - 'textgroup' => 'default', - 'source' => 'The type of data you would like to store in the database with this field. This option cannot be changed.', - 'version' => 'none', + 'name' => 'array_filter', + 'language' => 'en', + 'value' => 'b:1;', )) ->values(array( - 'lid' => '128', - 'location' => 'includes/content.admin.inc:651;869', - 'textgroup' => 'default', - 'source' => 'Widget type', - 'version' => 'none', + 'name' => 'i18nstrings_allowed_formats', + 'language' => 'en', + 'value' => 'a:2:{i:0;i:1;i:1;i:2;}', )) ->values(array( - 'lid' => '129', - 'location' => 'includes/content.admin.inc:655', - 'textgroup' => 'default', - 'source' => 'The type of form element you would like to present to the user when creating this field in the %type content type.', - 'version' => 'none', + 'name' => 'anonymous', + 'language' => 'fr', + 'value' => 's:8:"fr Guest";', )) ->values(array( - 'lid' => '130', - 'location' => 'includes/content.admin.inc:669', - 'textgroup' => 'default', - 'source' => 'Continue', - 'version' => 'none', + 'name' => 'error_level', + 'language' => 'fr', + 'value' => 's:1:"1";', )) ->values(array( - 'lid' => '131', - 'location' => 'includes/content.admin.inc:854', - 'textgroup' => 'default', - 'source' => 'Updated basic settings for field %label.', - 'version' => 'none', + 'name' => 'site_403', + 'language' => 'fr', + 'value' => 's:7:"fr-user";', )) ->values(array( - 'lid' => '132', - 'location' => 'includes/content.admin.inc:858', - 'textgroup' => 'default', - 'source' => 'There was a problem updating the basic settings for field %label.', - 'version' => 'none', + 'name' => 'site_404', + 'language' => 'fr', + 'value' => 's:17:"fr-page-not-found";', )) ->values(array( - 'lid' => '133', - 'location' => 'includes/content.admin.inc:786', - 'textgroup' => 'default', - 'source' => 'Are you sure you want to remove the field %field?', - 'version' => 'none', + 'name' => 'site_footer', + 'language' => 'fr', + 'value' => 's:0:"";', )) ->values(array( - 'lid' => '134', - 'location' => 'includes/content.admin.inc:789', - 'textgroup' => 'default', - 'source' => 'If you have any content left in this field, it will be lost. This action cannot be undone.', - 'version' => 'none', + 'name' => 'site_frontpage', + 'language' => 'fr', + 'value' => 's:4:"node";', )) ->values(array( - 'lid' => '135', - 'location' => 'includes/content.admin.inc:790 modules/fieldgroup/fieldgroup.module:236', - 'textgroup' => 'default', - 'source' => 'Cancel', - 'version' => 'none', + 'name' => 'site_mail', + 'language' => 'fr', + 'value' => 's:24:"fr_site_mail@example.com";', )) ->values(array( - 'lid' => '136', - 'location' => 'includes/content.admin.inc:894', - 'textgroup' => 'default', - 'source' => 'This field is locked and cannot be removed.', - 'version' => 'none', + 'name' => 'site_mission', + 'language' => 'fr', + 'value' => 's:0:"";', )) ->values(array( - 'lid' => '137', - 'location' => 'includes/content.admin.inc:808', - 'textgroup' => 'default', - 'source' => 'Removed field %field from %type.', - 'version' => 'none', + 'name' => 'site_name', + 'language' => 'fr', + 'value' => 's:12:"fr site name";', )) ->values(array( - 'lid' => '138', - 'location' => 'includes/content.admin.inc:813', - 'textgroup' => 'default', - 'source' => 'There was a problem deleting %field from %type.', - 'version' => 'none', + 'name' => 'site_offline', + 'language' => 'fr', + 'value' => 's:1:"0";', )) ->values(array( - 'lid' => '139', - 'location' => 'includes/content.admin.inc:939', - 'textgroup' => 'default', - 'source' => 'The field %field is locked and cannot be edited.', - 'version' => 'none', + 'name' => 'site_offline_message', + 'language' => 'fr', + 'value' => 's:99:"fr - Drupal is currently under maintenance. We should be back shortly. Thank you for your patience.";', )) ->values(array( - 'lid' => '140', - 'location' => 'includes/content.admin.inc:846', - 'textgroup' => 'default', - 'source' => '%type basic information', - 'version' => 'none', + 'name' => 'site_slogan', + 'language' => 'fr', + 'value' => 's:16:"fr Migrate rocks";', )) ->values(array( - 'lid' => '141', - 'location' => 'includes/content.admin.inc:876', - 'textgroup' => 'default', - 'source' => 'Change basic information', - 'version' => 'none', + 'name' => 'user_email_verification', + 'language' => 'fr', + 'value' => 'i:0;', )) ->values(array( - 'lid' => '142', - 'location' => 'includes/content.admin.inc:882', - 'textgroup' => 'default', - 'source' => '%type settings', - 'version' => 'none', + 'name' => 'user_mail_password_reset_body', + 'language' => 'fr', + 'value' => "s:424:\"fr - !username,\r\n\r\nA request to reset the password for your account has been made at !site.\r\n\r\nYou may now log in to !uri_brief by clicking on this link or copying and pasting it in your browser:\r\n\r\n!login_url\r\n\r\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\r\n\r\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\";", )) ->values(array( - 'lid' => '143', - 'location' => 'includes/content.admin.inc:883', - 'textgroup' => 'default', - 'source' => 'These settings apply only to the %field field as it appears in the %type content type.', - 'version' => 'none', + 'name' => 'user_mail_password_reset_subject', + 'language' => 'fr', + 'value' => 's:57:"fr - Replacement login information for !username at !site";', )) ->values(array( - 'lid' => '144', - 'location' => 'includes/content.admin.inc:897 modules/fieldgroup/fieldgroup.module:143', - 'textgroup' => 'default', - 'source' => 'Help text', - 'version' => 'none', + 'name' => 'user_mail_register_admin_created_body', + 'language' => 'fr', + 'value' => "s:473:\"fr - !username,\r\n\r\nA site administrator at !site has created an account for you. You may now log in to !login_uri using the following username and password:\r\n\r\nusername: !username\r\npassword: !password\r\n\r\nYou may also log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n!login_url\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\r\n\r\n\r\n-- !site team\";", )) ->values(array( - 'lid' => '145', - 'location' => 'includes/content.admin.inc:900', - 'textgroup' => 'default', - 'source' => 'Instructions to present to the user below this field on the editing form.
                          Allowed HTML tags: @tags', - 'version' => 'none', + 'name' => 'user_mail_register_admin_created_subject', + 'language' => 'fr', + 'value' => 's:57:"fr - An administrator created an account for you at !site";', )) ->values(array( - 'lid' => '146', - 'location' => 'includes/content.admin.inc:908', - 'textgroup' => 'default', - 'source' => 'Default value', - 'version' => 'none', + 'name' => 'user_mail_register_no_approval_required_body', + 'language' => 'fr', + 'value' => "s:447:\"fr - !username,\r\n\r\nThank you for registering at !site. You may now log in to !login_uri using the following username and password:\r\n\r\nusername: !username\r\npassword: !password\r\n\r\nYou may also log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n!login_url\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\r\n\r\n\r\n-- !site team\";", )) ->values(array( - 'lid' => '147', - 'location' => 'examples/example_field.php:182 includes/content.admin.inc:930, modules/number/number.module:137 modules/text/text.module:96', - 'textgroup' => 'default', - 'source' => 'PHP code', - 'version' => 'none', + 'name' => 'user_mail_register_no_approval_required_subject', + 'language' => 'fr', + 'value' => 's:43:"fr - Account details for !username at !site";', )) ->values(array( - 'lid' => '148', - 'location' => 'includes/content.admin.inc:1083;1238, includes/content.rules.inc:93', - 'textgroup' => 'default', - 'source' => "'@column' => value for @column", - 'version' => 'none', + 'name' => 'user_mail_register_pending_approval_body', + 'language' => 'fr', + 'value' => "s:277:\"fr - !username,\r\n\r\nThank you for registering at !site. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\r\n\r\n\r\n-- !site team\";", )) ->values(array( - 'lid' => '149', - 'location' => 'includes/content.admin.inc:1085;1240, includes/content.rules.inc:95', - 'textgroup' => 'default', - 'source' => "return array(\n 0 => array(@columns),\n // You'll usually want to stop here. Provide more values\n // if you want your 'default value' to be multi-valued:\n 1 => array(@columns),\n 2 => ...\n);", - 'version' => 'none', + 'name' => 'user_mail_register_pending_approval_subject', + 'language' => 'fr', + 'value' => 's:68:"fr - Account details for !username at !site (pending admin approval)";', )) ->values(array( - 'lid' => '150', - 'location' => 'examples/example_field.php:188 includes/content.admin.inc:951;964, modules/number/number.module:144;153 modules/text/text.module:103;112', - 'textgroup' => 'default', - 'source' => 'Code', - 'version' => 'none', + 'name' => 'user_mail_status_activated_body', + 'language' => 'fr', + 'value' => "s:439:\"fr - !username,\r\n\r\nYour account at !site has been activated.\r\n\r\nYou may now log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n!login_url\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\r\n\r\nOnce you have set your own password, you will be able to log in to !login_uri in the future using:\r\n\r\nusername: !username\r\n\";", )) ->values(array( - 'lid' => '151', - 'location' => 'includes/content.admin.inc:1093', - 'textgroup' => 'default', - 'source' => 'Advanced usage only: PHP code that returns a default value. Should not include <?php ?> delimiters. If this field is filled out, the value returned by this code will override any value specified above. Expected format:
                          !sample
                          To figure out the expected format, you can use the devel load tab provided by devel module on a %type content page.', - 'version' => 'none', + 'name' => 'user_mail_status_activated_notify', + 'language' => 'fr', + 'value' => 'i:0;', )) ->values(array( - 'lid' => '152', - 'location' => 'includes/content.admin.inc:965 modules/number/number.module:154, modules/text/text.module:113', - 'textgroup' => 'default', - 'source' => '<none>', - 'version' => 'none', + 'name' => 'user_mail_status_activated_subject', + 'language' => 'fr', + 'value' => 's:54:"fr - Account details for !username at !site (approved)";', )) ->values(array( - 'lid' => '153', - 'location' => 'includes/content.admin.inc:966 modules/number/number.module:155, modules/text/text.module:114', - 'textgroup' => 'default', - 'source' => "You're not allowed to input PHP code.", - 'version' => 'none', + 'name' => 'user_mail_status_blocked_body', + 'language' => 'fr', + 'value' => "s:58:\"fr - !username,\r\n\r\nYour account on !site has been blocked.\";", )) ->values(array( - 'lid' => '154', - 'location' => 'includes/content.admin.inc:966', - 'textgroup' => 'default', - 'source' => 'This PHP code was set by an administrator and will override any value specified above.', - 'version' => 'none', + 'name' => 'user_mail_status_blocked_notify', + 'language' => 'fr', + 'value' => 'i:1;', )) ->values(array( - 'lid' => '155', - 'location' => 'includes/content.admin.inc:973', - 'textgroup' => 'default', - 'source' => 'Global settings', - 'version' => 'none', + 'name' => 'user_mail_status_blocked_subject', + 'language' => 'fr', + 'value' => 's:53:"fr - Account details for !username at !site (blocked)";', )) ->values(array( - 'lid' => '156', - 'location' => 'includes/content.admin.inc:974', - 'textgroup' => 'default', - 'source' => 'These settings apply to the %field field in every content type in which it appears.', - 'version' => 'none', + 'name' => 'user_mail_status_deleted_body', + 'language' => 'fr', + 'value' => "s:58:\"fr - !username,\r\n\r\nYour account on !site has been deleted.\";", )) ->values(array( - 'lid' => '157', - 'location' => 'includes/content.admin.inc:978', - 'textgroup' => 'default', - 'source' => 'Required', - 'version' => 'none', + 'name' => 'user_mail_status_deleted_notify', + 'language' => 'fr', + 'value' => 'i:0;', )) ->values(array( - 'lid' => '158', - 'location' => 'includes/content.admin.inc:1119', - 'textgroup' => 'default', - 'source' => 'Maximum number of values users can enter for this field.', - 'version' => 'none', + 'name' => 'user_mail_status_deleted_subject', + 'language' => 'fr', + 'value' => 's:53:"fr - Account details for !username at !site (deleted)";', )) ->values(array( - 'lid' => '159', - 'location' => 'includes/content.admin.inc:1121', - 'textgroup' => 'default', - 'source' => "'Unlimited' will provide an 'Add more' button so the users can add as many values as they like.", - 'version' => 'none', + 'name' => 'user_pictures', + 'language' => 'fr', + 'value' => 's:1:"0";', )) ->values(array( - 'lid' => '160', - 'location' => 'includes/content.admin.inc:986', - 'textgroup' => 'default', - 'source' => 'Warning! Changing this setting after data has been created could result in the loss of data!', - 'version' => 'none', + 'name' => 'user_picture_default', + 'language' => 'fr', + 'value' => 's:0:"";', )) ->values(array( - 'lid' => '161', - 'location' => 'includes/content.admin.inc:983', - 'textgroup' => 'default', - 'source' => 'Number of values', - 'version' => 'none', + 'name' => 'user_picture_dimensions', + 'language' => 'fr', + 'value' => 's:5:"85x85";', )) ->values(array( - 'lid' => '162', - 'location' => 'includes/content.admin.inc:984', - 'textgroup' => 'default', - 'source' => 'Unlimited', - 'version' => 'none', + 'name' => 'user_picture_file_size', + 'language' => 'fr', + 'value' => 's:2:"30";', )) ->values(array( - 'lid' => '163', - 'location' => 'includes/content.admin.inc:1001', - 'textgroup' => 'default', - 'source' => 'Save field settings', - 'version' => 'none', + 'name' => 'user_picture_guidelines', + 'language' => 'fr', + 'value' => 's:0:"";', )) ->values(array( - 'lid' => '164', - 'location' => 'includes/content.admin.inc:1281', - 'textgroup' => 'default', - 'source' => "The PHP code for 'default value' returned @value, which is invalid.", - 'version' => 'none', + 'name' => 'user_picture_path', + 'language' => 'fr', + 'value' => 's:8:"pictures";', )) ->values(array( - 'lid' => '165', - 'location' => 'includes/content.admin.inc:1135', - 'textgroup' => 'default', - 'source' => 'The default value is invalid.', - 'version' => 'none', + 'name' => 'user_register', + 'language' => 'fr', + 'value' => 's:1:"0";', )) ->values(array( - 'lid' => '166', - 'location' => 'includes/content.admin.inc:547', - 'textgroup' => 'default', - 'source' => 'Added field %label.', - 'version' => 'none', + 'name' => 'user_registration_help', + 'language' => 'fr', + 'value' => 's:0:"";', )) ->values(array( - 'lid' => '167', - 'location' => 'includes/content.admin.inc:1158', - 'textgroup' => 'default', - 'source' => 'Saved field %label.', - 'version' => 'none', + 'name' => 'user_signatures', + 'language' => 'fr', + 'value' => 's:1:"1";', )) ->values(array( - 'lid' => '168', - 'location' => 'includes/content.admin.inc:1463', - 'textgroup' => 'default', - 'source' => 'Processing', - 'version' => 'none', + 'name' => 'anonymous', + 'language' => 'zu', + 'value' => 's:5:"Guest";', )) ->values(array( - 'lid' => '169', - 'location' => 'includes/content.admin.inc:1464', - 'textgroup' => 'default', - 'source' => 'The update has encountered an error.', - 'version' => 'none', + 'name' => 'error_level', + 'language' => 'zu', + 'value' => 's:1:"1";', )) ->values(array( - 'lid' => '170', - 'location' => 'includes/content.admin.inc:1478', - 'textgroup' => 'default', - 'source' => 'The database has been altered and data has been migrated or deleted.', - 'version' => 'none', + 'name' => 'site_403', + 'language' => 'zu', + 'value' => 's:7:"zu-user";', )) ->values(array( - 'lid' => '171', - 'location' => 'includes/content.admin.inc:1481', - 'textgroup' => 'default', - 'source' => 'An error occurred and database alteration did not complete.', - 'version' => 'none', + 'name' => 'site_404', + 'language' => 'zu', + 'value' => 's:17:"zu-page-not-found";', )) ->values(array( - 'lid' => '172', - 'location' => 'includes/content.admin.inc:1584', - 'textgroup' => 'default', - 'source' => 'Processing %title', - 'version' => 'none', + 'name' => 'site_footer', + 'language' => 'zu', + 'value' => 's:0:"";', )) ->values(array( - 'lid' => '173', - 'location' => 'includes/content.admin.inc:1849', - 'textgroup' => 'default', - 'source' => '%name must be an integer.', - 'version' => 'none', + 'name' => 'site_frontpage', + 'language' => 'zu', + 'value' => 's:4:"node";', )) ->values(array( - 'lid' => '174', - 'location' => 'includes/content.admin.inc:1859', - 'textgroup' => 'default', - 'source' => '%name must be a positive integer.', - 'version' => 'none', + 'name' => 'site_mail', + 'language' => 'zu', + 'value' => 's:21:"site_mail@example.com";', )) ->values(array( - 'lid' => '175', - 'location' => 'includes/content.admin.inc:1869', - 'textgroup' => 'default', - 'source' => '%name must be a number.', - 'version' => 'none', + 'name' => 'site_mission', + 'language' => 'zu', + 'value' => 's:0:"";', )) ->values(array( - 'lid' => '176', - 'location' => 'includes/content.admin.inc:1482', - 'textgroup' => 'default', - 'source' => '1 item successfully processed:', - 'version' => 'none', + 'name' => 'site_name', + 'language' => 'zu', + 'value' => 's:14:"zu - site_name";', )) ->values(array( - 'lid' => '177', - 'location' => 'includes/content.admin.inc:1482', - 'textgroup' => 'default', - 'source' => '@count items successfully processed:', - 'version' => 'none', + 'name' => 'site_slogan', + 'language' => 'zu', + 'value' => 's:13:"Migrate rocks";', )) ->values(array( - 'lid' => '178', - 'location' => 'includes/content.crud.inc:591', - 'textgroup' => 'default', - 'source' => 'Content fields table %old_name has been renamed to %new_name and field instances have been updated.', - 'version' => 'none', + 'name' => 'user_email_verification', + 'language' => 'zu', + 'value' => 'i:0;', )) ->values(array( - 'lid' => '179', - 'location' => 'includes/content.crud.inc:629', - 'textgroup' => 'default', - 'source' => 'The content fields table %name has been deleted.', - 'version' => 'none', + 'name' => 'user_mail_password_reset_body', + 'language' => 'zu', + 'value' => "s:419:\"!username,\r\n\r\nA request to reset the password for your account has been made at !site.\r\n\r\nYou may now log in to !uri_brief by clicking on this link or copying and pasting it in your browser:\r\n\r\n!login_url\r\n\r\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\r\n\r\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\";", )) ->values(array( - 'lid' => '180', - 'location' => 'includes/content.node_form.inc:206', - 'textgroup' => 'default', - 'source' => 'Add another item', - 'version' => 'none', + 'name' => 'user_mail_password_reset_subject', + 'language' => 'zu', + 'value' => 's:52:"Replacement login information for !username at !site";', )) ->values(array( - 'lid' => '181', - 'location' => 'includes/content.panels.inc:21;35', - 'textgroup' => 'default', - 'source' => 'Content field', - 'version' => 'none', + 'name' => 'user_mail_register_admin_created_body', + 'language' => 'zu', + 'value' => "s:473:\"zu - !username,\r\n\r\nA site administrator at !site has created an account for you. You may now log in to !login_uri using the following username and password:\r\n\r\nusername: !username\r\npassword: !password\r\n\r\nYou may also log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n!login_url\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\r\n\r\n\r\n-- !site team\";", )) ->values(array( - 'lid' => '182', - 'location' => 'includes/content.panels.inc:38', - 'textgroup' => 'default', - 'source' => 'A content field from the referenced node.', - 'version' => 'none', + 'name' => 'user_mail_register_admin_created_subject', + 'language' => 'zu', + 'value' => 's:57:"zu - An administrator created an account for you at !site";', )) ->values(array( - 'lid' => '183', - 'location' => 'includes/content.panels.inc:39, modules/fieldgroup/fieldgroup.panels.inc:31', - 'textgroup' => 'default', - 'source' => 'Node', - 'version' => 'none', + 'name' => 'user_mail_register_no_approval_required_body', + 'language' => 'zu', + 'value' => "s:442:\"!username,\r\n\r\nThank you for registering at !site. You may now log in to !login_uri using the following username and password:\r\n\r\nusername: !username\r\npassword: !password\r\n\r\nYou may also log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n!login_url\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\r\n\r\n\r\n-- !site team\";", )) ->values(array( - 'lid' => '184', - 'location' => 'includes/content.panels.inc:40, modules/fieldgroup/fieldgroup.panels.inc:32', - 'textgroup' => 'default', - 'source' => 'Node context', - 'version' => 'none', + 'name' => 'user_mail_register_no_approval_required_subject', + 'language' => 'zu', + 'value' => 's:38:"Account details for !username at !site";', )) ->values(array( - 'lid' => '185', - 'location' => 'includes/content.panels.inc:52', - 'textgroup' => 'default', - 'source' => 'Block title', - 'version' => 'none', + 'name' => 'user_mail_register_pending_approval_body', + 'language' => 'zu', + 'value' => "s:272:\"!username,\r\n\r\nThank you for registering at !site. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\r\n\r\n\r\n-- !site team\";", )) ->values(array( - 'lid' => '186', - 'location' => 'includes/content.panels.inc:55', - 'textgroup' => 'default', - 'source' => 'Hidden', - 'version' => 'none', + 'name' => 'user_mail_register_pending_approval_subject', + 'language' => 'zu', + 'value' => 's:63:"Account details for !username at !site (pending admin approval)";', )) ->values(array( - 'lid' => '187', - 'location' => 'includes/content.panels.inc:57', - 'textgroup' => 'default', - 'source' => 'Configure how the label is going to be displayed.', - 'version' => 'none', + 'name' => 'user_mail_status_activated_body', + 'language' => 'zu', + 'value' => "s:434:\"!username,\r\n\r\nYour account at !site has been activated.\r\n\r\nYou may now log in by clicking on this link or copying and pasting it in your browser:\r\n\r\n!login_url\r\n\r\nThis is a one-time login, so it can be used only once.\r\n\r\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\r\n\r\nOnce you have set your own password, you will be able to log in to !login_uri in the future using:\r\n\r\nusername: !username\r\n\";", )) ->values(array( - 'lid' => '188', - 'location' => 'includes/content.panels.inc:73', - 'textgroup' => 'default', - 'source' => 'Field / Formatter', - 'version' => 'none', + 'name' => 'user_mail_status_activated_notify', + 'language' => 'zu', + 'value' => 'i:0;', )) ->values(array( - 'lid' => '189', - 'location' => 'includes/content.panels.inc:76', - 'textgroup' => 'default', - 'source' => 'Select a field and formatter.', - 'version' => 'none', + 'name' => 'user_mail_status_activated_subject', + 'language' => 'zu', + 'value' => 's:49:"Account details for !username at !site (approved)";', )) ->values(array( - 'lid' => '190', - 'location' => 'includes/content.panels.inc:92', - 'textgroup' => 'default', - 'source' => '"@s" field @name', - 'version' => 'none', + 'name' => 'user_mail_status_blocked_body', + 'language' => 'zu', + 'value' => "s:53:\"!username,\r\n\r\nYour account on !site has been blocked.\";", )) ->values(array( - 'lid' => '191', - 'location' => 'includes/content.rules.inc:15', - 'textgroup' => 'default', - 'source' => 'Populate a field', - 'version' => 'none', + 'name' => 'user_mail_status_blocked_notify', + 'language' => 'zu', + 'value' => 'i:1;', )) ->values(array( - 'lid' => '192', - 'location' => 'includes/content.rules.inc:23;212', - 'textgroup' => 'default', - 'source' => 'You should make sure that the used field exists in the given content type.', - 'version' => 'none', + 'name' => 'user_mail_status_blocked_subject', + 'language' => 'zu', + 'value' => 's:48:"Account details for !username at !site (blocked)";', )) ->values(array( - 'lid' => '193', - 'location' => 'includes/content.admin.inc:413', - 'textgroup' => 'default', - 'source' => 'Field', - 'version' => 'none', + 'name' => 'user_mail_status_deleted_body', + 'language' => 'zu', + 'value' => "s:53:\"!username,\r\n\r\nYour account on !site has been deleted.\";", )) ->values(array( - 'lid' => '194', - 'location' => 'includes/content.rules.inc:56', - 'textgroup' => 'default', - 'source' => 'Select the machine-name of the field.', - 'version' => 'none', + 'name' => 'user_mail_status_deleted_notify', + 'language' => 'zu', + 'value' => 'i:0;', )) ->values(array( - 'lid' => '195', - 'location' => 'includes/content.rules.inc:84', - 'textgroup' => 'default', - 'source' => 'Advanced: Specify the fields value with PHP code', - 'version' => 'none', + 'name' => 'user_mail_status_deleted_subject', + 'language' => 'zu', + 'value' => 's:48:"Account details for !username at !site (deleted)";', )) ->values(array( - 'lid' => '196', - 'location' => 'includes/content.rules.inc:102', - 'textgroup' => 'default', - 'source' => "Advanced usage only: PHP code that returns the value to set. Should not include <?php ?> delimiters. If this field is filled out, the value returned by this code will override any value specified above. Expected format:
                          !sample
                          Using devel.module's 'devel load' tab on a content page might help you figure out the expected format.", - 'version' => 'none', + 'name' => 'user_pictures', + 'language' => 'zu', + 'value' => 's:1:"0";', )) ->values(array( - 'lid' => '197', - 'location' => 'includes/content.rules.inc:130', - 'textgroup' => 'default', - 'source' => 'You have to return the default value in the expected format.', - 'version' => 'none', + 'name' => 'user_picture_default', + 'language' => 'zu', + 'value' => 's:0:"";', )) ->values(array( - 'lid' => '198', - 'location' => 'includes/content.rules.inc:181', - 'textgroup' => 'default', - 'source' => "Populate @node's field '@field'", - 'version' => 'none', + 'name' => 'user_picture_dimensions', + 'language' => 'zu', + 'value' => 's:5:"85x85";', )) ->values(array( - 'lid' => '199', - 'location' => 'includes/content.rules.inc:198', - 'textgroup' => 'default', - 'source' => 'Field has value', - 'version' => 'none', + 'name' => 'user_picture_file_size', + 'language' => 'zu', + 'value' => 's:2:"30";', )) ->values(array( - 'lid' => '200', - 'location' => 'includes/content.rules.inc:203', - 'textgroup' => 'default', - 'source' => 'You should make sure that the used field exists in the given content type. The condition returns TRUE, if the selected field has the given value.', - 'version' => 'none', + 'name' => 'user_picture_guidelines', + 'language' => 'zu', + 'value' => 's:0:"";', )) ->values(array( - 'lid' => '201', - 'location' => 'includes/content.rules.inc:207', - 'textgroup' => 'default', - 'source' => 'Field has changed', - 'version' => 'none', + 'name' => 'user_picture_path', + 'language' => 'zu', + 'value' => 's:8:"pictures";', )) ->values(array( - 'lid' => '202', - 'location' => 'includes/content.rules.inc:209', - 'textgroup' => 'default', - 'source' => 'Content containing changes', - 'version' => 'none', + 'name' => 'user_register', + 'language' => 'zu', + 'value' => 's:1:"0";', )) ->values(array( - 'lid' => '203', - 'location' => 'includes/content.rules.inc:210', - 'textgroup' => 'default', - 'source' => 'Content not containing changes', - 'version' => 'none', + 'name' => 'user_registration_help', + 'language' => 'zu', + 'value' => 's:0:"";', )) ->values(array( - 'lid' => '204', - 'location' => 'includes/content.rules.inc:249', - 'textgroup' => 'default', - 'source' => "@node's field '@field' has value", - 'version' => 'none', + 'name' => 'user_signatures', + 'language' => 'zu', + 'value' => 's:1:"1";', )) -->values(array( - 'lid' => '205', - 'location' => 'includes/content.rules.inc:269', - 'textgroup' => 'default', - 'source' => 'Select the machine-name of the field to look at.', - 'version' => 'none', +->execute(); + +$connection->schema()->createTable('imagecache_action', array( + 'fields' => array( + 'actionid' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'presetid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + 'unsigned' => TRUE, + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ), + 'module' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '255', + ), + 'action' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '255', + ), + 'data' => array( + 'type' => 'text', + 'not null' => TRUE, + 'size' => 'normal', + ), + ), + 'primary key' => array( + 'actionid', + ), + 'mysql_character_set' => 'utf8', +)); + +$connection->insert('imagecache_action') +->fields(array( + 'actionid', + 'presetid', + 'weight', + 'module', + 'action', + 'data', )) ->values(array( - 'lid' => '206', - 'location' => 'includes/content.rules.inc:275', - 'textgroup' => 'default', - 'source' => "@node's field '@field' has been changed", - 'version' => 'none', + 'actionid' => '3', + 'presetid' => '1', + 'weight' => '0', + 'module' => 'imagecache', + 'action' => 'imagecache_scale_and_crop', + 'data' => 'a:2:{s:5:"width";s:4:"100%";s:6:"height";s:4:"100%";}', )) ->values(array( - 'lid' => '207', - 'location' => 'includes/content.token.inc:12;15', - 'textgroup' => 'default', - 'source' => 'Token', - 'version' => 'none', + 'actionid' => '4', + 'presetid' => '2', + 'weight' => '0', + 'module' => 'imagecache', + 'action' => 'imagecache_crop', + 'data' => 'a:4:{s:5:"width";s:3:"555";s:6:"height";s:4:"5555";s:7:"xoffset";s:6:"center";s:7:"yoffset";s:6:"center";}', )) ->values(array( - 'lid' => '208', - 'location' => 'includes/content.token.inc:60', - 'textgroup' => 'default', - 'source' => 'Referenced node ID', - 'version' => 'none', + 'actionid' => '5', + 'presetid' => '2', + 'weight' => '0', + 'module' => 'imagecache', + 'action' => 'imagecache_resize', + 'data' => 'a:2:{s:5:"width";s:3:"55%";s:6:"height";s:3:"55%";}', )) ->values(array( - 'lid' => '209', - 'location' => 'includes/content.token.inc:61', - 'textgroup' => 'default', - 'source' => 'Referenced node title', - 'version' => 'none', + 'actionid' => '6', + 'presetid' => '2', + 'weight' => '0', + 'module' => 'imagecache', + 'action' => 'imagecache_rotate', + 'data' => 'a:3:{s:7:"degrees";s:2:"55";s:6:"random";i:0;s:7:"bgcolor";s:0:"";}', )) -->values(array( - 'lid' => '210', +->execute(); + +$connection->schema()->createTable('imagecache_preset', array( + 'fields' => array( + 'presetid' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'presetname' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '255', + ), + ), + 'primary key' => array( + 'presetid', + ), + 'mysql_character_set' => 'utf8', +)); + +$connection->insert('imagecache_preset') +->fields(array( + 'presetid', + 'presetname', +)) +->values(array( + 'presetid' => '1', + 'presetname' => 'slackjaw_boys', +)) +->values(array( + 'presetid' => '2', + 'presetname' => 'big_blue_cheese', +)) +->execute(); + +$connection->schema()->createTable('languages', array( + 'fields' => array( + 'language' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '12', + 'default' => '', + ), + 'name' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '64', + 'default' => '', + ), + 'native' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '64', + 'default' => '', + ), + 'direction' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ), + 'enabled' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ), + 'plurals' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ), + 'formula' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'domain' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'prefix' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'weight' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ), + 'javascript' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '32', + 'default' => '', + ), + ), + 'primary key' => array( + 'language', + ), + 'indexes' => array( + 'list' => array( + 'weight', + 'name', + ), + ), + 'mysql_character_set' => 'utf8', +)); + +$connection->insert('languages') +->fields(array( + 'language', + 'name', + 'native', + 'direction', + 'enabled', + 'plurals', + 'formula', + 'domain', + 'prefix', + 'weight', + 'javascript', +)) +->values(array( + 'language' => 'en', + 'name' => 'English', + 'native' => 'English', + 'direction' => '0', + 'enabled' => '1', + 'plurals' => '0', + 'formula' => '', + 'domain' => '', + 'prefix' => '', + 'weight' => '0', + 'javascript' => '', +)) +->values(array( + 'language' => 'fr', + 'name' => 'French', + 'native' => 'Français', + 'direction' => '0', + 'enabled' => '1', + 'plurals' => '2', + 'formula' => '($n>1)', + 'domain' => '', + 'prefix' => 'fr', + 'weight' => '0', + 'javascript' => '047746d30d76aa44a54db9923c7c5fb0', +)) +->values(array( + 'language' => 'zu', + 'name' => 'Zulu', + 'native' => 'isiZulu', + 'direction' => '0', + 'enabled' => '1', + 'plurals' => '0', + 'formula' => '', + 'domain' => '', + 'prefix' => 'zu', + 'weight' => '0', + 'javascript' => '', +)) +->execute(); + +$connection->schema()->createTable('locales_source', array( + 'fields' => array( + 'lid' => array( + 'type' => 'serial', + 'not null' => TRUE, + 'size' => 'normal', + ), + 'location' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '255', + 'default' => '', + ), + 'textgroup' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '255', + 'default' => 'default', + ), + 'source' => array( + 'type' => 'blob', + 'not null' => TRUE, + 'size' => 'normal', + ), + 'version' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '20', + 'default' => 'none', + ), + ), + 'primary key' => array( + 'lid', + ), + 'indexes' => array( + 'source' => array( + array( + 'source', + '30', + ), + ), + 'textgroup_location' => array( + array( + 'textgroup', + '30', + ), + array( + 'location', + '191', + ), + ), + ), + 'mysql_character_set' => 'utf8', +)); + +$connection->insert('locales_source') +->fields(array( + 'lid', + 'location', + 'textgroup', + 'source', + 'version', +)) +->values(array( + 'lid' => '1', + 'location' => 'misc/drupal.js', + 'textgroup' => 'default', + 'source' => 'Unspecified error', + 'version' => 'none', +)) +->values(array( + 'lid' => '2', + 'location' => 'misc/drupal.js', + 'textgroup' => 'default', + 'source' => 'An error occurred. \n@uri\n@text', + 'version' => 'none', +)) +->values(array( + 'lid' => '3', + 'location' => 'misc/drupal.js', + 'textgroup' => 'default', + 'source' => 'An error occurred. \n@uri\n(no information available).', + 'version' => 'none', +)) +->values(array( + 'lid' => '4', + 'location' => 'misc/drupal.js', + 'textgroup' => 'default', + 'source' => 'An HTTP error @status occurred. \n@uri', + 'version' => 'none', +)) +->values(array( + 'lid' => '5', + 'location' => 'content.module:21', + 'textgroup' => 'default', + 'source' => 'The content module, a required component of the Content Construction Kit (CCK), allows administrators to associate custom fields with content types. In Drupal, content types are used to define the characteristics of a post, including the title and description of the fields displayed on its add and edit pages. Using the content module (and the other helper modules included in CCK), custom fields beyond the default "Title" and "Body" may be added. CCK features are accessible through tabs on the content types administration page. (See the node module help page for more information about content types.)', + 'version' => 'none', +)) +->values(array( + 'lid' => '6', + 'location' => 'content.module:22', + 'textgroup' => 'default', + 'source' => 'When adding a custom field to a content type, you determine its type (whether it will contain text, numbers, or references to other objects) and how it will be displayed (either as a text field or area, a select box, checkbox, radio button, or autocompleting field). A field may have multiple values (i.e., a "person" may have multiple e-mail addresses) or a single value (i.e., an "employee" has a single employee identification number). As you add and edit fields, CCK automatically adjusts the structure of the database as necessary. CCK also provides a number of other features, including intelligent caching for your custom data, an import and export facility for content type definitions, and integration with other contributed modules.', + 'version' => 'none', +)) +->values(array( + 'lid' => '7', + 'location' => 'content.module:23', + 'textgroup' => 'default', + 'source' => 'Custom field types are provided by a set of optional modules included with CCK (each module provides a different type). The modules page allows you to enable or disable CCK components. A default installation of CCK includes:', + 'version' => 'none', +)) +->values(array( + 'lid' => '8', + 'location' => 'content.module:25', + 'textgroup' => 'default', + 'source' => 'number, which adds numeric field types, in integer, decimal or floating point form. You may define a set of allowed inputs, or specify an allowable range of values. A variety of common formats for displaying numeric data are available.', + 'version' => 'none', +)) +->values(array( + 'lid' => '9', + 'location' => 'content.module:26', + 'textgroup' => 'default', + 'source' => "text, which adds text field types. A text field may contain plain text only, or optionally, may use Drupal's input format filters to securely manage rich text input. Text input fields may be either a single line (text field), multiple lines (text area), or for greater input control, a select box, checkbox, or radio buttons. If desired, CCK can validate the input to a set of allowed values.", + 'version' => 'none', +)) +->values(array( + 'lid' => '10', + 'location' => 'content.module:27', + 'textgroup' => 'default', + 'source' => 'nodereference, which creates custom references between Drupal nodes. By adding a nodereference field and two different content types, for instance, you can easily create complex parent/child relationships between data (multiple "employee" nodes may contain a nodereference field linking to an "employer" node).', + 'version' => 'none', +)) +->values(array( + 'lid' => '11', + 'location' => 'content.module:28', + 'textgroup' => 'default', + 'source' => "userreference, which creates custom references to your sites' user accounts. By adding a userreference field, you can create complex relationships between your site's users and posts. To track user involvement in a post beyond Drupal's standard Authored by field, for instance, add a userreference field named \"Edited by\" to a content type to store a link to an editor's user account page.", + 'version' => 'none', +)) +->values(array( + 'lid' => '12', + 'location' => 'content.module:29', + 'textgroup' => 'default', + 'source' => 'fieldgroup, which creates collapsible fieldsets to hold a group of related fields. A fieldset may either be open or closed by default. The order of your fieldsets, and the order of fields within a fieldset, is managed via a drag-and-drop interface provided by content module.', + 'version' => 'none', +)) +->values(array( + 'lid' => '13', + 'location' => 'content.module:31', + 'textgroup' => 'default', + 'source' => 'For more information, see the online handbook entry for CCK or the CCK project page.', + 'version' => 'none', +)) +->values(array( + 'lid' => '14', + 'location' => 'theme/theme.inc:111', + 'textgroup' => 'default', + 'source' => "Configure how this content type's fields and field labels should be displayed when it's viewed in teaser and full-page mode.", + 'version' => 'none', +)) +->values(array( + 'lid' => '15', + 'location' => 'theme/theme.inc:114', + 'textgroup' => 'default', + 'source' => "Configure how this content type's fields should be displayed when it's rendered in the following contexts.", + 'version' => 'none', +)) +->values(array( + 'lid' => '16', + 'location' => 'content.module:48', + 'textgroup' => 'default', + 'source' => 'Control the order of fields in the input form.', + 'version' => 'none', +)) +->values(array( + 'lid' => '17', + 'location' => 'content.module:492', + 'textgroup' => 'default', + 'source' => 'This field is required.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '18', + 'location' => 'content.module:496', + 'textgroup' => 'default', + 'source' => '!title: !required', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '19', + 'location' => 'content.module:499, modules/content_multigroup/content_multigroup.module:903', + 'textgroup' => 'default', + 'source' => 'Order', + 'version' => 'none', +)) +->values(array( + 'lid' => '20', + 'location' => 'content.module:1640', + 'textgroup' => 'default', + 'source' => 'RSS Item', + 'version' => 'none', +)) +->values(array( + 'lid' => '21', + 'location' => 'content.module:1883', + 'textgroup' => 'default', + 'source' => 'Search Index', + 'version' => 'none', +)) +->values(array( + 'lid' => '22', + 'location' => 'content.module:1887', + 'textgroup' => 'default', + 'source' => 'Search Result', + 'version' => 'none', +)) +->values(array( + 'lid' => '23', + 'location' => 'content.module:2362', + 'textgroup' => 'default', + 'source' => 'Language', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '24', + 'location' => 'content.module:2376', + 'textgroup' => 'default', + 'source' => 'Taxonomy', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '25', + 'location' => 'content.module:2407', + 'textgroup' => 'default', + 'source' => 'File attachments', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '26', + 'location' => 'content.module:595', + 'textgroup' => 'default', + 'source' => 'Updating field type %type with module %module.', + 'version' => 'none', +)) +->values(array( + 'lid' => '27', + 'location' => 'content.module:602', + 'textgroup' => 'default', + 'source' => 'Updating widget type %type with module %module.', + 'version' => 'none', +)) +->values(array( + 'lid' => '28', + 'location' => 'content.module:60', + 'textgroup' => 'default', + 'source' => 'Use PHP input for field settings (dangerous - grant with care)', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '29', + 'location' => 'content.module:101', + 'textgroup' => 'default', + 'source' => 'Manage fields', + 'version' => 'none', +)) +->values(array( + 'lid' => '30', + 'location' => 'content.module:110', + 'textgroup' => 'default', + 'source' => 'Display fields', + 'version' => 'none', +)) +->values(array( + 'lid' => '31', + 'location' => 'content.module:143', + 'textgroup' => 'default', + 'source' => 'General', + 'version' => 'none', +)) +->values(array( + 'lid' => '32', + 'location' => 'content.module:149', + 'textgroup' => 'default', + 'source' => 'Advanced', + 'version' => 'none', +)) +->values(array( + 'lid' => '33', + 'location' => 'content.module:141', + 'textgroup' => 'default', + 'source' => 'Remove field', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '34', + 'location' => 'content.info:0, includes/content.rules.inc:19;212, includes/views/content.views.inc:180;261', + 'textgroup' => 'default', + 'source' => 'Content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '35', + 'location' => 'content.info:0', + 'textgroup' => 'default', + 'source' => 'Allows administrators to define new content types.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '36', + 'location' => 'content.info:0, modules/content_copy/content_copy.info:0, modules/content_permissions/content_permissions.info:0, modules/fieldgroup/fieldgroup.info:0', + 'textgroup' => 'default', + 'source' => 'CCK', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '37', + 'location' => 'modules/text/text.module:41, modules/text/text.info:0', + 'textgroup' => 'default', + 'source' => 'Text', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '38', + 'location' => 'examples/example_field.php:178', + 'textgroup' => 'default', + 'source' => 'The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database and it must match the field storage type, %type. The label is optional and the key will be used as the label if no label is specified.', + 'version' => 'none', +)) +->values(array( + 'lid' => '39', + 'location' => 'examples/example_field.php:484', + 'textgroup' => 'default', + 'source' => 'Text area', + 'version' => 'none', +)) +->values(array( + 'lid' => '40', + 'location' => 'includes/content.admin.inc:171;197;895, modules/fieldgroup/fieldgroup.module:209', + 'textgroup' => 'default', + 'source' => 'Remove', + 'version' => 'none', +)) +->values(array( + 'lid' => '41', + 'location' => 'content.module:1854', + 'textgroup' => 'default', + 'source' => 'Basic', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '42', + 'location' => 'content.module:1857, modules/nodereference/nodereference.module:268', + 'textgroup' => 'default', + 'source' => 'Teaser', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '43', + 'location' => 'content.module:1861, modules/nodereference/nodereference.module:263', + 'textgroup' => 'default', + 'source' => 'Full node', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '44', + 'location' => 'content.module:1867;1870', + 'textgroup' => 'default', + 'source' => 'RSS', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '45', + 'location' => 'content.module:1880', + 'textgroup' => 'default', + 'source' => 'Search', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '46', + 'location' => 'content.module:2348;2355', + 'textgroup' => 'default', + 'source' => 'Node module form.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '47', + 'location' => 'content.module:2363', + 'textgroup' => 'default', + 'source' => 'Locale module form.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '48', + 'location' => 'content.module:2369', + 'textgroup' => 'default', + 'source' => 'Menu settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '49', + 'location' => 'content.module:2370', + 'textgroup' => 'default', + 'source' => 'Menu module form.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '50', + 'location' => 'content.module:2377', + 'textgroup' => 'default', + 'source' => 'Taxonomy module form.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '51', + 'location' => 'content.module:2383', + 'textgroup' => 'default', + 'source' => 'Book', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '52', + 'location' => 'content.module:2384', + 'textgroup' => 'default', + 'source' => 'Book module form.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '53', + 'location' => 'content.module:2390', + 'textgroup' => 'default', + 'source' => 'Poll title', + 'version' => 'none', +)) +->values(array( + 'lid' => '54', + 'location' => 'content.module:2391', + 'textgroup' => 'default', + 'source' => 'Poll module title.', + 'version' => 'none', +)) +->values(array( + 'lid' => '55', + 'location' => 'content.module:2396', + 'textgroup' => 'default', + 'source' => 'Poll choices', + 'version' => 'none', +)) +->values(array( + 'lid' => '56', + 'location' => 'content.module:2397', + 'textgroup' => 'default', + 'source' => 'Poll module choices.', + 'version' => 'none', +)) +->values(array( + 'lid' => '57', + 'location' => 'content.module:2400', + 'textgroup' => 'default', + 'source' => 'Poll settings', + 'version' => 'none', +)) +->values(array( + 'lid' => '58', + 'location' => 'content.module:2401', + 'textgroup' => 'default', + 'source' => 'Poll module settings.', + 'version' => 'none', +)) +->values(array( + 'lid' => '59', + 'location' => 'content.module:2408', + 'textgroup' => 'default', + 'source' => 'Upload module form.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '60', + 'location' => 'content.module:595;602;0, includes/content.crud.inc:589;633', + 'textgroup' => 'default', + 'source' => 'content', + 'version' => 'none', +)) +->values(array( + 'lid' => '61', + 'location' => 'content.module:79', + 'textgroup' => 'default', + 'source' => 'Fields', + 'version' => 'none', +)) +->values(array( + 'lid' => '62', + 'location' => 'content.install:236', + 'textgroup' => 'default', + 'source' => "Updates for CCK-related modules are not run until the modules are enabled on the administer modules page. When you enable them, you'll need to return to update.php and run the remaining updates.", + 'version' => 'none', +)) +->values(array( + 'lid' => '63', + 'location' => 'content.install:239', + 'textgroup' => 'default', + 'source' => '!module.module has updates but cannot be updated because content.module is not enabled.
                          If and when content.module is enabled, you will need to re-run the update script. You will continue to see this message until the module is enabled and updates are run.', + 'version' => 'none', +)) +->values(array( + 'lid' => '64', + 'location' => 'content.install:244', + 'textgroup' => 'default', + 'source' => '!module.module has updates and is available in the modules folder but is not enabled.
                          If and when it is enabled, you will need to re-run the update script. You will continue to see this message until the module is enabled and updates are run.', + 'version' => 'none', +)) +->values(array( + 'lid' => '65', + 'location' => 'content.install:251', + 'textgroup' => 'default', + 'source' => 'Some updates are still pending. Please return to update.php and run the remaining updates.', + 'version' => 'none', +)) +->values(array( + 'lid' => '66', + 'location' => '(duplicate) content.install:10', + 'textgroup' => 'default', + 'source' => 'CCK - No Views integration', + 'version' => 'none', +)) +->values(array( + 'lid' => '67', + 'location' => '(duplicate) content.install:11', + 'textgroup' => 'default', + 'source' => 'CCK integration with Views module requires Views 6.x-2.0-rc2 or greater.', + 'version' => 'none', +)) +->values(array( + 'lid' => '68', + 'location' => 'includes/content.admin.inc:16, modules/content_copy/content_copy_export_form.tpl.php:11, theme/content-admin-field-overview-form.tpl.php:12', + 'textgroup' => 'default', + 'source' => 'Name', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '69', + 'location' => 'includes/content.admin.inc:16, modules/content_copy/content_copy_export_form.tpl.php:12, theme/content-admin-field-overview-form.tpl.php:13', + 'textgroup' => 'default', + 'source' => 'Type', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '70', + 'location' => 'includes/content.admin.inc:16, modules/fieldgroup/fieldgroup.module:158', + 'textgroup' => 'default', + 'source' => 'Description', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '71', + 'location' => 'includes/content.admin.inc:16, theme/content-admin-field-overview-form.tpl.php:14', + 'textgroup' => 'default', + 'source' => 'Operations', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '72', + 'location' => 'includes/content.admin.inc:30', + 'textgroup' => 'default', + 'source' => 'edit', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '73', + 'location' => 'includes/content.admin.inc:33', + 'textgroup' => 'default', + 'source' => 'manage fields', + 'version' => 'none', +)) +->values(array( + 'lid' => '74', + 'location' => 'includes/content.admin.inc:36', + 'textgroup' => 'default', + 'source' => 'delete', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '75', + 'location' => 'includes/content.admin.inc:47', + 'textgroup' => 'default', + 'source' => 'No content types available.', + 'version' => 'none', +)) +->values(array( + 'lid' => '76', + 'location' => 'includes/content.admin.inc:54', + 'textgroup' => 'default', + 'source' => '» Add a new content type', + 'version' => 'none', +)) +->values(array( + 'lid' => '77', + 'location' => 'includes/content.admin.inc:67;796;991', + 'textgroup' => 'default', + 'source' => 'Field name', + 'version' => 'none', +)) +->values(array( + 'lid' => '78', + 'location' => 'includes/content.admin.inc:67;811;997', + 'textgroup' => 'default', + 'source' => 'Field type', + 'version' => 'none', +)) +->values(array( + 'lid' => '79', + 'location' => 'includes/content.admin.inc:67', + 'textgroup' => 'default', + 'source' => 'Used in', + 'version' => 'none', +)) +->values(array( + 'lid' => '80', + 'location' => 'includes/content.admin.inc:71', + 'textgroup' => 'default', + 'source' => '@field_name (Locked)', + 'version' => 'none', +)) +->values(array( + 'lid' => '81', + 'location' => 'includes/content.admin.inc:90', + 'textgroup' => 'default', + 'source' => 'No fields have been defined for any content type yet.', + 'version' => 'none', +)) +->values(array( + 'lid' => '82', + 'location' => 'not literally, English needs work, includes/content.admin.inc:106, fuzzy', + 'textgroup' => 'default', + 'source' => 'This content type has inactive fields. Inactive fields are not included in lists of available fields until their modules are enabled.', + 'version' => 'none', +)) +->values(array( + 'lid' => '83', + 'location' => 'includes/content.admin.inc:108', + 'textgroup' => 'default', + 'source' => '!field (!field_name) is an inactive !field_type field that uses a !widget_type widget.', + 'version' => 'none', +)) +->values(array( + 'lid' => '84', + 'location' => 'includes/content.admin.inc:170;196', + 'textgroup' => 'default', + 'source' => 'Configure', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '85', + 'location' => 'Schreibgeschützt, includes/content.admin.inc:181', + 'textgroup' => 'default', + 'source' => 'Locked', + 'version' => 'none', +)) +->values(array( + 'lid' => '86', + 'location' => 'includes/content.admin.inc:237', + 'textgroup' => 'default', + 'source' => '- Select a field type -', + 'version' => 'none', +)) +->values(array( + 'lid' => '87', + 'location' => 'includes/content.admin.inc:238', + 'textgroup' => 'default', + 'source' => '- Select a widget -', + 'version' => 'none', +)) +->values(array( + 'lid' => '88', + 'location' => 'includes/content.admin.inc:244;285;315;804;985, includes/panels/content_types/content_field.inc:97, includes/views/handlers/content_handler_field.inc:56', + 'textgroup' => 'default', + 'source' => 'Label', + 'version' => 'none', +)) +->values(array( + 'lid' => '89', + 'location' => 'includes/content.admin.inc:253', + 'textgroup' => 'default', + 'source' => 'Field name (a-z, 0-9, _)', + 'version' => 'none', +)) +->values(array( + 'lid' => '90', + 'location' => 'includes/content.admin.inc:258', + 'textgroup' => 'default', + 'source' => 'Type of data to store.', + 'version' => 'none', +)) +->values(array( + 'lid' => '91', + 'location' => 'includes/content.admin.inc:263;295', + 'textgroup' => 'default', + 'source' => 'Form element to edit the data.', + 'version' => 'none', +)) +->values(array( + 'lid' => '92', + 'location' => 'includes/content.admin.inc:279', + 'textgroup' => 'default', + 'source' => '- Select an existing field -', + 'version' => 'none', +)) +->values(array( + 'lid' => '93', + 'location' => 'includes/content.admin.inc:290', + 'textgroup' => 'default', + 'source' => 'Field to share', + 'version' => 'none', +)) +->values(array( + 'lid' => '94', + 'location' => 'includes/content.admin.inc:324', + 'textgroup' => 'default', + 'source' => 'Group name (a-z, 0-9, _)', + 'version' => 'none', +)) +->values(array( + 'lid' => '95', + 'location' => 'includes/content.admin.inc:352;677, modules/fieldgroup/fieldgroup.module:177;341', + 'textgroup' => 'default', + 'source' => 'Save', + 'version' => 'none', +)) +->values(array( + 'lid' => '96', + 'location' => 'includes/content.admin.inc:373', + 'textgroup' => 'default', + 'source' => 'Add new field: you need to provide a label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '97', + 'location' => 'includes/content.admin.inc:378', + 'textgroup' => 'default', + 'source' => 'Add new field: you need to provide a field name.', + 'version' => 'none', +)) +->values(array( + 'lid' => '98', + 'location' => 'includes/content.admin.inc:392', + 'textgroup' => 'default', + 'source' => 'Add new field: the field name %field_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', + 'version' => 'none', +)) +->values(array( + 'lid' => '99', + 'location' => 'includes/content.admin.inc:395', + 'textgroup' => 'default', + 'source' => "Add new field: the field name %field_name is too long. The name is limited to 32 characters, including the 'field_' prefix.", + 'version' => 'none', +)) +->values(array( + 'lid' => '100', + 'location' => 'includes/content.admin.inc:399', + 'textgroup' => 'default', + 'source' => "Add new field: the name 'field_instance' is a reserved name.", + 'version' => 'none', +)) +->values(array( + 'lid' => '101', + 'location' => 'includes/content.admin.inc:411', + 'textgroup' => 'default', + 'source' => 'Add new field: the field name %field_name already exists.', + 'version' => 'none', +)) +->values(array( + 'lid' => '102', + 'location' => 'includes/content.admin.inc:417', + 'textgroup' => 'default', + 'source' => 'Add new field: you need to select a field type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '103', + 'location' => 'includes/content.admin.inc:422', + 'textgroup' => 'default', + 'source' => 'Add new field: you need to select a widget.', + 'version' => 'none', +)) +->values(array( + 'lid' => '104', + 'location' => 'includes/content.admin.inc:428', + 'textgroup' => 'default', + 'source' => 'Add new field: invalid widget.', + 'version' => 'none', +)) +->values(array( + 'lid' => '105', + 'location' => 'includes/content.admin.inc:449', + 'textgroup' => 'default', + 'source' => 'Add existing field: you need to provide a label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '106', + 'location' => 'includes/content.admin.inc:454', + 'textgroup' => 'default', + 'source' => 'Add existing field: you need to select a field.', + 'version' => 'none', +)) +->values(array( + 'lid' => '107', + 'location' => 'includes/content.admin.inc:459', + 'textgroup' => 'default', + 'source' => 'Add existing field: you need to select a widget.', + 'version' => 'none', +)) +->values(array( + 'lid' => '108', + 'location' => 'includes/content.admin.inc:465', + 'textgroup' => 'default', + 'source' => 'Add existing field: invalid widget.', + 'version' => 'none', +)) +->values(array( + 'lid' => '109', + 'location' => 'includes/content.admin.inc:514', + 'textgroup' => 'default', + 'source' => 'There was a problem creating field %label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '110', + 'location' => 'includes/content.admin.inc:526', + 'textgroup' => 'default', + 'source' => 'The field %label cannot be added to a content type because it is locked.', + 'version' => 'none', +)) +->values(array( + 'lid' => '111', + 'location' => 'includes/content.admin.inc:536', + 'textgroup' => 'default', + 'source' => 'There was a problem adding field %label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '112', + 'location' => 'includes/content.admin.inc:578', + 'textgroup' => 'default', + 'source' => 'There are no fields configured for this content type. You can add new fields on the Manage fields page.', + 'version' => 'none', +)) +->values(array( + 'lid' => '113', + 'location' => 'includes/content.admin.inc:585;633, includes/panels/content_types/content_field.inc:101, modules/content_multigroup/content_multigroup.module:352', + 'textgroup' => 'default', + 'source' => 'Above', + 'version' => 'none', +)) +->values(array( + 'lid' => '114', + 'location' => 'includes/content.admin.inc:586, includes/panels/content_types/content_field.inc:102', + 'textgroup' => 'default', + 'source' => 'Inline', + 'version' => 'none', +)) +->values(array( + 'lid' => '115', + 'location' => 'includes/content.admin.inc:625;668', + 'textgroup' => 'default', + 'source' => 'Include', + 'version' => 'none', +)) +->values(array( + 'lid' => '116', + 'location' => 'includes/content.admin.inc:625;668, theme/content-admin-display-overview-form.tpl.php:17', + 'textgroup' => 'default', + 'source' => 'Exclude', + 'version' => 'none', +)) +->values(array( + 'lid' => '117', + 'location' => 'includes/content.admin.inc:637', + 'textgroup' => 'default', + 'source' => 'no styling', + 'version' => 'none', +)) +->values(array( + 'lid' => '118', + 'location' => 'includes/content.admin.inc:638', + 'textgroup' => 'default', + 'source' => 'simple', + 'version' => 'none', +)) +->values(array( + 'lid' => '119', + 'location' => 'includes/content.admin.inc:639', + 'textgroup' => 'default', + 'source' => 'fieldset', + 'version' => 'none', +)) +->values(array( + 'lid' => '120', + 'location' => 'includes/content.admin.inc:640', + 'textgroup' => 'default', + 'source' => 'fieldset - collapsible', + 'version' => 'none', +)) +->values(array( + 'lid' => '121', + 'location' => 'includes/content.admin.inc:641', + 'textgroup' => 'default', + 'source' => 'fieldset - collapsed', + 'version' => 'none', +)) +->values(array( + 'lid' => '122', + 'location' => 'includes/content.admin.inc:697', + 'textgroup' => 'default', + 'source' => 'Your settings have been saved.', + 'version' => 'none', +)) +->values(array( + 'lid' => '123', + 'location' => 'includes/content.admin.inc:767', + 'textgroup' => 'default', + 'source' => '@type: @field (@label)', + 'version' => 'none', +)) +->values(array( + 'lid' => '124', + 'location' => 'includes/content.admin.inc:793', + 'textgroup' => 'default', + 'source' => 'Edit basic information', + 'version' => 'none', +)) +->values(array( + 'lid' => '125', + 'location' => 'includes/content.admin.inc:799', + 'textgroup' => 'default', + 'source' => 'The machine-readable name of the field. This name cannot be changed.', + 'version' => 'none', +)) +->values(array( + 'lid' => '126', + 'location' => 'includes/content.admin.inc:807', + 'textgroup' => 'default', + 'source' => 'A human-readable name to be used as the label for this field in the %type content type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '127', + 'location' => 'includes/content.admin.inc:814', + 'textgroup' => 'default', + 'source' => 'The type of data you would like to store in the database with this field. This option cannot be changed.', + 'version' => 'none', +)) +->values(array( + 'lid' => '128', + 'location' => 'includes/content.admin.inc:819;1003', + 'textgroup' => 'default', + 'source' => 'Widget type', + 'version' => 'none', +)) +->values(array( + 'lid' => '129', + 'location' => 'includes/content.admin.inc:823', + 'textgroup' => 'default', + 'source' => 'The type of form element you would like to present to the user when creating this field in the %type content type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '130', + 'location' => 'includes/content.admin.inc:833, includes/content.rules.inc:66', + 'textgroup' => 'default', + 'source' => 'Continue', + 'version' => 'none', +)) +->values(array( + 'lid' => '131', + 'location' => 'includes/content.admin.inc:861', + 'textgroup' => 'default', + 'source' => 'Updated basic settings for field %label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '132', + 'location' => 'includes/content.admin.inc:865', + 'textgroup' => 'default', + 'source' => 'There was a problem updating the basic settings for field %label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '133', + 'location' => 'includes/content.admin.inc:892', + 'textgroup' => 'default', + 'source' => 'Are you sure you want to remove the field %field?', + 'version' => 'none', +)) +->values(array( + 'lid' => '134', + 'location' => 'includes/content.admin.inc:894', + 'textgroup' => 'default', + 'source' => 'If you have any content left in this field, it will be lost. This action cannot be undone.', + 'version' => 'none', +)) +->values(array( + 'lid' => '135', + 'location' => 'includes/content.admin.inc:895, modules/fieldgroup/fieldgroup.module:209', + 'textgroup' => 'default', + 'source' => 'Cancel', + 'version' => 'none', +)) +->values(array( + 'lid' => '136', + 'location' => 'includes/content.admin.inc:901', + 'textgroup' => 'default', + 'source' => 'This field is locked and cannot be removed.', + 'version' => 'none', +)) +->values(array( + 'lid' => '137', + 'location' => 'includes/content.admin.inc:922', + 'textgroup' => 'default', + 'source' => 'Removed field %field from %type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '138', + 'location' => 'includes/content.admin.inc:927', + 'textgroup' => 'default', + 'source' => 'There was a problem deleting %field from %type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '139', + 'location' => 'includes/content.admin.inc:946', + 'textgroup' => 'default', + 'source' => 'The field %field is locked and cannot be edited.', + 'version' => 'none', +)) +->values(array( + 'lid' => '140', + 'location' => 'includes/content.admin.inc:980', + 'textgroup' => 'default', + 'source' => '%type basic information', + 'version' => 'none', +)) +->values(array( + 'lid' => '141', + 'location' => 'includes/content.admin.inc:1010;1189', + 'textgroup' => 'default', + 'source' => 'Change basic information', + 'version' => 'none', +)) +->values(array( + 'lid' => '142', + 'location' => 'includes/content.admin.inc:1016', + 'textgroup' => 'default', + 'source' => '%type settings', + 'version' => 'none', +)) +->values(array( + 'lid' => '143', + 'location' => 'includes/content.admin.inc:1017', + 'textgroup' => 'default', + 'source' => 'These settings apply only to the %field field as it appears in the %type content type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '144', + 'location' => 'includes/content.admin.inc:1031, modules/fieldgroup/fieldgroup.module:145', + 'textgroup' => 'default', + 'source' => 'Help text', + 'version' => 'none', +)) +->values(array( + 'lid' => '145', + 'location' => 'includes/content.admin.inc:1034', + 'textgroup' => 'default', + 'source' => 'Instructions to present to the user below this field on the editing form.
                          Allowed HTML tags: @tags', + 'version' => 'none', +)) +->values(array( + 'lid' => '146', + 'location' => 'includes/content.admin.inc:1060', + 'textgroup' => 'default', + 'source' => 'Default value', + 'version' => 'none', +)) +->values(array( + 'lid' => '147', + 'location' => 'includes/content.admin.inc:1081, modules/number/number.module:123, modules/text/text.module:86', + 'textgroup' => 'default', + 'source' => 'PHP code', + 'version' => 'none', +)) +->values(array( + 'lid' => '148', + 'location' => 'includes/content.admin.inc:1090;1245, includes/content.rules.inc:93', + 'textgroup' => 'default', + 'source' => "'@column' => value for @column", + 'version' => 'none', +)) +->values(array( + 'lid' => '149', + 'location' => 'includes/content.admin.inc:1092;1247, includes/content.rules.inc:95', + 'textgroup' => 'default', + 'source' => "return array(\n 0 => array(@columns),\n // You'll usually want to stop here. Provide more values\n // if you want your 'default value' to be multi-valued:\n 1 => array(@columns),\n 2 => ...\n);", + 'version' => 'none', +)) +->values(array( + 'lid' => '150', + 'location' => 'includes/content.admin.inc:1096;1109, includes/content.rules.inc:99, modules/number/number.module:130;139, modules/text/text.module:93;102', + 'textgroup' => 'default', + 'source' => 'Code', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '151', + 'location' => 'includes/content.admin.inc:1100', + 'textgroup' => 'default', + 'source' => 'Advanced usage only: PHP code that returns a default value. Should not include <?php ?> delimiters. If this field is filled out, the value returned by this code will override any value specified above. Expected format:
                          !sample
                          To figure out the expected format, you can use the devel load tab provided by devel module on a %type content page.', + 'version' => 'none', +)) +->values(array( + 'lid' => '152', + 'location' => 'includes/content.admin.inc:1110, modules/number/number.module:140, modules/text/text.module:103', + 'textgroup' => 'default', + 'source' => '<none>', + 'version' => 'none', +)) +->values(array( + 'lid' => '153', + 'location' => 'includes/content.admin.inc:1111, modules/number/number.module:141, modules/text/text.module:104', + 'textgroup' => 'default', + 'source' => "You're not allowed to input PHP code.", + 'version' => 'none', +)) +->values(array( + 'lid' => '154', + 'location' => 'includes/content.admin.inc:1111', + 'textgroup' => 'default', + 'source' => 'This PHP code was set by an administrator and will override any value specified above.', + 'version' => 'none', +)) +->values(array( + 'lid' => '155', + 'location' => 'includes/content.admin.inc:1118', + 'textgroup' => 'default', + 'source' => 'Global settings', + 'version' => 'none', +)) +->values(array( + 'lid' => '156', + 'location' => 'includes/content.admin.inc:1119', + 'textgroup' => 'default', + 'source' => 'These settings apply to the %field field in every content type in which it appears.', + 'version' => 'none', +)) +->values(array( + 'lid' => '157', + 'location' => 'includes/content.admin.inc:1123', + 'textgroup' => 'default', + 'source' => 'Required', + 'version' => 'none', +)) +->values(array( + 'lid' => '158', + 'location' => 'includes/content.admin.inc:1126', + 'textgroup' => 'default', + 'source' => 'Maximum number of values users can enter for this field.', + 'version' => 'none', +)) +->values(array( + 'lid' => '159', + 'location' => 'includes/content.admin.inc:1128', + 'textgroup' => 'default', + 'source' => "'Unlimited' will provide an 'Add more' button so the users can add as many values as they like.", + 'version' => 'none', +)) +->values(array( + 'lid' => '160', + 'location' => 'includes/content.admin.inc:1130', + 'textgroup' => 'default', + 'source' => 'Warning! Changing this setting after data has been created could result in the loss of data!', + 'version' => 'none', +)) +->values(array( + 'lid' => '161', + 'location' => 'includes/content.admin.inc:1133', + 'textgroup' => 'default', + 'source' => 'Number of values', + 'version' => 'none', +)) +->values(array( + 'lid' => '162', + 'location' => 'includes/content.admin.inc:1134, modules/content_multigroup/content_multigroup.module:73', + 'textgroup' => 'default', + 'source' => 'Unlimited', + 'version' => 'none', +)) +->values(array( + 'lid' => '163', + 'location' => 'includes/content.admin.inc:1151, modules/content_copy/content_copy.module:251', + 'textgroup' => 'default', + 'source' => 'Save field settings', + 'version' => 'none', +)) +->values(array( + 'lid' => '164', + 'location' => 'includes/content.admin.inc:1288', + 'textgroup' => 'default', + 'source' => "The PHP code for 'default value' returned @value, which is invalid.", + 'version' => 'none', +)) +->values(array( + 'lid' => '165', + 'location' => 'includes/content.admin.inc:1292', + 'textgroup' => 'default', + 'source' => 'The default value is invalid.', + 'version' => 'none', +)) +->values(array( + 'lid' => '166', + 'location' => 'includes/content.admin.inc:1316', + 'textgroup' => 'default', + 'source' => 'Added field %label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '167', + 'location' => 'includes/content.admin.inc:1320', + 'textgroup' => 'default', + 'source' => 'Saved field %label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '168', + 'location' => 'includes/content.admin.inc:1678', + 'textgroup' => 'default', + 'source' => 'Processing', + 'version' => 'none', +)) +->values(array( + 'lid' => '169', + 'location' => 'includes/content.admin.inc:1679', + 'textgroup' => 'default', + 'source' => 'The update has encountered an error.', + 'version' => 'none', +)) +->values(array( + 'lid' => '170', + 'location' => 'includes/content.admin.inc:1693', + 'textgroup' => 'default', + 'source' => 'The database has been altered and data has been migrated or deleted.', + 'version' => 'none', +)) +->values(array( + 'lid' => '171', + 'location' => 'includes/content.admin.inc:1696', + 'textgroup' => 'default', + 'source' => 'An error occurred and database alteration did not complete.', + 'version' => 'none', +)) +->values(array( + 'lid' => '172', + 'location' => 'includes/content.admin.inc:1799', + 'textgroup' => 'default', + 'source' => 'Processing %title', + 'version' => 'none', +)) +->values(array( + 'lid' => '173', + 'location' => 'includes/content.admin.inc:1865', + 'textgroup' => 'default', + 'source' => '%name must be an integer.', + 'version' => 'none', +)) +->values(array( + 'lid' => '174', + 'location' => 'includes/content.admin.inc:1875', + 'textgroup' => 'default', + 'source' => '%name must be a positive integer.', + 'version' => 'none', +)) +->values(array( + 'lid' => '175', + 'location' => 'includes/content.admin.inc:1885', + 'textgroup' => 'default', + 'source' => '%name must be a number.', + 'version' => 'none', +)) +->values(array( + 'lid' => '176', + 'location' => 'includes/content.admin.inc:1697', + 'textgroup' => 'default', + 'source' => '1 item successfully processed:', + 'version' => 'none', +)) +->values(array( + 'lid' => '177', + 'location' => 'includes/content.admin.inc:1697', + 'textgroup' => 'default', + 'source' => '@count items successfully processed:', + 'version' => 'none', +)) +->values(array( + 'lid' => '178', + 'location' => 'includes/content.crud.inc:589', + 'textgroup' => 'default', + 'source' => 'Content fields table %old_name has been renamed to %new_name and field instances have been updated.', + 'version' => 'none', +)) +->values(array( + 'lid' => '179', + 'location' => 'includes/content.crud.inc:633', + 'textgroup' => 'default', + 'source' => 'The content fields table %name has been deleted.', + 'version' => 'none', +)) +->values(array( + 'lid' => '180', + 'location' => 'includes/content.node_form.inc:223', + 'textgroup' => 'default', + 'source' => 'Add another item', + 'version' => 'none', +)) +->values(array( + 'lid' => '181', + 'location' => 'includes/panels/content_types/content_field.inc:14', + 'textgroup' => 'default', + 'source' => 'Content field', + 'version' => 'none', +)) +->values(array( + 'lid' => '182', + 'location' => 'includes/content.panels.inc:38', + 'textgroup' => 'default', + 'source' => 'A content field from the referenced node.', + 'version' => 'none', +)) +->values(array( + 'lid' => '183', + 'location' => 'includes/panels/content_types/content_field.inc:45, modules/fieldgroup/fieldgroup.panels.inc:31, modules/fieldgroup/panels/content_types/content_fieldgroup.inc:43', + 'textgroup' => 'default', + 'source' => 'Node', + 'version' => 'none', +)) +->values(array( + 'lid' => '184', + 'location' => 'includes/content.panels.inc:40, modules/fieldgroup/fieldgroup.panels.inc:32', + 'textgroup' => 'default', + 'source' => 'Node context', + 'version' => 'none', +)) +->values(array( + 'lid' => '185', + 'location' => 'includes/panels/content_types/content_field.inc:100', + 'textgroup' => 'default', + 'source' => 'Block title', + 'version' => 'none', +)) +->values(array( + 'lid' => '186', + 'location' => 'includes/panels/content_types/content_field.inc:103', + 'textgroup' => 'default', + 'source' => 'Hidden', + 'version' => 'none', +)) +->values(array( + 'lid' => '187', + 'location' => 'includes/panels/content_types/content_field.inc:105', + 'textgroup' => 'default', + 'source' => 'Configure how the label is going to be displayed.', + 'version' => 'none', +)) +->values(array( + 'lid' => '188', + 'location' => 'includes/content.panels.inc:73', + 'textgroup' => 'default', + 'source' => 'Field / Formatter', + 'version' => 'none', +)) +->values(array( + 'lid' => '189', + 'location' => 'includes/content.panels.inc:76', + 'textgroup' => 'default', + 'source' => 'Select a field and formatter.', + 'version' => 'none', +)) +->values(array( + 'lid' => '190', + 'location' => 'includes/content.panels.inc:92', + 'textgroup' => 'default', + 'source' => '"@s" field @name', + 'version' => 'none', +)) +->values(array( + 'lid' => '191', + 'location' => 'includes/content.rules.inc:15', + 'textgroup' => 'default', + 'source' => 'Populate a field', + 'version' => 'none', +)) +->values(array( + 'lid' => '192', + 'location' => 'includes/content.rules.inc:23;224', + 'textgroup' => 'default', + 'source' => 'You should make sure that the used field exists in the given content type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '193', + 'location' => 'includes/content.rules.inc:53;276, modules/nodereference/nodereference.rules.inc:45, modules/userreference/userreference.rules.inc:47', + 'textgroup' => 'default', + 'source' => 'Field', + 'version' => 'none', +)) +->values(array( + 'lid' => '194', + 'location' => 'includes/content.rules.inc:56', + 'textgroup' => 'default', + 'source' => 'Select the machine-name of the field.', + 'version' => 'none', +)) +->values(array( + 'lid' => '195', + 'location' => 'includes/content.rules.inc:84', + 'textgroup' => 'default', + 'source' => 'Advanced: Specify the fields value with PHP code', + 'version' => 'none', +)) +->values(array( + 'lid' => '196', + 'location' => 'not literally, includes/content.rules.inc:102', + 'textgroup' => 'default', + 'source' => "Advanced usage only: PHP code that returns the value to set. Should not include <?php ?> delimiters. If this field is filled out, the value returned by this code will override any value specified above. Expected format:
                          !sample
                          Using devel.module's 'devel load' tab on a content page might help you figure out the expected format.", + 'version' => 'none', +)) +->values(array( + 'lid' => '197', + 'location' => 'includes/content.rules.inc:130', + 'textgroup' => 'default', + 'source' => 'You have to return the default value in the expected format.', + 'version' => 'none', +)) +->values(array( + 'lid' => '198', + 'location' => 'includes/content.rules.inc:193', + 'textgroup' => 'default', + 'source' => "Populate @node's field '@field'", + 'version' => 'none', +)) +->values(array( + 'lid' => '199', + 'location' => 'includes/content.rules.inc:210', + 'textgroup' => 'default', + 'source' => 'Field has value', + 'version' => 'none', +)) +->values(array( + 'lid' => '200', + 'location' => 'includes/content.rules.inc:215', + 'textgroup' => 'default', + 'source' => 'You should make sure that the used field exists in the given content type. The condition returns TRUE, if the selected field has the given value.', + 'version' => 'none', +)) +->values(array( + 'lid' => '201', + 'location' => 'includes/content.rules.inc:219', + 'textgroup' => 'default', + 'source' => 'Field has changed', + 'version' => 'none', +)) +->values(array( + 'lid' => '202', + 'location' => 'includes/content.rules.inc:221', + 'textgroup' => 'default', + 'source' => 'Content containing changes', + 'version' => 'none', +)) +->values(array( + 'lid' => '203', + 'location' => 'includes/content.rules.inc:222', + 'textgroup' => 'default', + 'source' => 'Content not containing changes', + 'version' => 'none', +)) +->values(array( + 'lid' => '204', + 'location' => 'includes/content.rules.inc:259', + 'textgroup' => 'default', + 'source' => "@node's field '@field' has value", + 'version' => 'none', +)) +->values(array( + 'lid' => '205', + 'location' => 'not literally, includes/content.rules.inc:279, fuzzy', + 'textgroup' => 'default', + 'source' => 'Select the machine-name of the field to look at.', + 'version' => 'none', +)) +->values(array( + 'lid' => '206', + 'location' => '@node?, includes/content.rules.inc:285', + 'textgroup' => 'default', + 'source' => "@node's field '@field' has been changed", + 'version' => 'none', +)) +->values(array( + 'lid' => '207', + 'location' => 'includes/content.token.inc:12;15', + 'textgroup' => 'default', + 'source' => 'Token', + 'version' => 'none', +)) +->values(array( + 'lid' => '208', + 'location' => 'includes/content.token.inc:81', + 'textgroup' => 'default', + 'source' => 'Referenced node ID', + 'version' => 'none', +)) +->values(array( + 'lid' => '209', + 'location' => 'includes/content.token.inc:82', + 'textgroup' => 'default', + 'source' => 'Referenced node title', + 'version' => 'none', +)) +->values(array( + 'lid' => '210', 'location' => 'includes/content.token.inc:83', 'textgroup' => 'default', - 'source' => 'Referenced node unfiltered title. WARNING - raw user input.', + 'source' => 'Referenced node unfiltered title. WARNING - raw user input.', + 'version' => 'none', +)) +->values(array( + 'lid' => '211', + 'location' => 'includes/content.token.inc:84', + 'textgroup' => 'default', + 'source' => 'Formatted html link to the referenced node.', + 'version' => 'none', +)) +->values(array( + 'lid' => '212', + 'location' => 'includes/content.token.inc:85', + 'textgroup' => 'default', + 'source' => 'Relative path alias to the referenced node.', + 'version' => 'none', +)) +->values(array( + 'lid' => '213', + 'location' => 'includes/content.token.inc:86', + 'textgroup' => 'default', + 'source' => 'Absolute path alias to the referenced node.', + 'version' => 'none', +)) +->values(array( + 'lid' => '214', + 'location' => 'includes/content.token.inc:114', + 'textgroup' => 'default', + 'source' => 'Raw number value', + 'version' => 'none', +)) +->values(array( + 'lid' => '215', + 'location' => 'includes/content.token.inc:115', + 'textgroup' => 'default', + 'source' => 'Formatted number value', + 'version' => 'none', +)) +->values(array( + 'lid' => '216', + 'location' => 'includes/content.token.inc:138', + 'textgroup' => 'default', + 'source' => 'Raw, unfiltered text', + 'version' => 'none', +)) +->values(array( + 'lid' => '217', + 'location' => 'includes/content.token.inc:139', + 'textgroup' => 'default', + 'source' => 'Formatted and filtered text', + 'version' => 'none', +)) +->values(array( + 'lid' => '218', + 'location' => 'includes/content.token.inc:161', + 'textgroup' => 'default', + 'source' => 'Referenced user ID', + 'version' => 'none', +)) +->values(array( + 'lid' => '219', + 'location' => 'includes/content.token.inc:162', + 'textgroup' => 'default', + 'source' => 'Referenced user name', + 'version' => 'none', +)) +->values(array( + 'lid' => '220', + 'location' => 'includes/content.token.inc:163', + 'textgroup' => 'default', + 'source' => 'Formatted HTML link to referenced user', + 'version' => 'none', +)) +->values(array( + 'lid' => '221', + 'location' => 'includes/content.token.inc:164', + 'textgroup' => 'default', + 'source' => 'Relative path alias to the referenced user.', + 'version' => 'none', +)) +->values(array( + 'lid' => '222', + 'location' => 'includes/content.token.inc:165', + 'textgroup' => 'default', + 'source' => 'Absolute path alias to the referenced user.', + 'version' => 'none', +)) +->values(array( + 'lid' => '223', + 'location' => 'includes/views/content.views.inc:245;261', + 'textgroup' => 'default', + 'source' => '@label (!name)', + 'version' => 'none', +)) +->values(array( + 'lid' => '224', + 'location' => 'includes/views/content.views.inc:249', + 'textgroup' => 'default', + 'source' => '@label (!name) - !column', + 'version' => 'none', +)) +->values(array( + 'lid' => '225', + 'location' => 'includes/views/content.views.inc:250', + 'textgroup' => 'default', + 'source' => '@label-truncated - !column', + 'version' => 'none', +)) +->values(array( + 'lid' => '226', + 'location' => 'includes/views/content.views.inc:257', + 'textgroup' => 'default', + 'source' => 'Appears in: @types', + 'version' => 'none', +)) +->values(array( + 'lid' => '227', + 'location' => 'includes/views/handlers/content_handler_field.inc:56', + 'textgroup' => 'default', + 'source' => 'None', + 'version' => 'none', +)) +->values(array( + 'lid' => '228', + 'location' => 'includes/views/handlers/content_handler_field.inc:57', + 'textgroup' => 'default', + 'source' => 'Widget label (@label)', + 'version' => 'none', +)) +->values(array( + 'lid' => '229', + 'location' => 'includes/views/handlers/content_handler_field.inc:58', + 'textgroup' => 'default', + 'source' => 'Custom', + 'version' => 'none', +)) +->values(array( + 'lid' => '230', + 'location' => 'includes/views/handlers/content_handler_field.inc:64', + 'textgroup' => 'default', + 'source' => 'Custom label', + 'version' => 'none', +)) +->values(array( + 'lid' => '231', + 'location' => 'includes/views/handlers/content_handler_field.inc:80', + 'textgroup' => 'default', + 'source' => 'Format', + 'version' => 'none', +)) +->values(array( + 'lid' => '232', + 'location' => 'includes/views/handlers/content_handler_field_multiple.inc:56', + 'textgroup' => 'default', + 'source' => 'Group multiple values', + 'version' => 'none', +)) +->values(array( + 'lid' => '233', + 'location' => 'includes/views/handlers/content_handler_field_multiple.inc:61', + 'textgroup' => 'default', + 'source' => 'If unchecked, each item in the field will create a new row, which may appear to cause duplicates. This setting is not compatible with click-sorting in table displays.', + 'version' => 'none', +)) +->values(array( + 'lid' => '234', + 'location' => 'includes/views/handlers/content_handler_field_multiple.inc:63', + 'textgroup' => 'default', + 'source' => 'Show @count value(s)', + 'version' => 'none', +)) +->values(array( + 'lid' => '235', + 'location' => 'includes/views/handlers/content_handler_field_multiple.inc:74', + 'textgroup' => 'default', + 'source' => 'starting from @count', + 'version' => 'none', +)) +->values(array( + 'lid' => '236', + 'location' => 'includes/views/handlers/content_handler_field_multiple.inc:85', + 'textgroup' => 'default', + 'source' => 'Reversed (start from last values)', + 'version' => 'none', +)) +->values(array( + 'lid' => '237', + 'location' => 'includes/views/handlers/content_handler_relationship.inc:40, includes/views/handlers/content_handler_sort.inc:41', + 'textgroup' => 'default', + 'source' => 'All', + 'version' => 'none', +)) +->values(array( + 'lid' => '238', + 'location' => 'includes/views/handlers/content_handler_relationship.inc:48, includes/views/handlers/content_handler_sort.inc:49', + 'textgroup' => 'default', + 'source' => 'Delta', + 'version' => 'none', +)) +->values(array( + 'lid' => '239', + 'location' => 'includes/views/handlers/content_handler_relationship.inc:49', + 'textgroup' => 'default', + 'source' => 'The delta allows you to select which item in a multiple value field to key the relationship off of. Select "1" to use the first item, "2" for the second item, and so on. If you select "All", each item in the field will create a new row, which may appear to cause duplicates.', + 'version' => 'none', +)) +->values(array( + 'lid' => '240', + 'location' => 'includes/views/handlers/content_handler_sort.inc:50', + 'textgroup' => 'default', + 'source' => 'The delta allows you to select which item in a multiple value field will be used for sorting. Select "1" to use the first item, "2" for the second item, and so on. If you select "All", each item in the field will create a new row, which may appear to cause duplicates.', + 'version' => 'none', +)) +->values(array( + 'lid' => '241', + 'location' => 'modules/content_copy/content_copy_export_form.tpl.php:9, modules/content_copy/content_copy.module:191;38', + 'textgroup' => 'default', + 'source' => 'Export', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '242', + 'location' => 'modules/content_copy/content_copy.module:97', + 'textgroup' => 'default', + 'source' => 'This form will process a content type and one or more fields from that type and export the settings. The export created by this process can be copied and pasted as an import into the current or any other database. The import will add the fields to into an existing content type or create a new content type that includes the selected fields.', + 'version' => 'none', +)) +->values(array( + 'lid' => '243', + 'location' => 'modules/content_copy/content_copy.module:103', + 'textgroup' => 'default', + 'source' => 'Types', + 'version' => 'none', +)) +->values(array( + 'lid' => '244', + 'location' => 'modules/content_copy/content_copy.module:107', + 'textgroup' => 'default', + 'source' => 'Select the content type to export.', + 'version' => 'none', +)) +->values(array( + 'lid' => '245', + 'location' => 'modules/content_copy/content_copy.module:175', + 'textgroup' => 'default', + 'source' => 'Export data', + 'version' => 'none', +)) +->values(array( + 'lid' => '246', + 'location' => 'modules/content_copy/content_copy.module:180', + 'textgroup' => 'default', + 'source' => 'Copy the export text and paste it into another content type using the import function.', + 'version' => 'none', +)) +->values(array( + 'lid' => '247', + 'location' => 'content_admin.inc:42', + 'textgroup' => 'default', + 'source' => 'Content types', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '248', + 'location' => 'modules/content_copy/content_copy.module:308', + 'textgroup' => 'default', + 'source' => 'Content type', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '249', + 'location' => 'modules/content_copy/content_copy.module:309', + 'textgroup' => 'default', + 'source' => 'Select the content type to import these fields into.
                          Select <Create> to create a new content type to contain the fields.', + 'version' => 'none', +)) +->values(array( + 'lid' => '250', + 'location' => 'modules/content_copy/content_copy.module:314', + 'textgroup' => 'default', + 'source' => 'Import data', + 'version' => 'none', +)) +->values(array( + 'lid' => '251', + 'location' => 'modules/content_copy/content_copy.module:316', + 'textgroup' => 'default', + 'source' => 'Paste the text created by a content export into this field.', + 'version' => 'none', +)) +->values(array( + 'lid' => '252', + 'location' => 'modules/content_copy/content_copy.module:320;46', + 'textgroup' => 'default', + 'source' => 'Import', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '253', + 'location' => 'modules/content_copy/content_copy.module:328', + 'textgroup' => 'default', + 'source' => 'A file has been pre-loaded for import.', + 'version' => 'none', +)) +->values(array( + 'lid' => '254', + 'location' => 'modules/content_copy/content_copy.module:354', + 'textgroup' => 'default', + 'source' => 'The import data is not valid import text.', + 'version' => 'none', +)) +->values(array( + 'lid' => '255', + 'location' => 'modules/content_copy/content_copy.module:403', + 'textgroup' => 'default', + 'source' => 'The following modules must be enabled for this import to work: %modules.', + 'version' => 'none', +)) +->values(array( + 'lid' => '256', + 'location' => 'modules/content_copy/content_copy.module:411', + 'textgroup' => 'default', + 'source' => 'The content type %type already exists in this database.', + 'version' => 'none', +)) +->values(array( + 'lid' => '257', + 'location' => 'modules/content_copy/content_copy.module:418', + 'textgroup' => 'default', + 'source' => 'Exiting. No import performed.', + 'version' => 'none', +)) +->values(array( + 'lid' => '258', + 'location' => 'modules/content_copy/content_copy.module:442', + 'textgroup' => 'default', + 'source' => 'An error has occurred adding the content type %type.
                          Please check the errors displayed for more details.', + 'version' => 'none', +)) +->values(array( + 'lid' => '259', + 'location' => 'modules/content_copy/content_copy.module:467', + 'textgroup' => 'default', + 'source' => 'The imported field %field_label (%field_name) was not added to %type because that field already exists in %type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '260', + 'location' => 'modules/content_copy/content_copy.module:476', + 'textgroup' => 'default', + 'source' => 'The field %field_label (%field_name) was added to the content type %type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '261', + 'location' => 'modules/content_copy/content_copy.module:0', + 'textgroup' => 'default', + 'source' => 'content_copy', + 'version' => 'none', +)) +->values(array( + 'lid' => '262', + 'location' => 'modules/content_copy/content_copy.info:0', + 'textgroup' => 'default', + 'source' => 'Content Copy', + 'version' => 'none', +)) +->values(array( + 'lid' => '263', + 'location' => 'modules/content_copy/content_copy.info:0', + 'textgroup' => 'default', + 'source' => 'Enables ability to import/export field definitions.', + 'version' => 'none', +)) +->values(array( + 'lid' => '264', + 'location' => 'modules/content_multigroup/content_multigroup.module:12', + 'textgroup' => 'default', + 'source' => 'The fields in a Standard group are independent of each other and each can have either single or multiple values. The fields in a Multigroup are treated as a repeating collection of single value fields.', + 'version' => 'none', +)) +->values(array( + 'lid' => '265', + 'location' => 'modules/content_multigroup/content_multigroup.module:65;135', + 'textgroup' => 'default', + 'source' => 'Multigroup', + 'version' => 'none', +)) +->values(array( + 'lid' => '266', + 'location' => 'modules/content_multigroup/content_multigroup.module:134', + 'textgroup' => 'default', + 'source' => 'Standard', + 'version' => 'none', +)) +->values(array( + 'lid' => '267', + 'location' => 'includes/content.admin.inc:344, modules/content_multigroup/content_multigroup.module:126', + 'textgroup' => 'default', + 'source' => 'Type of group.', + 'version' => 'none', +)) +->values(array( + 'lid' => '268', + 'location' => 'modules/content_multigroup/content_multigroup.module:215', + 'textgroup' => 'default', + 'source' => 'The field %field has been updated to use %multiple values, to match the multiple value setting of the Multigroup %group.', + 'version' => 'none', +)) +->values(array( + 'lid' => '269', + 'location' => 'modules/content_multigroup/content_multigroup.module:248', + 'textgroup' => 'default', + 'source' => 'This change is not allowed. The field %field already has %multiple values in the database but the group %group only allows %group_max. Making this change would result in the loss of data.', + 'version' => 'none', +)) +->values(array( + 'lid' => '270', + 'location' => 'modules/content_multigroup/content_multigroup.module:272', + 'textgroup' => 'default', + 'source' => 'This change is not allowed. The field %field handles multiple values differently than the Content module. Making this change could result in the loss of data.', + 'version' => 'none', +)) +->values(array( + 'lid' => '271', + 'location' => 'modules/content_multigroup/content_multigroup.module:287', + 'textgroup' => 'default', + 'source' => 'You are moving the field %field into a Multigroup.', + 'version' => 'none', +)) +->values(array( + 'lid' => '272', + 'location' => 'modules/content_multigroup/content_multigroup.module:320', + 'textgroup' => 'default', + 'source' => 'This change is not allowed. The field %field already has data created and uses a widget that stores data differently in a Standard group than in a Multigroup. Making this change could result in the loss of data.', + 'version' => 'none', +)) +->values(array( + 'lid' => '273', + 'location' => 'modules/content_multigroup/content_multigroup.module:334', + 'textgroup' => 'default', + 'source' => 'You are moving the field %field out of a Multigroup.', + 'version' => 'none', +)) +->values(array( + 'lid' => '274', + 'location' => 'modules/content_multigroup/content_multigroup.module:369', + 'textgroup' => 'default', + 'source' => 'Simple', + 'version' => 'none', +)) +->values(array( + 'lid' => '275', + 'location' => 'modules/content_multigroup/content_multigroup.module:370', + 'textgroup' => 'default', + 'source' => 'Fieldset', + 'version' => 'none', +)) +->values(array( + 'lid' => '276', + 'location' => 'modules/content_multigroup/content_multigroup.module:371', + 'textgroup' => 'default', + 'source' => 'Horizontal line', + 'version' => 'none', +)) +->values(array( + 'lid' => '277', + 'location' => 'modules/content_multigroup/content_multigroup.module:372', + 'textgroup' => 'default', + 'source' => 'Table - Single column', + 'version' => 'none', +)) +->values(array( + 'lid' => '278', + 'location' => 'modules/content_multigroup/content_multigroup.module:373', + 'textgroup' => 'default', + 'source' => 'Table - Multiple columns', + 'version' => 'none', +)) +->values(array( + 'lid' => '279', + 'location' => 'modules/content_multigroup/content_multigroup.module:384', + 'textgroup' => 'default', + 'source' => '[Subgroup format]', + 'version' => 'none', +)) +->values(array( + 'lid' => '280', + 'location' => 'modules/content_multigroup/content_multigroup.module:461', + 'textgroup' => 'default', + 'source' => 'Multigroup settings', + 'version' => 'none', +)) +->values(array( + 'lid' => '281', + 'location' => 'modules/content_multigroup/content_multigroup.module:476', + 'textgroup' => 'default', + 'source' => 'Multiple columns', + 'version' => 'none', +)) +->values(array( + 'lid' => '282', + 'location' => 'modules/content_multigroup/content_multigroup.module:478', + 'textgroup' => 'default', + 'source' => 'Enable this option to render each field on a separate column on the node edit form.', + 'version' => 'none', +)) +->values(array( + 'lid' => '283', + 'location' => 'modules/content_multigroup/content_multigroup.module:485', + 'textgroup' => 'default', + 'source' => 'Enable this option to require a minimum of one collection of fields in this Multigroup.', + 'version' => 'none', +)) +->values(array( + 'lid' => '284', + 'location' => 'modules/content_multigroup/content_multigroup.module:488', + 'textgroup' => 'default', + 'source' => 'Number of times to repeat the collection of Multigroup fields.', + 'version' => 'none', +)) +->values(array( + 'lid' => '285', + 'location' => 'modules/content_multigroup/content_multigroup.module:489', + 'textgroup' => 'default', + 'source' => "'Unlimited' will provide an 'Add more' button so the users can add items as many times as they like.", + 'version' => 'none', +)) +->values(array( + 'lid' => '286', + 'location' => 'modules/content_multigroup/content_multigroup.module:490', + 'textgroup' => 'default', + 'source' => 'All fields in this group will automatically be set to allow this number of values.', + 'version' => 'none', +)) +->values(array( + 'lid' => '287', + 'location' => 'modules/content_multigroup/content_multigroup.module:495', + 'textgroup' => 'default', + 'source' => 'Number of repeats', + 'version' => 'none', +)) +->values(array( + 'lid' => '288', + 'location' => 'modules/content_multigroup/content_multigroup.module:503', + 'textgroup' => 'default', + 'source' => 'Labels', + 'version' => 'none', +)) +->values(array( + 'lid' => '289', + 'location' => 'modules/content_multigroup/content_multigroup.module:504', + 'textgroup' => 'default', + 'source' => "Labels for each subgroup of fields. Labels can be hidden or shown in various contexts using the 'Display fields' screen.", + 'version' => 'none', +)) +->values(array( + 'lid' => '290', + 'location' => 'modules/content_multigroup/content_multigroup.module:512', + 'textgroup' => 'default', + 'source' => 'Subgroup %number label', + 'version' => 'none', +)) +->values(array( + 'lid' => '291', + 'location' => 'modules/content_multigroup/content_multigroup.module:539', + 'textgroup' => 'default', + 'source' => 'The field %field in this group already has %multiple values in the database. To prevent the loss of data you cannot set the number of Multigroup values to less than this.', + 'version' => 'none', +)) +->values(array( + 'lid' => '292', + 'location' => 'modules/content_multigroup/content_multigroup.module:932', + 'textgroup' => 'default', + 'source' => '!name field is required in group @group.', + 'version' => 'none', +)) +->values(array( + 'lid' => '293', + 'location' => 'modules/content_multigroup/content_multigroup.module:946', + 'textgroup' => 'default', + 'source' => 'Group @name requires one collection of fields minimum.', + 'version' => 'none', +)) +->values(array( + 'lid' => '294', + 'location' => 'modules/content_multigroup/content_multigroup.module:1145', + 'textgroup' => 'default', + 'source' => 'Add more values', + 'version' => 'none', +)) +->values(array( + 'lid' => '295', + 'location' => 'modules/content_multigroup/content_multigroup.module:0', + 'textgroup' => 'default', + 'source' => 'content_multigroup', + 'version' => 'none', +)) +->values(array( + 'lid' => '296', + 'location' => 'modules/content_multigroup/content_multigroup.info:0', + 'textgroup' => 'default', + 'source' => 'Content Multigroup', + 'version' => 'none', +)) +->values(array( + 'lid' => '297', + 'location' => 'modules/content_multigroup/content_multigroup.info:0', + 'textgroup' => 'default', + 'source' => 'Combine multiple CCK fields into repeating field collections that work in unison.', + 'version' => 'none', +)) +->values(array( + 'lid' => '298', + 'location' => 'modules/content_permissions/content_permissions.module:10', + 'textgroup' => 'default', + 'source' => 'edit', + 'version' => 'none', +)) +->values(array( + 'lid' => '299', + 'location' => 'modules/content_permissions/content_permissions.module:10;11', + 'textgroup' => 'default', + 'source' => 'field_name', + 'version' => 'none', +)) +->values(array( + 'lid' => '300', + 'location' => 'modules/content_permissions/content_permissions.module:11', + 'textgroup' => 'default', + 'source' => 'view', + 'version' => 'none', +)) +->values(array( + 'lid' => '301', + 'location' => 'modules/content_permissions/content_permissions.module:0', + 'textgroup' => 'default', + 'source' => 'content_permissions', + 'version' => 'none', +)) +->values(array( + 'lid' => '302', + 'location' => 'modules/content_permissions/content_permissions.install:9', + 'textgroup' => 'default', + 'source' => 'Please configure your field permissions immediately. All fields are inaccessible by default.', + 'version' => 'none', +)) +->values(array( + 'lid' => '303', + 'location' => 'modules/content_permissions/content_permissions.info:0', + 'textgroup' => 'default', + 'source' => 'Content Permissions', + 'version' => 'none', +)) +->values(array( + 'lid' => '304', + 'location' => 'modules/content_permissions/content_permissions.info:0', + 'textgroup' => 'default', + 'source' => 'Set field-level permissions for CCK fields.', + 'version' => 'none', +)) +->values(array( + 'lid' => '305', + 'location' => 'modules/fieldgroup/fieldgroup.panels.inc:10;27, modules/fieldgroup/panels/content_types/content_fieldgroup.inc:14', + 'textgroup' => 'default', + 'source' => 'Content fieldgroup', + 'version' => 'none', +)) +->values(array( + 'lid' => '306', + 'location' => 'modules/fieldgroup/fieldgroup.panels.inc:30', + 'textgroup' => 'default', + 'source' => 'All fields from a fieldgroup on the referenced node.', + 'version' => 'none', +)) +->values(array( + 'lid' => '307', + 'location' => 'modules/fieldgroup/fieldgroup.panels.inc:91', + 'textgroup' => 'default', + 'source' => '@group_label (@group_type_name)', + 'version' => 'none', +)) +->values(array( + 'lid' => '308', + 'location' => 'modules/fieldgroup/fieldgroup.panels.inc:102, modules/fieldgroup/fieldgroup.info:0', + 'textgroup' => 'default', + 'source' => 'Fieldgroup', + 'version' => 'none', +)) +->values(array( + 'lid' => '309', + 'location' => 'modules/fieldgroup/fieldgroup.panels.inc:112, modules/fieldgroup/panels/content_types/content_fieldgroup.inc:102', + 'textgroup' => 'default', + 'source' => 'Text to display if group has no data. Note that title will not display unless overridden.', + 'version' => 'none', +)) +->values(array( + 'lid' => '310', + 'location' => 'modules/fieldgroup/fieldgroup.panels.inc:128', + 'textgroup' => 'default', + 'source' => '"@s" fieldgroup @name', + 'version' => 'none', +)) +->values(array( + 'lid' => '311', + 'location' => 'modules/fieldgroup/fieldgroup.module:124', + 'textgroup' => 'default', + 'source' => 'Form settings', + 'version' => 'none', +)) +->values(array( + 'lid' => '312', + 'location' => 'modules/fieldgroup/fieldgroup.module:125', + 'textgroup' => 'default', + 'source' => 'These settings apply to the group in the node editing form.', + 'version' => 'none', +)) +->values(array( + 'lid' => '313', + 'location' => 'modules/fieldgroup/fieldgroup.module:129', + 'textgroup' => 'default', + 'source' => 'Style', + 'version' => 'none', +)) +->values(array( + 'lid' => '314', + 'location' => 'modules/fieldgroup/fieldgroup.module:132', + 'textgroup' => 'default', + 'source' => 'always open', + 'version' => 'none', +)) +->values(array( + 'lid' => '315', + 'location' => 'modules/fieldgroup/fieldgroup.module:133', + 'textgroup' => 'default', + 'source' => 'collapsible', + 'version' => 'none', +)) +->values(array( + 'lid' => '316', + 'location' => 'modules/fieldgroup/fieldgroup.module:134', + 'textgroup' => 'default', + 'source' => 'collapsed', + 'version' => 'none', +)) +->values(array( + 'lid' => '317', + 'location' => 'modules/fieldgroup/fieldgroup.module:142', + 'textgroup' => 'default', + 'source' => 'Instructions to present to the user on the editing form.', + 'version' => 'none', +)) +->values(array( + 'lid' => '318', + 'location' => 'modules/fieldgroup/fieldgroup.module:147', + 'textgroup' => 'default', + 'source' => 'Display settings', + 'version' => 'none', +)) +->values(array( + 'lid' => '319', + 'location' => 'modules/fieldgroup/fieldgroup.module:148', + 'textgroup' => 'default', + 'source' => 'These settings apply to the group on node display.', + 'version' => 'none', +)) +->values(array( + 'lid' => '320', + 'location' => 'modules/fieldgroup/fieldgroup.module:155', + 'textgroup' => 'default', + 'source' => 'A description of the group.', + 'version' => 'none', +)) +->values(array( + 'lid' => '321', + 'location' => 'modules/fieldgroup/fieldgroup.module:200', + 'textgroup' => 'default', + 'source' => 'Are you sure you want to remove the group %label?', + 'version' => 'none', +)) +->values(array( + 'lid' => '322', + 'location' => 'modules/fieldgroup/fieldgroup.module:202', + 'textgroup' => 'default', + 'source' => 'This action cannot be undone.', + 'version' => 'none', +)) +->values(array( + 'lid' => '323', + 'location' => 'modules/fieldgroup/fieldgroup.module:211', + 'textgroup' => 'default', + 'source' => 'The group %group_name has been removed.', + 'version' => 'none', +)) +->values(array( + 'lid' => '324', + 'location' => 'modules/content_multigroup/content_multigroup.module:356, modules/fieldgroup/fieldgroup.module:266', + 'textgroup' => 'default', + 'source' => 'none', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '325', + 'location' => 'modules/fieldgroup/fieldgroup.module:353', + 'textgroup' => 'default', + 'source' => 'You need to provide a label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '326', + 'location' => 'modules/fieldgroup/fieldgroup.module:358', + 'textgroup' => 'default', + 'source' => 'You need to provide a group name.', + 'version' => 'none', +)) +->values(array( + 'lid' => '327', + 'location' => 'modules/fieldgroup/fieldgroup.module:372', + 'textgroup' => 'default', + 'source' => 'The group name %group_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', + 'version' => 'none', +)) +->values(array( + 'lid' => '328', + 'location' => 'modules/fieldgroup/fieldgroup.module:375', + 'textgroup' => 'default', + 'source' => "The group name %group_name is too long. The name is limited to 32 characters, including the 'group_' prefix.", + 'version' => 'none', +)) +->values(array( + 'lid' => '329', + 'location' => 'modules/fieldgroup/fieldgroup.module:381', + 'textgroup' => 'default', + 'source' => 'The group name %group_name already exists.', + 'version' => 'none', +)) +->values(array( + 'lid' => '330', + 'location' => 'modules/fieldgroup/fieldgroup.module:400;403', + 'textgroup' => 'default', + 'source' => 'Add new group:', + 'version' => 'none', +)) +->values(array( + 'lid' => '331', + 'location' => 'modules/fieldgroup/fieldgroup.module:418', + 'textgroup' => 'default', + 'source' => 'Add new group: you need to provide a label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '332', + 'location' => 'modules/fieldgroup/fieldgroup.module:419', + 'textgroup' => 'default', + 'source' => 'Add new group: you need to provide a group name.', + 'version' => 'none', +)) +->values(array( + 'lid' => '333', + 'location' => 'modules/fieldgroup/fieldgroup.module:648', + 'textgroup' => 'default', + 'source' => 'Standard group', + 'version' => 'none', +)) +->values(array( + 'lid' => '334', + 'location' => 'modules/fieldgroup/fieldgroup.module:39;46', + 'textgroup' => 'default', + 'source' => 'Edit group', + 'version' => 'none', +)) +->values(array( + 'lid' => '335', + 'location' => 'modules/fieldgroup/fieldgroup.module:0', + 'textgroup' => 'default', + 'source' => 'fieldgroup', + 'version' => 'none', +)) +->values(array( + 'lid' => '336', + 'location' => 'modules/fieldgroup/fieldgroup.info:0', + 'textgroup' => 'default', + 'source' => 'Create display groups for CCK fields.', + 'version' => 'none', +)) +->values(array( + 'lid' => '337', + 'location' => 'modules/nodereference/nodereference.rules.inc:15', + 'textgroup' => 'default', + 'source' => 'Load a referenced node', + 'version' => 'none', +)) +->values(array( + 'lid' => '338', + 'location' => 'modules/nodereference/nodereference.rules.inc:19', + 'textgroup' => 'default', + 'source' => 'Content containing the node reference field', + 'version' => 'none', +)) +->values(array( + 'lid' => '339', + 'location' => 'modules/nodereference/nodereference.rules.inc:25', + 'textgroup' => 'default', + 'source' => 'Referenced content', + 'version' => 'none', +)) +->values(array( + 'lid' => '340', + 'location' => 'modules/nodereference/nodereference.rules.inc:29', + 'textgroup' => 'default', + 'source' => 'Note that if the field has multiple values, only the first content node will be loaded.', + 'version' => 'none', +)) +->values(array( + 'lid' => '341', + 'location' => 'modules/nodereference/nodereference.rules.inc:50', + 'textgroup' => 'default', + 'source' => 'There are no nodereference fields defined.', + 'version' => 'none', +)) +->values(array( + 'lid' => '342', + 'location' => 'modules/nodereference/nodereference.module:60', + 'textgroup' => 'default', + 'source' => 'Node reference', + 'version' => 'none', +)) +->values(array( + 'lid' => '343', + 'location' => 'modules/nodereference/nodereference.module:61', + 'textgroup' => 'default', + 'source' => 'Store the ID of a related node as an integer value.', + 'version' => 'none', +)) +->values(array( + 'lid' => '344', + 'location' => 'nodereference.module:51', + 'textgroup' => 'default', + 'source' => 'Content types that can be referenced', + 'version' => 'none', +)) +->values(array( + 'lid' => '345', + 'location' => 'modules/nodereference/nodereference.module:97, modules/userreference/userreference.module:94', + 'textgroup' => 'default', + 'source' => 'Default Views', + 'version' => 'none', +)) +->values(array( + 'lid' => '346', + 'location' => 'modules/nodereference/nodereference.module:100, modules/userreference/userreference.module:97', + 'textgroup' => 'default', + 'source' => 'Existing Views', + 'version' => 'none', +)) +->values(array( + 'lid' => '347', + 'location' => 'modules/nodereference/nodereference.module:97', + 'textgroup' => 'default', + 'source' => 'Advanced - Nodes that can be referenced (View)', + 'version' => 'none', +)) +->values(array( + 'lid' => '348', + 'location' => 'modules/nodereference/nodereference.module:104', + 'textgroup' => 'default', + 'source' => 'View used to select the nodes', + 'version' => 'none', +)) +->values(array( + 'lid' => '349', + 'location' => 'modules/nodereference/nodereference.module:107', + 'textgroup' => 'default', + 'source' => '

                          Choose the "Views module" view that selects the nodes that can be referenced.
                          Note:

                          ', + 'version' => 'none', +)) +->values(array( + 'lid' => '350', + 'location' => 'modules/nodereference/nodereference.module:108;121', + 'textgroup' => 'default', + 'source' => "
                          • Only views that have fields will work for this purpose.
                          • This will discard the \"Content types\" settings above. Use the view's \"filters\" section instead.
                          • Use the view's \"fields\" section to display additional informations about candidate nodes on node creation/edition form.
                          • Use the view's \"sort criteria\" section to determine the order in which candidate nodes will be displayed.
                          ", + 'version' => 'none', +)) +->values(array( + 'lid' => '351', + 'location' => 'modules/nodereference/nodereference.module:122, modules/userreference/userreference.module:119', + 'textgroup' => 'default', + 'source' => 'View arguments', + 'version' => 'none', +)) +->values(array( + 'lid' => '352', + 'location' => 'modules/nodereference/nodereference.module:125, modules/userreference/userreference.module:122', + 'textgroup' => 'default', + 'source' => 'Provide a comma separated list of arguments to pass to the view.', + 'version' => 'none', +)) +->values(array( + 'lid' => '353', + 'location' => 'modules/nodereference/nodereference.module:120', + 'textgroup' => 'default', + 'source' => '

                          The list of nodes that can be referenced can be based on a "Views module" view but no appropriate views were found.
                          Note:

                          ', + 'version' => 'none', +)) +->values(array( + 'lid' => '354', + 'location' => 'modules/nodereference/nodereference.module:216, modules/userreference/userreference.module:195', + 'textgroup' => 'default', + 'source' => '%name: invalid input.', + 'version' => 'none', +)) +->values(array( + 'lid' => '355', + 'location' => 'modules/nodereference/nodereference.module:217', + 'textgroup' => 'default', + 'source' => "%name: this post can't be referenced.", + 'version' => 'none', +)) +->values(array( + 'lid' => '356', + 'location' => 'modules/nodereference/nodereference.module:242', + 'textgroup' => 'default', + 'source' => 'Title (link)', + 'version' => 'none', +)) +->values(array( + 'lid' => '357', + 'location' => 'modules/nodereference/nodereference.module:247', + 'textgroup' => 'default', + 'source' => 'Title (no link)', + 'version' => 'none', +)) +->values(array( + 'lid' => '358', + 'location' => 'modules/nodereference/nodereference.module:358, modules/optionwidgets/optionwidgets.module:80, modules/userreference/userreference.module:284', + 'textgroup' => 'default', + 'source' => 'Select list', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '359', + 'location' => 'modules/nodereference/nodereference.module:366, modules/optionwidgets/optionwidgets.module:88, modules/userreference/userreference.module:292', + 'textgroup' => 'default', + 'source' => 'Check boxes/radio buttons', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '360', + 'location' => 'modules/nodereference/nodereference.module:374, modules/userreference/userreference.module:300', + 'textgroup' => 'default', + 'source' => 'Autocomplete text field', + 'version' => 'none', +)) +->values(array( + 'lid' => '361', + 'location' => 'modules/nodereference/nodereference.module:429, modules/userreference/userreference.module:355', + 'textgroup' => 'default', + 'source' => 'Autocomplete matching', + 'version' => 'none', +)) +->values(array( + 'lid' => '362', + 'location' => 'modules/nodereference/nodereference.module:432, modules/userreference/userreference.module:358', + 'textgroup' => 'default', + 'source' => 'Starts with', + 'version' => 'none', +)) +->values(array( + 'lid' => '363', + 'location' => 'modules/nodereference/nodereference.module:433, modules/userreference/userreference.module:359', + 'textgroup' => 'default', + 'source' => 'Contains', + 'version' => 'none', +)) +->values(array( + 'lid' => '364', + 'location' => 'modules/nodereference/nodereference.module:423', + 'textgroup' => 'default', + 'source' => 'Select the method used to collect autocomplete suggestions. Note that Contains can cause performance issues on sites with thousands of nodes.', + 'version' => 'none', +)) +->values(array( + 'lid' => '365', + 'location' => 'modules/nodereference/nodereference.module:671', + 'textgroup' => 'default', + 'source' => '%name: title mismatch. Please check your selection.', + 'version' => 'none', +)) +->values(array( + 'lid' => '366', + 'location' => 'modules/nodereference/nodereference.module:678', + 'textgroup' => 'default', + 'source' => '%name: found no valid post with that title.', + 'version' => 'none', +)) +->values(array( + 'lid' => '367', + 'location' => 'modules/nodereference/nodereference.module:15', + 'textgroup' => 'default', + 'source' => 'Nodereference autocomplete', + 'version' => 'none', +)) +->values(array( + 'lid' => '368', + 'location' => 'nodereference.module:0', + 'textgroup' => 'default', + 'source' => 'nodereference', + 'version' => 'none', +)) +->values(array( + 'lid' => '369', + 'location' => 'modules/nodereference/nodereference.info:0', + 'textgroup' => 'default', + 'source' => 'Node Reference', + 'version' => 'none', +)) +->values(array( + 'lid' => '370', + 'location' => 'modules/nodereference/nodereference.info:0', + 'textgroup' => 'default', + 'source' => 'Defines a field type for referencing one node from another.', + 'version' => 'none', +)) +->values(array( + 'lid' => '371', + 'location' => 'modules/number/number.module:34', + 'textgroup' => 'default', + 'source' => 'Integer', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '372', + 'location' => 'modules/number/number.module:35', + 'textgroup' => 'default', + 'source' => 'Store a number in the database as an integer.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '373', + 'location' => 'modules/number/number.module:38', + 'textgroup' => 'default', + 'source' => 'Decimal', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '374', + 'location' => 'modules/number/number.module:39', + 'textgroup' => 'default', + 'source' => 'Store a number in the database in a fixed decimal format.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '375', + 'location' => 'modules/number/number.module:42', + 'textgroup' => 'default', + 'source' => 'Float', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '376', + 'location' => 'modules/number/number.module:43', + 'textgroup' => 'default', + 'source' => 'Store a number in the database in a floating point format.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '377', + 'location' => 'modules/number/number.module:57', + 'textgroup' => 'default', + 'source' => 'Minimum', + 'version' => 'none', +)) +->values(array( + 'lid' => '378', + 'location' => 'modules/number/number.module:63', + 'textgroup' => 'default', + 'source' => 'Maximum', + 'version' => 'none', +)) +->values(array( + 'lid' => '379', + 'location' => 'modules/number/number.module:71', + 'textgroup' => 'default', + 'source' => 'Precision', + 'version' => 'none', +)) +->values(array( + 'lid' => '380', + 'location' => 'modules/number/number.module:72', + 'textgroup' => 'default', + 'source' => 'The total number of digits to store in the database, including those to the right of the decimal.', + 'version' => 'none', +)) +->values(array( + 'lid' => '381', + 'location' => 'modules/number/number.module:78', + 'textgroup' => 'default', + 'source' => 'Scale', + 'version' => 'none', +)) +->values(array( + 'lid' => '382', + 'location' => 'modules/number/number.module:79', + 'textgroup' => 'default', + 'source' => 'The number of digits to the right of the decimal.', + 'version' => 'none', +)) +->values(array( + 'lid' => '383', + 'location' => 'modules/number/number.module:85', + 'textgroup' => 'default', + 'source' => 'Decimal marker', + 'version' => 'none', +)) +->values(array( + 'lid' => '384', + 'location' => 'modules/number/number.module:86', + 'textgroup' => 'default', + 'source' => 'The character users will input to mark the decimal point in forms.', + 'version' => 'none', +)) +->values(array( + 'lid' => '385', + 'location' => 'modules/number/number.module:92', + 'textgroup' => 'default', + 'source' => 'Prefix', + 'version' => 'none', +)) +->values(array( + 'lid' => '386', + 'location' => 'modules/number/number.module:95', + 'textgroup' => 'default', + 'source' => 'Define a string that should be prefixed to the value, like $ or €. Leave blank for none. Separate singular and plural values with a pipe (pound|pounds).', + 'version' => 'none', +)) +->values(array( + 'lid' => '387', + 'location' => 'modules/number/number.module:99', + 'textgroup' => 'default', + 'source' => 'Suffix', + 'version' => 'none', +)) +->values(array( + 'lid' => '388', + 'location' => 'modules/number/number.module:102', + 'textgroup' => 'default', + 'source' => 'Define a string that should suffixed to the value, like m², m/s², kb/s. Leave blank for none. Separate singular and plural values with a pipe (pound|pounds).', + 'version' => 'none', +)) +->values(array( + 'lid' => '389', + 'location' => 'modules/number/number.module:109, modules/text/text.module:72', + 'textgroup' => 'default', + 'source' => 'Allowed values', + 'version' => 'none', +)) +->values(array( + 'lid' => '390', + 'location' => 'modules/number/number.module:115, modules/text/text.module:78', + 'textgroup' => 'default', + 'source' => 'Allowed values list', + 'version' => 'none', +)) +->values(array( + 'lid' => '391', + 'location' => 'modules/number/number.module:119, modules/text/text.module:82', + 'textgroup' => 'default', + 'source' => 'The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database, and it must match the field storage type (%type). The label is optional, and the key will be used as the label if no label is specified.
                          Allowed HTML tags: @tags', + 'version' => 'none', +)) +->values(array( + 'lid' => '392', + 'location' => 'modules/number/number.module:133, modules/text/text.module:96', + 'textgroup' => 'default', + 'source' => 'Advanced usage only: PHP code that returns a keyed array of allowed values. Should not include <?php ?> delimiters. If this field is filled out, the array returned by this code will override the allowed values list above.', + 'version' => 'none', +)) +->values(array( + 'lid' => '393', + 'location' => 'modules/number/number.module:141, modules/text/text.module:104', + 'textgroup' => 'default', + 'source' => 'This PHP code was set by an administrator and will override the allowed values list above.', + 'version' => 'none', +)) +->values(array( + 'lid' => '394', + 'location' => 'modules/number/number.module:181, modules/text/text.module:133', + 'textgroup' => 'default', + 'source' => '@label (!name) - Allowed values', + 'version' => 'none', +)) +->values(array( + 'lid' => '395', + 'location' => 'modules/number/number.module:195', + 'textgroup' => 'default', + 'source' => '"Minimum" must be a number.', + 'version' => 'none', +)) +->values(array( + 'lid' => '396', + 'location' => 'modules/number/number.module:202', + 'textgroup' => 'default', + 'source' => '"Maximum" must be a number.', + 'version' => 'none', +)) +->values(array( + 'lid' => '397', + 'location' => 'modules/number/number.module:219', + 'textgroup' => 'default', + 'source' => '%name: the value may be no smaller than %min.', + 'version' => 'none', +)) +->values(array( + 'lid' => '398', + 'location' => 'modules/number/number.module:222', + 'textgroup' => 'default', + 'source' => '%name: the value may be no larger than %max.', + 'version' => 'none', +)) +->values(array( + 'lid' => '399', + 'location' => 'modules/number/number.module:238, modules/text/text.module:157', + 'textgroup' => 'default', + 'source' => '%name: illegal value.', + 'version' => 'none', +)) +->values(array( + 'lid' => '400', + 'location' => 'modules/number/number.module:270', + 'textgroup' => 'default', + 'source' => 'unformatted', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '401', + 'location' => 'modules/number/number.module:356, modules/text/text.module:257', + 'textgroup' => 'default', + 'source' => 'Text field', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '402', + 'location' => 'Float validation: English needs work, modules/number/number.module:509, fuzzy', + 'textgroup' => 'default', + 'source' => 'Only numbers and decimals are allowed in %field.', + 'version' => 'none', +)) +->values(array( + 'lid' => '403', + 'location' => 'Integer validation: English needs work, modules/number/number.module:532, fuzzy', + 'textgroup' => 'default', + 'source' => 'Only numbers are allowed in %field.', + 'version' => 'none', +)) +->values(array( + 'lid' => '404', + 'location' => 'Decimal validation with decimal character: English needs work, modules/number/number.module:556, fuzzy', + 'textgroup' => 'default', + 'source' => 'Only numbers and the decimal character (%decimal) are allowed in %field.', + 'version' => 'none', +)) +->values(array( + 'lid' => '405', + 'location' => 'modules/number/number.module:0', + 'textgroup' => 'default', + 'source' => 'number', + 'version' => 'none', +)) +->values(array( + 'lid' => '406', + 'location' => 'modules/number/number.info:0', + 'textgroup' => 'default', + 'source' => 'Number', + 'version' => 'none', +)) +->values(array( + 'lid' => '407', + 'location' => 'modules/number/number.info:0', + 'textgroup' => 'default', + 'source' => 'Defines numeric field types.', + 'version' => 'none', +)) +->values(array( + 'lid' => '408', + 'location' => 'modules/optionwidgets/optionwidgets.module:19', + 'textgroup' => 'default', + 'source' => 'Create a list of options as a list in Allowed values list or as an array in PHP code. These values will be the same for %field in all content types.', + 'version' => 'none', +)) +->values(array( + 'lid' => '409', + 'location' => 'modules/optionwidgets/optionwidgets.module:22', + 'textgroup' => 'default', + 'source' => "For a 'single on/off checkbox' widget, define the 'off' value first, then the 'on' value in the Allowed values section. Note that the checkbox will be labeled with the label of the 'on' value.", + 'version' => 'none', +)) +->values(array( + 'lid' => '410', + 'location' => 'modules/optionwidgets/optionwidgets.module:25', + 'textgroup' => 'default', + 'source' => "The 'checkboxes/radio buttons' widget will display checkboxes if the multiple values option is selected for this field, otherwise radios will be displayed.", + 'version' => 'none', +)) +->values(array( + 'lid' => '411', + 'location' => 'modules/optionwidgets/optionwidgets.module:37', + 'textgroup' => 'default', + 'source' => "You need to specify the 'allowed values' for this field.", + 'version' => 'none', +)) +->values(array( + 'lid' => '412', + 'location' => 'modules/optionwidgets/optionwidgets.module:96', + 'textgroup' => 'default', + 'source' => 'Single on/off checkbox', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '413', + 'location' => 'modules/optionwidgets/optionwidgets.module:331', + 'textgroup' => 'default', + 'source' => '%name: this field cannot hold more that @count values.', + 'version' => 'none', +)) +->values(array( + 'lid' => '414', + 'location' => 'modules/optionwidgets/optionwidgets.module:411', + 'textgroup' => 'default', + 'source' => 'N/A', + 'version' => 'none', +)) +->values(array( + 'lid' => '415', + 'location' => 'modules/optionwidgets/optionwidgets.module:415', + 'textgroup' => 'default', + 'source' => '- None -', + 'version' => 'none', +)) +->values(array( + 'lid' => '416', + 'location' => 'modules/optionwidgets/optionwidgets.module:0', + 'textgroup' => 'default', + 'source' => 'optionwidgets', + 'version' => 'none', +)) +->values(array( + 'lid' => '417', + 'location' => 'modules/optionwidgets/optionwidgets.info:0', + 'textgroup' => 'default', + 'source' => 'Option Widgets', + 'version' => 'none', +)) +->values(array( + 'lid' => '418', + 'location' => 'modules/optionwidgets/optionwidgets.info:0', + 'textgroup' => 'default', + 'source' => 'Defines selection, check box and radio button widgets for text and numeric fields.', + 'version' => 'none', +)) +->values(array( + 'lid' => '419', + 'location' => 'modules/text/text.module:42', + 'textgroup' => 'default', + 'source' => 'Store text in the database.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '420', + 'location' => 'modules/text/text.module:55;202, modules/userreference/userreference.module:237', + 'textgroup' => 'default', + 'source' => 'Plain text', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '421', + 'location' => 'modules/text/text.module:55', + 'textgroup' => 'default', + 'source' => 'Filtered text (user selects input format)', + 'version' => 'none', +)) +->values(array( + 'lid' => '422', + 'location' => 'modules/text/text.module:58', + 'textgroup' => 'default', + 'source' => 'Text processing', + 'version' => 'none', +)) +->values(array( + 'lid' => '423', + 'location' => 'modules/text/text.module:64', + 'textgroup' => 'default', + 'source' => 'Maximum length', + 'version' => 'none', +)) +->values(array( + 'lid' => '424', + 'location' => 'modules/text/text.module:68', + 'textgroup' => 'default', + 'source' => 'The maximum length of the field in characters. Leave blank for an unlimited size.', + 'version' => 'none', +)) +->values(array( + 'lid' => '425', + 'location' => 'modules/text/text.module:160', + 'textgroup' => 'default', + 'source' => '%name: the value may not be longer than %max characters.', + 'version' => 'none', +)) +->values(array( + 'lid' => '426', + 'location' => 'modules/text/text.module:197, modules/userreference/userreference.module:232', + 'textgroup' => 'default', + 'source' => 'Default', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '427', + 'location' => 'modules/text/text.module:207', + 'textgroup' => 'default', + 'source' => 'Trimmed', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '428', + 'location' => 'modules/text/text.module:265', + 'textgroup' => 'default', + 'source' => 'Text area (multiple rows)', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '429', + 'location' => 'modules/nodereference/nodereference.module:439, modules/text/text.module:317, modules/userreference/userreference.module:365', + 'textgroup' => 'default', + 'source' => 'Size of textfield', + 'version' => 'none', +)) +->values(array( + 'lid' => '430', + 'location' => 'modules/text/text.module:326', + 'textgroup' => 'default', + 'source' => 'Rows', + 'version' => 'none', +)) +->values(array( + 'lid' => '431', + 'location' => 'modules/text/text.module:0', + 'textgroup' => 'default', + 'source' => 'text', + 'version' => 'none', +)) +->values(array( + 'lid' => '432', + 'location' => 'modules/text/text.info:0', + 'textgroup' => 'default', + 'source' => 'Defines simple text field types.', + 'version' => 'none', +)) +->values(array( + 'lid' => '433', + 'location' => 'modules/userreference/userreference.rules.inc:15', + 'textgroup' => 'default', + 'source' => 'Load a referenced user', + 'version' => 'none', +)) +->values(array( + 'lid' => '434', + 'location' => 'modules/userreference/userreference.rules.inc:19', + 'textgroup' => 'default', + 'source' => 'Content containing the user reference field', + 'version' => 'none', +)) +->values(array( + 'lid' => '435', + 'location' => 'modules/userreference/userreference.rules.inc:25', + 'textgroup' => 'default', + 'source' => 'Referenced user', + 'version' => 'none', +)) +->values(array( + 'lid' => '436', + 'location' => 'modules/userreference/userreference.rules.inc:29', + 'textgroup' => 'default', + 'source' => 'Note that if the field has multiple values, only the first user will be loaded.', + 'version' => 'none', +)) +->values(array( + 'lid' => '437', + 'location' => 'modules/userreference/userreference.rules.inc:52', + 'textgroup' => 'default', + 'source' => 'There are no userreference fields defined.', + 'version' => 'none', +)) +->values(array( + 'lid' => '438', + 'location' => 'modules/userreference/userreference.module:52', + 'textgroup' => 'default', + 'source' => 'User reference', + 'version' => 'none', +)) +->values(array( + 'lid' => '439', + 'location' => 'modules/userreference/userreference.module:53', + 'textgroup' => 'default', + 'source' => 'Store the ID of a related user as an integer value.', + 'version' => 'none', +)) +->values(array( + 'lid' => '440', + 'location' => 'modules/userreference/userreference.module:67', + 'textgroup' => 'default', + 'source' => 'User roles that can be referenced', + 'version' => 'none', +)) +->values(array( + 'lid' => '441', + 'location' => 'modules/userreference/userreference.module:73', + 'textgroup' => 'default', + 'source' => 'User status that can be referenced', + 'version' => 'none', +)) +->values(array( + 'lid' => '442', + 'location' => 'modules/userreference/userreference.module:75', + 'textgroup' => 'default', + 'source' => 'Active', + 'version' => 'none', +)) +->values(array( + 'lid' => '443', + 'location' => 'modules/userreference/userreference.module:75', + 'textgroup' => 'default', + 'source' => 'Blocked', + 'version' => 'none', +)) +->values(array( + 'lid' => '444', + 'location' => 'modules/userreference/userreference.module:94', + 'textgroup' => 'default', + 'source' => 'Advanced - Users that can be referenced (View)', + 'version' => 'none', +)) +->values(array( + 'lid' => '445', + 'location' => 'modules/userreference/userreference.module:101', + 'textgroup' => 'default', + 'source' => 'View used to select the users', + 'version' => 'none', +)) +->values(array( + 'lid' => '446', + 'location' => 'modules/userreference/userreference.module:104', + 'textgroup' => 'default', + 'source' => '

                          Choose the "Views module" view that selects the users that can be referenced.
                          Note:

                          ', + 'version' => 'none', +)) +->values(array( + 'lid' => '447', + 'location' => 'modules/userreference/userreference.module:105;118', + 'textgroup' => 'default', + 'source' => "
                          • Only views that have fields will work for this purpose.
                          • This will discard the \"Referenceable Roles\" and \"Referenceable Status\" settings above. Use the view's \"filters\" section instead.
                          • Use the view's \"fields\" section to display additional informations about candidate users on user creation/edition form.
                          • Use the view's \"sort criteria\" section to determine the order in which candidate users will be displayed.
                          ", + 'version' => 'none', +)) +->values(array( + 'lid' => '448', + 'location' => 'modules/userreference/userreference.module:117', + 'textgroup' => 'default', + 'source' => '

                          The list of user that can be referenced can be based on a "Views module" view but no appropriate views were found.
                          Note:

                          ', + 'version' => 'none', +)) +->values(array( + 'lid' => '449', + 'location' => 'modules/userreference/userreference.module:196', + 'textgroup' => 'default', + 'source' => '%name: invalid user.', + 'version' => 'none', +)) +->values(array( + 'lid' => '450', + 'location' => 'modules/userreference/userreference.module:349', + 'textgroup' => 'default', + 'source' => 'Select the method used to collect autocomplete suggestions. Note that Contains can cause performance issues on sites with thousands of users.', + 'version' => 'none', +)) +->values(array( + 'lid' => '451', + 'location' => 'modules/userreference/userreference.module:357', + 'textgroup' => 'default', + 'source' => 'Reverse link', + 'version' => 'none', +)) +->values(array( + 'lid' => '452', + 'location' => 'modules/userreference/userreference.module:359', + 'textgroup' => 'default', + 'source' => 'If selected, a reverse link back to the referencing node will displayed on the referenced user record.', + 'version' => 'none', +)) +->values(array( + 'lid' => '453', + 'location' => 'modules/userreference/userreference.module:594', + 'textgroup' => 'default', + 'source' => '%name: found no valid user with that name.', + 'version' => 'none', +)) +->values(array( + 'lid' => '454', + 'location' => 'modules/userreference/userreference.module:887', + 'textgroup' => 'default', + 'source' => 'Related content', + 'version' => 'none', +)) +->values(array( + 'lid' => '455', + 'location' => 'modules/userreference/userreference.module:15', + 'textgroup' => 'default', + 'source' => 'Userreference autocomplete', + 'version' => 'none', +)) +->values(array( + 'lid' => '456', + 'location' => 'userreference.module:0', + 'textgroup' => 'default', + 'source' => 'userreference', + 'version' => 'none', +)) +->values(array( + 'lid' => '457', + 'location' => 'modules/userreference/userreference.info:0', + 'textgroup' => 'default', + 'source' => 'User Reference', + 'version' => 'none', +)) +->values(array( + 'lid' => '458', + 'location' => 'modules/userreference/userreference.info:0', + 'textgroup' => 'default', + 'source' => 'Defines a field type for referencing a user from a node.', + 'version' => 'none', +)) +->values(array( + 'lid' => '459', + 'location' => 'theme/content-admin-field-overview-form.tpl.php:11', + 'textgroup' => 'default', + 'source' => 'Weight', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '460', + 'location' => 'theme/content-admin-field-overview-form.tpl.php:53', + 'textgroup' => 'default', + 'source' => 'Add', + 'version' => 'none', +)) +->values(array( + 'lid' => '461', + 'location' => 'theme/content-admin-field-overview-form.tpl.php:59', + 'textgroup' => 'default', + 'source' => 'New field', + 'version' => 'none', +)) +->values(array( + 'lid' => '462', + 'location' => 'theme/content-admin-field-overview-form.tpl.php:72', + 'textgroup' => 'default', + 'source' => 'Existing field', + 'version' => 'none', +)) +->values(array( + 'lid' => '463', + 'location' => 'theme/content-admin-field-overview-form.tpl.php:84', + 'textgroup' => 'default', + 'source' => 'New group', + 'version' => 'none', +)) +->values(array( + 'lid' => '464', + 'location' => 'theme/theme.inc:11', + 'textgroup' => 'default', + 'source' => 'Add fields and groups to the content type, and arrange them on content display and input forms.', + 'version' => 'none', +)) +->values(array( + 'lid' => '465', + 'location' => 'theme/theme.inc:13', + 'textgroup' => 'default', + 'source' => 'You can add a field to a group by dragging it below and to the right of the group.', + 'version' => 'none', +)) +->values(array( + 'lid' => '466', + 'location' => 'theme/theme.inc:16', + 'textgroup' => 'default', + 'source' => 'Note: Installing the Advanced help module will let you access more and better help.', + 'version' => 'none', +)) +->values(array( + 'lid' => '467', + 'location' => 'theme/theme.inc:116', + 'textgroup' => 'default', + 'source' => "Use the 'Exclude' checkbox to exclude an item from the !content value passed to the node template.", + 'version' => 'none', +)) +->values(array( + 'lid' => '468', + 'location' => 'theme/content-edit.js:0', + 'textgroup' => 'default', + 'source' => 'Remove this item', + 'version' => 'none', +)) +->values(array( + 'lid' => '469', + 'location' => 'content_admin.inc:290', + 'textgroup' => 'default', + 'source' => 'Add field', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '470', + 'location' => 'field.php:180;190, number.module:119, text.module:107', + 'textgroup' => 'default', + 'source' => 'Illegal value for %name.', + 'version' => 'none', +)) +->values(array( + 'lid' => '471', + 'location' => 'examples/example_field.php:287 examples/simple_field.php:231, modules/text/text.module:169', + 'textgroup' => 'default', + 'source' => '%label is longer than %max characters.', + 'version' => 'none', +)) +->values(array( + 'lid' => '472', + 'location' => 'field.php:273 text.module:167', + 'textgroup' => 'default', + 'source' => '"Rows" must be a positive integer.', + 'version' => 'none', +)) +->values(array( + 'lid' => '473', + 'location' => 'modules/number/number.module:133 modules/text/text.module:92', + 'textgroup' => 'default', + 'source' => 'The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database and it must match the field storage type, %type. The label is optional and the key will be used as the label if no label is specified.
                          Allowed HTML tags: @tags', + 'version' => 'none', +)) +->values(array( + 'lid' => '474', + 'location' => 'content.module:144', + 'textgroup' => 'default', + 'source' => 'add field', + 'version' => 'none', +)) +->values(array( + 'lid' => '475', + 'location' => 'includes/content.admin.inc:112;291', + 'textgroup' => 'default', + 'source' => 'There are no fields configured for this content type. You can !link.', + 'version' => 'none', +)) +->values(array( + 'lid' => '476', + 'location' => 'includes/content.admin.inc:113;292', + 'textgroup' => 'default', + 'source' => 'Add a new field', + 'version' => 'none', +)) +->values(array( + 'lid' => '477', + 'location' => 'includes/content.admin.inc:137', + 'textgroup' => 'default', + 'source' => 'To change the order of a field, grab a drag-and-drop handle under the Label column and drag the field to a new location in the list. (Grab a handle by clicking and holding the mouse while hovering over a handle icon.) Remember that your changes will not be saved until you click the Save button at the bottom of the page.', + 'version' => 'none', +)) +->values(array( + 'lid' => '478', + 'location' => 'includes/content.admin.inc:477', + 'textgroup' => 'default', + 'source' => 'No field modules are enabled. You need to enable one, such as text.module, before you can add new fields.', + 'version' => 'none', +)) +->values(array( + 'lid' => '479', + 'location' => 'content_admin.inc:277', + 'textgroup' => 'default', + 'source' => 'Add existing field', + 'version' => 'none', +)) +->values(array( + 'lid' => '480', + 'location' => 'content_admin.inc:311', + 'textgroup' => 'default', + 'source' => 'Create new field', + 'version' => 'none', +)) +->values(array( + 'lid' => '481', + 'location' => 'includes/content.admin.inc:606', + 'textgroup' => 'default', + 'source' => 'The machine-readable name of the field.', + 'version' => 'none', +)) +->values(array( + 'lid' => '482', + 'location' => 'includes/content.admin.inc:610', + 'textgroup' => 'default', + 'source' => 'This name cannot be changed.', + 'version' => 'none', +)) +->values(array( + 'lid' => '483', + 'location' => 'includes/content.admin.inc:618', + 'textgroup' => 'default', + 'source' => "This name cannot be changed later! The name will be prefixed with 'field_' and can include lowercase unaccented letters, numbers, and underscores. The length of the name, including the prefix, is limited to no more than 32 letters.", + 'version' => 'none', +)) +->values(array( + 'lid' => '484', + 'location' => 'includes/content.admin.inc:636', + 'textgroup' => 'default', + 'source' => 'The type of data you would like to store in the database with this field.', + 'version' => 'none', +)) +->values(array( + 'lid' => '485', + 'location' => 'includes/content.admin.inc:692', + 'textgroup' => 'default', + 'source' => 'The field name %field_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', + 'version' => 'none', +)) +->values(array( + 'lid' => '486', + 'location' => 'includes/content.admin.inc:695', + 'textgroup' => 'default', + 'source' => "The field name %field_name is too long. The name is limited to 32 characters, including the 'field_' prefix.", + 'version' => 'none', +)) +->values(array( + 'lid' => '487', + 'location' => 'includes/content.admin.inc:706', + 'textgroup' => 'default', + 'source' => 'The field name %field_name already exists.', + 'version' => 'none', +)) +->values(array( + 'lid' => '488', + 'location' => 'includes/content.admin.inc:709', + 'textgroup' => 'default', + 'source' => "The name 'field_instance' is a reserved name.", + 'version' => 'none', +)) +->values(array( + 'lid' => '489', + 'location' => 'content_admin.inc:432', + 'textgroup' => 'default', + 'source' => 'Created field %label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '490', + 'location' => 'includes/content.admin.inc:754', + 'textgroup' => 'default', + 'source' => 'Update field %label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '491', + 'location' => 'includes/content.admin.inc:758', + 'textgroup' => 'default', + 'source' => 'There was a problem updating field %label.', + 'version' => 'none', +)) +->values(array( + 'lid' => '492', + 'location' => 'includes/content.admin.inc:955', + 'textgroup' => 'default', + 'source' => "Advanced usage only: PHP code that returns a default value. Should not include <?php ?> delimiters. If this field is filled out, the value returned by this code will override any value specified above. Expected format:
                          !sample
                          Using !link_devel's 'devel load' tab on a %type content page might help you figure out the expected format.", + 'version' => 'none', +)) +->values(array( + 'lid' => '493', + 'location' => 'includes/content.admin.inc:986', + 'textgroup' => 'default', + 'source' => "Select a specific number of values for this field, or 'Unlimited' to provide an 'Add more' button so the users can add as many values as they like.", + 'version' => 'none', +)) +->values(array( + 'lid' => '494', + 'location' => 'includes/content.admin.inc:1131', + 'textgroup' => 'default', + 'source' => 'The default value PHP code created @value which is invalid.', + 'version' => 'none', +)) +->values(array( + 'lid' => '495', + 'location' => 'includes/content.token.inc:62', + 'textgroup' => 'default', + 'source' => 'Formatted HTML link to the node', + 'version' => 'none', +)) +->values(array( + 'lid' => '496', + 'location' => 'number.module:113', + 'textgroup' => 'default', + 'source' => 'The value of %name may be no smaller than %min.', + 'version' => 'none', +)) +->values(array( + 'lid' => '497', + 'location' => 'number.module:116', + 'textgroup' => 'default', + 'source' => 'The value of %name may be no larger than %max.', + 'version' => 'none', +)) +->values(array( + 'lid' => '498', + 'location' => 'modules/number/number.module:476', + 'textgroup' => 'default', + 'source' => 'Only numbers and decimals are allowed in %field. %start was changed to %value.', + 'version' => 'none', +)) +->values(array( + 'lid' => '499', + 'location' => 'modules/number/number.module:494', + 'textgroup' => 'default', + 'source' => 'Only numbers are allowed in %field. %start was changed to %value.', + 'version' => 'none', +)) +->values(array( + 'lid' => '500', + 'location' => 'modules/number/number.module:513', + 'textgroup' => 'default', + 'source' => 'Only numbers and the decimal character (%decimal) are allowed in %field. %start was changed to %value.', + 'version' => 'none', +)) +->values(array( + 'lid' => '501', + 'location' => 'modules/optionwidgets/optionwidgets.module:10', + 'textgroup' => 'default', + 'source' => 'Create a list of options as a list in Allowed values or as an array in PHP code. These values will be the same for %field in all content types.', + 'version' => 'none', +)) +->values(array( + 'lid' => '502', + 'location' => 'misc/tabledrag.js', + 'textgroup' => 'default', + 'source' => 'Drag to re-order', + 'version' => 'none', +)) +->values(array( + 'lid' => '503', + 'location' => 'misc/tabledrag.js', + 'textgroup' => 'default', + 'source' => 'Changes made in this table will not be saved until the form is submitted.', + 'version' => 'none', +)) +->values(array( + 'lid' => '504', + 'location' => 'field:profile_color:title', + 'textgroup' => 'profile', + 'source' => 'Favorite color', + 'version' => '1', +)) +->values(array( + 'lid' => '505', + 'location' => 'field:profile_color:explanation', + 'textgroup' => 'profile', + 'source' => 'List your favorite color', + 'version' => '1', +)) +->values(array( + 'lid' => '506', + 'location' => 'category', + 'textgroup' => 'profile', + 'source' => 'Personal information', + 'version' => '1', +)) +->values(array( + 'lid' => '507', + 'location' => 'field:profile_biography:title', + 'textgroup' => 'profile', + 'source' => 'Biography', + 'version' => '1', +)) +->values(array( + 'lid' => '508', + 'location' => 'field:profile_biography:explanation', + 'textgroup' => 'profile', + 'source' => 'Tell people a little bit about yourself', + 'version' => '1', +)) +->values(array( + 'lid' => '509', + 'location' => 'field:profile_sell_address:title', + 'textgroup' => 'profile', + 'source' => 'Sell your email address?', + 'version' => '1', +)) +->values(array( + 'lid' => '510', + 'location' => 'field:profile_sell_address:explanation', + 'textgroup' => 'profile', + 'source' => "If you check this box, we'll sell your address to spammers to help line the pockets of our shareholders. Thanks!", + 'version' => '1', +)) +->values(array( + 'lid' => '511', + 'location' => 'category', + 'textgroup' => 'profile', + 'source' => 'Communication preferences', + 'version' => '1', +)) +->values(array( + 'lid' => '512', + 'location' => 'field:profile_sold_to:title', + 'textgroup' => 'profile', + 'source' => 'Sales Category', + 'version' => '1', +)) +->values(array( + 'lid' => '513', + 'location' => 'field:profile_sold_to:explanation', + 'textgroup' => 'profile', + 'source' => "Select the sales categories to which this user's address was sold.", + 'version' => '1', +)) +->values(array( + 'lid' => '514', + 'location' => 'field:profile_sold_to:options', + 'textgroup' => 'profile', + 'source' => "Pill spammers\r\nFitness spammers\r\nBack\\slash\r\nForward/slash\r\nDot.in.the.middle", + 'version' => '1', +)) +->values(array( + 'lid' => '515', + 'location' => 'category', + 'textgroup' => 'profile', + 'source' => 'Administrative data', + 'version' => '1', +)) +->values(array( + 'lid' => '516', + 'location' => 'field:profile_bands:title', + 'textgroup' => 'profile', + 'source' => 'Favorite bands', + 'version' => '1', +)) +->values(array( + 'lid' => '517', + 'location' => 'field:profile_bands:explanation', + 'textgroup' => 'profile', + 'source' => "Enter your favorite bands. When you've saved your profile, you'll be able to find other people with the same favorites.", + 'version' => '1', +)) +->values(array( + 'lid' => '518', + 'location' => 'field:profile_birthdate:title', + 'textgroup' => 'profile', + 'source' => 'Birthdate', + 'version' => '1', +)) +->values(array( + 'lid' => '519', + 'location' => 'field:profile_birthdate:explanation', + 'textgroup' => 'profile', + 'source' => "Enter your birth date and we'll send you a coupon.", + 'version' => '1', +)) +->values(array( + 'lid' => '520', + 'location' => 'field:profile_love_migrations:title', + 'textgroup' => 'profile', + 'source' => 'I love migrations', + 'version' => '1', +)) +->values(array( + 'lid' => '521', + 'location' => 'field:profile_love_migrations:explanation', + 'textgroup' => 'profile', + 'source' => 'If you check this box, you love migrations.', + 'version' => '1', +)) +->values(array( + 'lid' => '522', + 'location' => 'field:profile_blog:title', + 'textgroup' => 'profile', + 'source' => 'Blog', + 'version' => '1', +)) +->values(array( + 'lid' => '523', + 'location' => 'field:profile_blog:explanation', + 'textgroup' => 'profile', + 'source' => 'Paste the full URL, including http://, of your personal blog.', + 'version' => '1', +)) +->values(array( + 'lid' => '524', + 'location' => 'block:1:title', + 'textgroup' => 'blocks', + 'source' => 'Static Block', + 'version' => '1', +)) +->values(array( + 'lid' => '525', + 'location' => 'block:1:body', + 'textgroup' => 'blocks', + 'source' => '

                          My first custom block body

                          ', + 'version' => '1', +)) +->values(array( + 'lid' => '526', + 'location' => 'block:2:title', + 'textgroup' => 'blocks', + 'source' => 'Another Static Block', + 'version' => '1', +)) +->values(array( + 'lid' => '527', + 'location' => 'block:2:body', + 'textgroup' => 'blocks', + 'source' => '

                          My second custom block body

                          ', + 'version' => '1', +)) +->values(array( + 'lid' => '528', + 'location' => 'vocabulary:4:name', + 'textgroup' => 'taxonomy', + 'source' => 'Tags', + 'version' => '1', +)) +->values(array( + 'lid' => '529', + 'location' => 'vocabulary:1:name', + 'textgroup' => 'taxonomy', + 'source' => 'vocabulary 1 (i=0)', + 'version' => '1', +)) +->values(array( + 'lid' => '530', + 'location' => 'vocabulary:2:name', + 'textgroup' => 'taxonomy', + 'source' => 'vocabulary 2 (i=1)', + 'version' => '1', +)) +->values(array( + 'lid' => '531', + 'location' => 'vocabulary:3:name', + 'textgroup' => 'taxonomy', + 'source' => 'vocabulary 3 (i=2)', + 'version' => '1', +)) +->values(array( + 'lid' => '532', + 'location' => 'vocabulary:5:name', + 'textgroup' => 'taxonomy', + 'source' => 'vocabulary name much longer than thirty two characters', + 'version' => '1', +)) +->values(array( + 'lid' => '533', + 'location' => 'type:article:name', + 'textgroup' => 'nodetype', + 'source' => 'Article', + 'version' => '1', +)) +->values(array( + 'lid' => '534', + 'location' => 'type:article:title', + 'textgroup' => 'nodetype', + 'source' => 'Title', + 'version' => '1', +)) +->values(array( + 'lid' => '535', + 'location' => 'type:article:body', + 'textgroup' => 'nodetype', + 'source' => 'Body', + 'version' => '1', +)) +->values(array( + 'lid' => '536', + 'location' => 'type:article:description', + 'textgroup' => 'nodetype', + 'source' => 'An article, content type.', + 'version' => '1', +)) +->values(array( + 'lid' => '537', + 'location' => 'type:company:name', + 'textgroup' => 'nodetype', + 'source' => 'Company', + 'version' => '1', +)) +->values(array( + 'lid' => '538', + 'location' => 'type:company:title', + 'textgroup' => 'nodetype', + 'source' => 'Name', + 'version' => '1', +)) +->values(array( + 'lid' => '539', + 'location' => 'type:company:body', + 'textgroup' => 'nodetype', + 'source' => 'Description', + 'version' => '1', +)) +->values(array( + 'lid' => '540', + 'location' => 'type:company:description', + 'textgroup' => 'nodetype', + 'source' => 'Company node type', + 'version' => '1', +)) +->values(array( + 'lid' => '541', + 'location' => 'type:employee:name', + 'textgroup' => 'nodetype', + 'source' => 'Employee', + 'version' => '1', +)) +->values(array( + 'lid' => '542', + 'location' => 'type:employee:title', + 'textgroup' => 'nodetype', + 'source' => 'Name', + 'version' => '1', +)) +->values(array( + 'lid' => '543', + 'location' => 'type:employee:body', + 'textgroup' => 'nodetype', + 'source' => 'Bio', + 'version' => '1', +)) +->values(array( + 'lid' => '544', + 'location' => 'type:employee:description', + 'textgroup' => 'nodetype', + 'source' => 'Employee node type', + 'version' => '1', +)) +->values(array( + 'lid' => '545', + 'location' => 'type:sponsor:name', + 'textgroup' => 'nodetype', + 'source' => 'Sponsor', + 'version' => '1', +)) +->values(array( + 'lid' => '546', + 'location' => 'type:sponsor:title', + 'textgroup' => 'nodetype', + 'source' => 'Name', + 'version' => '1', +)) +->values(array( + 'lid' => '547', + 'location' => 'type:sponsor:body', + 'textgroup' => 'nodetype', + 'source' => 'Body', + 'version' => '1', +)) +->values(array( + 'lid' => '548', + 'location' => 'type:sponsor:description', + 'textgroup' => 'nodetype', + 'source' => 'Sponsor node type', + 'version' => '1', +)) +->values(array( + 'lid' => '549', + 'location' => 'type:story:name', + 'textgroup' => 'nodetype', + 'source' => 'Story', + 'version' => '1', +)) +->values(array( + 'lid' => '550', + 'location' => 'type:story:title', + 'textgroup' => 'nodetype', + 'source' => 'Title', + 'version' => '1', +)) +->values(array( + 'lid' => '551', + 'location' => 'type:story:body', + 'textgroup' => 'nodetype', + 'source' => 'Body', + 'version' => '1', +)) +->values(array( + 'lid' => '552', + 'location' => 'type:story:description', + 'textgroup' => 'nodetype', + 'source' => "A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments.", + 'version' => '1', +)) +->values(array( + 'lid' => '553', + 'location' => 'type:test_event:name', + 'textgroup' => 'nodetype', + 'source' => 'Migrate test event', + 'version' => '1', +)) +->values(array( + 'lid' => '554', + 'location' => 'type:test_event:title', + 'textgroup' => 'nodetype', + 'source' => 'Event Name', + 'version' => '1', +)) +->values(array( + 'lid' => '555', + 'location' => 'type:test_event:body', + 'textgroup' => 'nodetype', + 'source' => 'Body', + 'version' => '1', +)) +->values(array( + 'lid' => '556', + 'location' => 'type:test_event:description', + 'textgroup' => 'nodetype', + 'source' => 'test event description here', + 'version' => '1', +)) +->values(array( + 'lid' => '558', + 'location' => 'type:test_page:name', + 'textgroup' => 'nodetype', + 'source' => 'Migrate test page', + 'version' => '1', +)) +->values(array( + 'lid' => '559', + 'location' => 'type:test_page:title', + 'textgroup' => 'nodetype', + 'source' => 'Title', + 'version' => '1', +)) +->values(array( + 'lid' => '560', + 'location' => 'type:test_page:body', + 'textgroup' => 'nodetype', + 'source' => 'This is the body field label', + 'version' => '1', +)) +->values(array( + 'lid' => '561', + 'location' => 'type:test_page:description', + 'textgroup' => 'nodetype', + 'source' => "A page, similar in form to a story, is a simple method for creating and displaying information that rarely changes, such as an \"About us\" section of a website. By default, a page entry does not allow visitor comments and is not featured on the site's initial home page.", + 'version' => '1', +)) +->values(array( + 'lid' => '562', + 'location' => 'type:test_planet:name', + 'textgroup' => 'nodetype', + 'source' => 'Migrate test planet', + 'version' => '1', +)) +->values(array( + 'lid' => '563', + 'location' => 'type:test_planet:title', + 'textgroup' => 'nodetype', + 'source' => 'Title', + 'version' => '1', +)) +->values(array( + 'lid' => '564', + 'location' => 'type:test_planet:body', + 'textgroup' => 'nodetype', + 'source' => 'Body', + 'version' => '1', +)) +->values(array( + 'lid' => '565', + 'location' => 'type:test_planet:description', + 'textgroup' => 'nodetype', + 'source' => "A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments.", + 'version' => '1', +)) +->values(array( + 'lid' => '566', + 'location' => 'type:test_story:name', + 'textgroup' => 'nodetype', + 'source' => 'Migrate test story', + 'version' => '1', +)) +->values(array( + 'lid' => '567', + 'location' => 'type:test_story:title', + 'textgroup' => 'nodetype', + 'source' => 'Title', + 'version' => '1', +)) +->values(array( + 'lid' => '568', + 'location' => 'type:test_story:body', + 'textgroup' => 'nodetype', + 'source' => 'Body', + 'version' => '1', +)) +->values(array( + 'lid' => '569', + 'location' => 'type:test_story:description', + 'textgroup' => 'nodetype', + 'source' => "A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments.", + 'version' => '1', +)) +->values(array( + 'lid' => '570', + 'location' => 'field:story-field_test_exclude_unset:widget_label', + 'textgroup' => 'cck', + 'source' => 'Text Field', + 'version' => '1', +)) +->values(array( + 'lid' => '571', + 'location' => 'field:story-field_test_exclude_unset:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example text field without exclude.', + 'version' => '1', +)) +->values(array( + 'lid' => '572', + 'location' => 'field:story-field_test_two:widget_label', + 'textgroup' => 'cck', + 'source' => 'Integer Field', + 'version' => '1', +)) +->values(array( + 'lid' => '573', + 'location' => 'field:story-field_test_two:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example integer field.', + 'version' => '1', +)) +->values(array( + 'lid' => '574', + 'location' => 'field:story-field_test:widget_label', + 'textgroup' => 'cck', + 'source' => 'Text Field', + 'version' => '1', +)) +->values(array( + 'lid' => '575', + 'location' => 'field:story-field_test:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example text field.', + 'version' => '1', +)) +->values(array( + 'lid' => '576', + 'location' => 'field:story-field_test_three:widget_label', + 'textgroup' => 'cck', + 'source' => 'Decimal Field', + 'version' => '1', +)) +->values(array( + 'lid' => '577', + 'location' => 'field:story-field_test_three:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example decimal field.', + 'version' => '1', +)) +->values(array( + 'lid' => '578', + 'location' => 'field:story-field_test_four:widget_label', + 'textgroup' => 'cck', + 'source' => 'Float Field', + 'version' => '1', +)) +->values(array( + 'lid' => '579', + 'location' => 'field:story-field_test_four:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example float field.', + 'version' => '1', +)) +->values(array( + 'lid' => '580', + 'location' => 'field:story-field_test_identical1:widget_label', + 'textgroup' => 'cck', + 'source' => 'Integer Field', + 'version' => '1', +)) +->values(array( + 'lid' => '581', + 'location' => 'field:story-field_test_identical1:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example integer field.', + 'version' => '1', +)) +->values(array( + 'lid' => '582', + 'location' => 'field:story-field_test_identical2:widget_label', + 'textgroup' => 'cck', + 'source' => 'Integer Field', + 'version' => '1', +)) +->values(array( + 'lid' => '583', + 'location' => 'field:story-field_test_identical2:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example integer field.', + 'version' => '1', +)) +->values(array( + 'lid' => '584', + 'location' => 'field:story-field_test_email:widget_label', + 'textgroup' => 'cck', + 'source' => 'Email Field', + 'version' => '1', +)) +->values(array( + 'lid' => '585', + 'location' => 'field:story-field_test_email:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example email field.', + 'version' => '1', +)) +->values(array( + 'lid' => '586', + 'location' => 'field:story-field_test_link:widget_label', + 'textgroup' => 'cck', + 'source' => 'Link Field', + 'version' => '1', +)) +->values(array( + 'lid' => '587', + 'location' => 'field:story-field_test_link:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example link field.', + 'version' => '1', +)) +->values(array( + 'lid' => '588', + 'location' => 'field:story-field_test_filefield:widget_label', + 'textgroup' => 'cck', + 'source' => 'File Field', + 'version' => '1', +)) +->values(array( + 'lid' => '589', + 'location' => 'field:story-field_test_filefield:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example image field.', + 'version' => '1', +)) +->values(array( + 'lid' => '590', + 'location' => 'field:story-field_test_imagefield:widget_label', + 'textgroup' => 'cck', + 'source' => 'Image Field', + 'version' => '1', +)) +->values(array( + 'lid' => '591', + 'location' => 'field:story-field_test_imagefield:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example image field.', + 'version' => '1', +)) +->values(array( + 'lid' => '592', + 'location' => 'field:story-field_test_date:widget_label', + 'textgroup' => 'cck', + 'source' => 'Date Field', + 'version' => '1', +)) +->values(array( + 'lid' => '593', + 'location' => 'field:story-field_test_date:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example date field.', + 'version' => '1', +)) +->values(array( + 'lid' => '594', + 'location' => 'field:story-field_test_datestamp:widget_label', + 'textgroup' => 'cck', + 'source' => 'Date Stamp Field', + 'version' => '1', +)) +->values(array( + 'lid' => '595', + 'location' => 'field:story-field_test_datestamp:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example date stamp field.', + 'version' => '1', +)) +->values(array( + 'lid' => '596', + 'location' => 'field:story-field_test_datetime:widget_label', + 'textgroup' => 'cck', + 'source' => 'Datetime Field', + 'version' => '1', +)) +->values(array( + 'lid' => '597', + 'location' => 'field:story-field_test_datetime:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example datetime field.', + 'version' => '1', +)) +->values(array( + 'lid' => '598', + 'location' => 'field:story-field_test_phone:widget_label', + 'textgroup' => 'cck', + 'source' => 'Phone Field', + 'version' => '1', +)) +->values(array( + 'lid' => '599', + 'location' => 'field:story-field_test_phone:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example phone field.', + 'version' => '1', +)) +->values(array( + 'lid' => '600', + 'location' => 'field:story-field_test_decimal_radio_buttons:widget_label', + 'textgroup' => 'cck', + 'source' => 'Decimal Radio Buttons Field', + 'version' => '1', +)) +->values(array( + 'lid' => '601', + 'location' => 'field:story-field_test_decimal_radio_buttons:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example decimal field using radio buttons.', + 'version' => '1', +)) +->values(array( + 'lid' => '602', + 'location' => 'field:field_test_decimal_radio_buttons:option_1.2', + 'textgroup' => 'cck', + 'source' => '1.2', + 'version' => '1', +)) +->values(array( + 'lid' => '603', + 'location' => 'field:field_test_decimal_radio_buttons:option_2.1', + 'textgroup' => 'cck', + 'source' => '2.1', + 'version' => '1', +)) +->values(array( + 'lid' => '604', + 'location' => 'field:story-field_test_float_single_checkbox:widget_label', + 'textgroup' => 'cck', + 'source' => 'Float Single Checkbox Field', + 'version' => '1', +)) +->values(array( + 'lid' => '605', + 'location' => 'field:story-field_test_float_single_checkbox:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example float field using a single on/off checkbox.', + 'version' => '1', +)) +->values(array( + 'lid' => '606', + 'location' => 'field:field_test_float_single_checkbox:option_3.142', + 'textgroup' => 'cck', + 'source' => '3.142', + 'version' => '1', +)) +->values(array( + 'lid' => '607', + 'location' => 'field:field_test_float_single_checkbox:option_1.234', + 'textgroup' => 'cck', + 'source' => '1.234', + 'version' => '1', +)) +->values(array( + 'lid' => '608', + 'location' => 'field:story-field_test_integer_selectlist:widget_label', + 'textgroup' => 'cck', + 'source' => 'Integer Select List Field', + 'version' => '1', +)) +->values(array( + 'lid' => '609', + 'location' => 'field:story-field_test_integer_selectlist:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example integer field using a select list.', + 'version' => '1', +)) +->values(array( + 'lid' => '610', + 'location' => 'field:field_test_integer_selectlist:option_1234', + 'textgroup' => 'cck', + 'source' => '1234', + 'version' => '1', +)) +->values(array( + 'lid' => '611', + 'location' => 'field:field_test_integer_selectlist:option_2341', + 'textgroup' => 'cck', + 'source' => '2341', + 'version' => '1', +)) +->values(array( + 'lid' => '612', + 'location' => 'field:field_test_integer_selectlist:option_3412', + 'textgroup' => 'cck', + 'source' => '3412', + 'version' => '1', +)) +->values(array( + 'lid' => '613', + 'location' => 'field:field_test_integer_selectlist:option_4123', + 'textgroup' => 'cck', + 'source' => '4123', + 'version' => '1', +)) +->values(array( + 'lid' => '614', + 'location' => 'field:story-field_test_text_single_checkbox:widget_label', + 'textgroup' => 'cck', + 'source' => 'Text Single Checkbox Field', + 'version' => '1', +)) +->values(array( + 'lid' => '615', + 'location' => 'field:story-field_test_text_single_checkbox:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example text field using a single on/off checkbox.', + 'version' => '1', +)) +->values(array( + 'lid' => '616', + 'location' => 'field:field_test_text_single_checkbox:option_0', + 'textgroup' => 'cck', + 'source' => 'Hello', + 'version' => '1', +)) +->values(array( + 'lid' => '617', + 'location' => 'field:field_test_text_single_checkbox:option_1', + 'textgroup' => 'cck', + 'source' => 'Goodbye', + 'version' => '1', +)) +->values(array( + 'lid' => '618', + 'location' => 'field:story-field_test_text_single_checkbox2:widget_label', + 'textgroup' => 'cck', + 'source' => 'Text Single Checkbox Field 2', + 'version' => '1', +)) +->values(array( + 'lid' => '619', + 'location' => 'field:story-field_test_text_single_checkbox2:widget_description', + 'textgroup' => 'cck', + 'source' => 'Checkbox that uses keys only and no label.', + 'version' => '1', +)) +->values(array( + 'lid' => '620', + 'location' => 'field:field_test_text_single_checkbox2:option_Off', + 'textgroup' => 'cck', + 'source' => 'Off', + 'version' => '1', +)) +->values(array( + 'lid' => '621', + 'location' => 'field:field_test_text_single_checkbox2:option_Hello', + 'textgroup' => 'cck', + 'source' => 'Hello', + 'version' => '1', +)) +->values(array( + 'lid' => '622', + 'location' => 'field:test_page-field_test:widget_label', + 'textgroup' => 'cck', + 'source' => 'Text Field', + 'version' => '1', +)) +->values(array( + 'lid' => '623', + 'location' => 'field:test_page-field_test:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example text field.', + 'version' => '1', +)) +->values(array( + 'lid' => '624', + 'location' => 'field:test_planet-field_multivalue:widget_label', + 'textgroup' => 'cck', + 'source' => 'Decimal Field', + 'version' => '1', +)) +->values(array( + 'lid' => '625', + 'location' => 'field:test_planet-field_multivalue:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example multi-valued decimal field.', + 'version' => '1', +)) +->values(array( + 'lid' => '626', + 'location' => 'field:test_planet-field_test_text_single_checkbox:widget_label', + 'textgroup' => 'cck', + 'source' => 'Text Single Checkbox Field', + 'version' => '1', +)) +->values(array( + 'lid' => '627', + 'location' => 'field:test_planet-field_test_text_single_checkbox:widget_description', + 'textgroup' => 'cck', + 'source' => 'An example text field using a single on/off checkbox.', + 'version' => '1', +)) +->values(array( + 'lid' => '628', + 'location' => 'misc/tableselect.js', + 'textgroup' => 'default', + 'source' => 'Select all rows in this table', + 'version' => 'none', +)) +->values(array( + 'lid' => '629', + 'location' => 'misc/tableselect.js', + 'textgroup' => 'default', + 'source' => 'Deselect all rows in this table', + 'version' => 'none', +)) +->values(array( + 'lid' => '630', + 'location' => 'sites/all/modules/filefield/filefield.js', + 'textgroup' => 'default', + 'source' => 'The selected file %filename cannot be uploaded. Only files with the following extensions are allowed: %extensions.', + 'version' => 'none', +)) +->values(array( + 'lid' => '631', + 'location' => 'misc/teaser.js', + 'textgroup' => 'default', + 'source' => 'Split summary at cursor', + 'version' => 'none', +)) +->values(array( + 'lid' => '632', + 'location' => 'misc/teaser.js', + 'textgroup' => 'default', + 'source' => 'Join summary', + 'version' => 'none', +)) +->values(array( + 'lid' => '633', + 'location' => 'item:140:title', + 'textgroup' => 'menu', + 'source' => 'Drupal.org', + 'version' => '1', +)) +->values(array( + 'lid' => '634', + 'location' => 'item:139:title', + 'textgroup' => 'menu', + 'source' => 'Test 2', + 'version' => '1', +)) +->values(array( + 'lid' => '635', + 'location' => 'item:139:description', + 'textgroup' => 'menu', + 'source' => 'Test menu link 2', + 'version' => '1', +)) +->values(array( + 'lid' => '636', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Site information', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '637', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Change basic site information, such as the site name, slogan, e-mail address, mission, front page and more.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '638', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'The name of this website.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '639', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'E-mail address', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '640', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => "The From address in automated e-mails sent during registration and new password requests, and other notifications. (Use an address ending in your site's domain to help prevent this e-mail being flagged as spam.)", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '641', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Slogan', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '642', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => "Your site's motto, tag line, or catchphrase (often displayed alongside the title of the site).", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '643', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Mission', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '644', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => "Your site's mission or focus statement (often prominently displayed on the front page).", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '645', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Footer message', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '646', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'This text will be displayed at the bottom of each page. Useful for adding a copyright notice to your pages.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '647', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Anonymous user', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '648', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Anonymous', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '649', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'The name used to indicate anonymous users.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '650', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Default front page', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '651', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'The home page displays content from this relative URL. If unsure, specify "node".', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '652', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Save configuration', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '653', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Reset to defaults', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '654', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'This is a multilingual variable.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '655', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Left sidebar', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '656', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Right sidebar', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '657', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Header', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '658', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Footer', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '659', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'RSS feed', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '660', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => '', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '661', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Administer', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '662', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Compact mode', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '663', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Content management', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '664', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => "Manage your site's content.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '665', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Reports', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '666', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'View reports from system logs and other status information.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '667', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Site building', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '668', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Control how your site looks and feels.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '669', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Site configuration', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '670', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Adjust basic site configuration options.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '671', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Actions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '672', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Manage the actions defined for your site.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '673', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Administration theme', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '674', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Settings for how your administrative pages should look.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '675', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Clean URLs', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '676', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Enable or disable clean URLs for your site.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '677', + 'location' => 'date.module:39', + 'textgroup' => 'default', + 'source' => 'Date and time', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '678', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => "Settings for how Drupal displays date and time, as well as the system's default timezone.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '679', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Error reporting', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '680', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Control how Drupal deals with errors including 403/404 errors as well as PHP error reporting.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '681', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'File system', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '682', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Tell Drupal where to store uploaded files and how they are accessed.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '683', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'File uploads', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '684', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Control how files may be attached to content.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '685', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Image toolkit', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '686', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Choose which image toolkit to use if you have installed optional toolkits.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '687', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Input formats', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '688', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Configure how content input by users is filtered, including allowed HTML tags. Also allows enabling of module-provided filters.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '689', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Logging and alerts', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '690', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => "Settings for logging and alerts modules. Various modules can route Drupal's system events to different destination, such as syslog, database, email, ...etc.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '691', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Performance', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '692', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '693', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Site maintenance', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '694', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Take the site off-line for maintenance or bring it back online.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '695', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Events', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '696', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Set up how your site handles events.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '697', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'CCK Email Contact Form Settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '698', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Administer flood control settings for email contact forms', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '699', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'ImageAPI', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '700', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Configure ImageAPI.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '701', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Languages', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '702', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Configure languages for content and the user interface.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '703', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Variables', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '704', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Edit and delete site variables.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '705', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'User management', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '706', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => "Manage your site's users, groups and access to site features.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '707', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Contact', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '708', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Log out', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '709', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'User account', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '710', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'User list', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '711', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Autocomplete taxonomy', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '712', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Compose tips', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '713', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Create content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '714', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Delete comment', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '715', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Edit comment', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '716', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'File download', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '717', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'User autocomplete', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '718', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'User timezone', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '719', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'My account', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '720', + 'location' => 'content_admin.inc:199', + 'textgroup' => 'default', + 'source' => 'Delete', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '721', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Feed aggregator', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '722', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Books', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '723', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Save string', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '724', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Node title autocomplete', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '725', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'View user profile.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '726', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => "Who's new", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '727', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Powered by Drupal, an open source content management system', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '728', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Home', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '729', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'The selected file %file could not be uploaded, because the destination %directory is not properly configured.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '730', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'French', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '731', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'The configuration options have been saved.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '732', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Long', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '733', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Medium', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '734', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'Short', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '735', + 'location' => '/?q=fr/admin/settings/site-information', + 'textgroup' => 'default', + 'source' => 'The e-mail address %mail is not valid.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '736', + 'location' => '/?q=fr/admin/settings/error-reporting', + 'textgroup' => 'default', + 'source' => 'Default 403 (access denied) page', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '737', + 'location' => '/?q=fr/admin/settings/error-reporting', + 'textgroup' => 'default', + 'source' => 'This page is displayed when the requested document is denied to the current user. If unsure, specify nothing.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '738', + 'location' => '/?q=fr/admin/settings/error-reporting', + 'textgroup' => 'default', + 'source' => 'Default 404 (not found) page', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '739', + 'location' => '/?q=fr/admin/settings/error-reporting', + 'textgroup' => 'default', + 'source' => 'This page is displayed when no other content matches the requested document. If unsure, specify nothing.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '740', + 'location' => '/?q=fr/admin/settings/error-reporting', + 'textgroup' => 'default', + 'source' => 'Write errors to the log', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '741', + 'location' => '/?q=fr/admin/settings/error-reporting', + 'textgroup' => 'default', + 'source' => 'Write errors to the log and to the screen', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '742', + 'location' => '/?q=fr/admin/settings/error-reporting', + 'textgroup' => 'default', + 'source' => 'Specify where Drupal, PHP and SQL errors are logged. While it is recommended that a site running in a production environment write errors to the log only, in a development or testing environment it may be helpful to write errors both to the log and to the screen.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '743', + 'location' => '/?q=fr/admin/user', + 'textgroup' => 'default', + 'source' => 'Access rules', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '744', + 'location' => '/?q=fr/admin/user', + 'textgroup' => 'default', + 'source' => 'List and create rules to disallow usernames, e-mail addresses, and IP addresses.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '745', + 'location' => '/?q=fr/admin/user', + 'textgroup' => 'default', + 'source' => 'Permissions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '746', + 'location' => '/?q=fr/admin/user', + 'textgroup' => 'default', + 'source' => 'Determine access to features by selecting permissions for roles.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '747', + 'location' => '/?q=fr/admin/user', + 'textgroup' => 'default', + 'source' => 'Profiles', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '748', + 'location' => '/?q=fr/admin/user', + 'textgroup' => 'default', + 'source' => 'Create customizable fields for your users.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '749', + 'location' => '/?q=fr/admin/user', + 'textgroup' => 'default', + 'source' => 'Roles', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '750', + 'location' => '/?q=fr/admin/user', + 'textgroup' => 'default', + 'source' => 'List, edit, or add user roles.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '751', + 'location' => '/?q=fr/admin/user', + 'textgroup' => 'default', + 'source' => 'User settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '752', + 'location' => '/?q=fr/admin/user', + 'textgroup' => 'default', + 'source' => 'Configure default behavior of users, including registration requirements, e-mails, and user pictures.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '753', + 'location' => '/?q=fr/admin/user', + 'textgroup' => 'default', + 'source' => 'Users', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '754', + 'location' => '/?q=fr/admin/user', + 'textgroup' => 'default', + 'source' => 'List, add, and edit users.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '755', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'User registration settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '756', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Public registrations', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '757', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Only site administrators can create new user accounts.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '758', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Visitors can create accounts and no administrator approval is required.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '759', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Visitors can create accounts but administrator approval is required.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '760', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Require e-mail verification when a visitor creates an account', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '761', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'If this box is checked, new users will be required to validate their e-mail address prior to logging into the site, and will be assigned a system-generated password. With it unchecked, users will be logged in immediately upon registering, and may select their own passwords during registration.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '762', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'User registration guidelines', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '763', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'This text is displayed at the top of the user registration form and is useful for helping or instructing your users.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '764', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'User e-mail settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '765', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Drupal sends emails whenever new users register on your site, and optionally, may also notify users after other account actions. Using a simple set of content templates, notification e-mails can be customized to fit the specific needs of your site.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '766', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Available variables are:', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '767', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Welcome, new user created by administrator', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '768', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Customize welcome e-mail messages sent to new member accounts created by an administrator.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '769', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Subject', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '770', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Body', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '771', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Welcome, no approval required', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '772', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Customize welcome e-mail messages sent to new members upon registering, when no administrator approval is required.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '773', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Welcome, awaiting administrator approval', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '774', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Customize welcome e-mail messages sent to new members upon registering, when administrative approval is required.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '775', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Password recovery email', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '776', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Customize e-mail messages sent to users who request a new password.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '777', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Account activation email', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '778', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Enable and customize e-mail messages sent to users upon account activation (when an administrator activates an account of a user who has already registered, on a site where administrative approval is required).', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '779', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Notify user when account is activated.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '780', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Account blocked email', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '781', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Enable and customize e-mail messages sent to users when their accounts are blocked.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '782', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Notify user when account is blocked.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '783', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Account deleted email', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '784', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Enable and customize e-mail messages sent to users when their accounts are deleted.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '785', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Notify user when account is deleted.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '786', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Signatures', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '787', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Signature support', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '788', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Disabled', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '789', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Enabled', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '790', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Pictures', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '791', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Picture support', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '792', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Picture image path', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '793', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Subdirectory in the directory %dir where pictures will be stored.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '794', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Default picture', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '795', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'URL of picture to display for users with no custom picture selected. Leave blank for none.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '796', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Picture maximum dimensions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '797', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Maximum dimensions for pictures, in pixels.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '798', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Picture maximum file size', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '799', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Maximum file size for pictures, in kB.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '800', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => 'Picture guidelines', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '801', + 'location' => '/?q=fr/admin/user/settings', + 'textgroup' => 'default', + 'source' => "This text is displayed at the picture upload form in addition to the default guidelines. It's useful for helping or instructing your users.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '802', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'The normal cache mode is suitable for most sites and does not cause any side effects. The aggressive cache mode causes Drupal to skip the loading (boot) and unloading (exit) of enabled modules when serving a cached page. This results in an additional performance boost but can cause unwanted side effects.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '803', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'The following enabled modules are incompatible with aggressive mode caching and will not function properly: %modules', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '804', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Page cache', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '805', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Enabling the page cache will offer a significant performance boost. Drupal can store and send compressed cached pages requested by anonymous users. By caching a web page, Drupal does not have to construct the page each time it is viewed.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '806', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Caching mode', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '807', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Normal (recommended for production sites, no side effects)', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '808', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Aggressive (experts only, possible side effects)', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '809', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => '0 sec', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '810', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => '1 min', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '811', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => '@count min', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '812', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => '1 hour', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '813', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => '@count hours', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '814', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => '1 day', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '815', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Minimum cache lifetime', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '816', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'On high-traffic sites, it may be necessary to enforce a minimum cache lifetime. The minimum cache lifetime is the minimum amount of time that will elapse before the cache is emptied and recreated, and is applied to both page and block caches. A larger minimum cache lifetime offers better performance, but users will not see new content for a longer period of time.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '817', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Page compression', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '818', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'By default, Drupal compresses the pages it caches in order to save bandwidth and improve download times. This option should be disabled when using a webserver that performs compression.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '819', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Block cache', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '820', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Enabling the block cache can offer a performance increase for all users by preventing blocks from being reconstructed on each page load. If the page cache is also enabled, performance increases from enabling the block cache will mainly benefit authenticated users.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '821', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Enabled (recommended)', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '822', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Note that block caching is inactive when modules defining content access restrictions are enabled.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '823', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Bandwidth optimizations', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '824', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => '

                          Drupal can automatically optimize external resources like CSS and JavaScript, which can reduce both the size and number of requests made to your website. CSS files can be aggregated and compressed into a single file, while JavaScript files are aggregated (but not compressed). These optional optimizations may reduce server load, bandwidth requirements, and page loading times.

                          These options are disabled if you have not set up your files directory, or if your download method is set to private.

                          ', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '825', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Optimize CSS files', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '826', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'This option can interfere with theme development and should only be enabled in a production environment.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '827', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Optimize JavaScript files', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '828', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'This option can interfere with module development and should only be enabled in a production environment.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '829', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Clear cached data', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '830', + 'location' => '/?q=fr/admin/settings/performance', + 'textgroup' => 'default', + 'source' => 'Caching data improves performance, but may cause problems while troubleshooting new modules, themes, or translations, if outdated information has been cached. To refresh all cached data on your site, click the button below. Warning: high-traffic sites will experience performance slowdowns while cached data is rebuilt.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '831', + 'location' => '/?q=fr/admin/settings/site-maintenance', + 'textgroup' => 'default', + 'source' => 'Site status', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '832', + 'location' => '/?q=fr/admin/settings/site-maintenance', + 'textgroup' => 'default', + 'source' => 'Online', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '833', + 'location' => '/?q=fr/admin/settings/site-maintenance', + 'textgroup' => 'default', + 'source' => 'Off-line', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '834', + 'location' => '/?q=fr/admin/settings/site-maintenance', + 'textgroup' => 'default', + 'source' => 'When set to "Online", all visitors will be able to browse your site normally. When set to "Off-line", only users with the "administer site configuration" permission will be able to access your site to perform maintenance; all other visitors will see the site off-line message configured below. Authorized users can log in during "Off-line" mode directly via the user login page.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '835', + 'location' => '/?q=fr/admin/settings/site-maintenance', + 'textgroup' => 'default', + 'source' => 'Site off-line message', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '836', + 'location' => '/?q=fr/admin/settings/site-maintenance', + 'textgroup' => 'default', + 'source' => '@site is currently under maintenance. We should be back shortly. Thank you for your patience.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '837', + 'location' => '/?q=fr/admin/settings/site-maintenance', + 'textgroup' => 'default', + 'source' => 'Message to show visitors when the site is in off-line mode.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '838', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'Left to right', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '839', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'English name', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '840', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'Native name', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '841', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'Direction', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '842', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'Language negotiation', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '843', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'List', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '844', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'Options', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '845', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'Configure extended options for multilingual content and translations.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '846', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'Multilingual variables.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '847', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'Add language', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '848', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'Multilingual system', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '849', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'String translation', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '850', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => "This page provides an overview of your site's enabled languages. If multiple languages are available and enabled, the text on your site interface may be translated, registered users may select their preferred language on the My account page, and site authors may indicate a specific language when creating posts. The site's default language is used for anonymous visitors and for users who have not selected a preferred language.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '851', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'For each language available on the site, use the edit link to configure language details, including name, an optional language-specific path or domain, and whether the language is natively presented either left-to-right or right-to-left. These languages also appear in the Language selection when creating a post of a content type with multilingual support.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '852', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'Use the add language page to enable additional languages (and automatically import files from a translation package, if available), the translate interface page to locate strings for manual translation, or the import page to add translations from individual .po files. A number of contributed translation packages containing .po files are available on the Drupal.org translations page.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '853', + 'location' => '/?q=fr/admin/settings/language', + 'textgroup' => 'default', + 'source' => 'Warning: Changing the default language may have unwanted effects on string translations. Read more about String translation', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '854', + 'location' => '/?q=fr/admin/settings/filters', + 'textgroup' => 'default', + 'source' => 'anonymous user', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '855', + 'location' => '/?q=fr/admin/settings/filters', + 'textgroup' => 'default', + 'source' => 'authenticated user', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '856', + 'location' => '/?q=fr/admin/settings/filters', + 'textgroup' => 'default', + 'source' => 'All roles may use default format', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '857', + 'location' => 'content_admin.inc:250', + 'textgroup' => 'default', + 'source' => 'configure', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '858', + 'location' => '/?q=fr/admin/settings/filters', + 'textgroup' => 'default', + 'source' => 'No roles may use this format', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '859', + 'location' => '/?q=fr/admin/settings/filters', + 'textgroup' => 'default', + 'source' => 'Set default format', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '860', + 'location' => '/?q=fr/admin/settings/filters', + 'textgroup' => 'default', + 'source' => 'Delete input format', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '861', + 'location' => '/?q=fr/admin/settings/filters', + 'textgroup' => 'default', + 'source' => 'Add input format', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '862', + 'location' => '/?q=fr/admin/settings/filters', + 'textgroup' => 'default', + 'source' => 'Input formats define a way of processing user-supplied text in Drupal. Each input format uses filters to manipulate text, and most input formats apply several different filters to text, in a specific order. Each filter is designed to accomplish a specific purpose, and generally either removes elements from or adds elements to text before it is displayed. Users can choose between the available input formats when submitting content.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '863', + 'location' => '/?q=fr/admin/settings/filters', + 'textgroup' => 'default', + 'source' => 'Use the list below to configure which input formats are available to which roles, as well as choose a default input format (used for imported content, for example). The default format is always available to users. All input formats are available to users in a role with the "administer filters" permission.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '864', + 'location' => '/?q=fr/admin/settings/filters', + 'textgroup' => 'default', + 'source' => 'After updating your Input formats do not forget to review the list of formats allowed for string translations on the configure translatable strings page.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '865', + 'location' => '/?q=fr/admin/settings/imageapi', + 'textgroup' => 'default', + 'source' => 'There are no image toolkit modules enabled. Toolkit modules can be enabled from the module configuration page.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '866', + 'location' => '/?q=fr/admin/settings/image-toolkit', + 'textgroup' => 'default', + 'source' => 'GD2 image manipulation toolkit', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '867', + 'location' => '/?q=fr/admin/settings/image-toolkit', + 'textgroup' => 'default', + 'source' => 'The GD toolkit is installed and working properly.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '868', + 'location' => '/?q=fr/admin/settings/image-toolkit', + 'textgroup' => 'default', + 'source' => 'JPEG quality', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '869', + 'location' => '/?q=fr/admin/settings/image-toolkit', + 'textgroup' => 'default', + 'source' => 'Define the image quality for JPEG manipulations. Ranges from 0 to 100. Higher values mean better image quality but bigger files.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '870', + 'location' => '/?q=fr/admin/settings/image-toolkit', + 'textgroup' => 'default', + 'source' => '%', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '871', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'General settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '872', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'Maximum resolution for uploaded images', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '873', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'The maximum allowed image size (e.g. 640x480). Set to 0 for no restriction. If an image toolkit is installed, files exceeding this value will be scaled down to fit.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '874', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'WIDTHxHEIGHT', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '875', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'List files by default', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '876', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'No', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '877', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'Yes', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '878', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'Display attached files when viewing a post.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '879', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'Default permitted file extensions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '880', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'Default extensions that users can upload. Separate extensions with a space and do not include the leading dot.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '881', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'Default maximum file size per upload', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '882', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'The default maximum file size a user can upload. If an image is uploaded and a maximum resolution is set, the size will be checked after the file has been resized.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '883', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'MB', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '884', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'Default total file size per user', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '885', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'The default maximum size of all files a user can have on the site.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '886', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'KB', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '887', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => '@size @suffix', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '888', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'Your PHP settings limit the maximum file size per upload to %size.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '889', + 'location' => '/?q=fr/admin/settings/uploads', + 'textgroup' => 'default', + 'source' => 'Users with the upload files permission can upload attachments. Users with the view uploaded files permission can view uploaded attachments. You can choose which post types can take attachments on the content types settings page.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '890', + 'location' => '/?q=fr/admin/settings/file-system', + 'textgroup' => 'default', + 'source' => 'File system path', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '891', + 'location' => '/?q=fr/admin/settings/file-system', + 'textgroup' => 'default', + 'source' => 'A file system path where the files will be stored. This directory must exist and be writable by Drupal. If the download method is set to public, this directory must be relative to the Drupal installation directory and be accessible over the web. If the download method is set to private, this directory should not be accessible over the web. Changing this location will modify all download paths and may cause unexpected problems on an existing site.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '892', + 'location' => '/?q=fr/admin/settings/file-system', + 'textgroup' => 'default', + 'source' => 'Temporary directory', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '893', + 'location' => '/?q=fr/admin/settings/file-system', + 'textgroup' => 'default', + 'source' => 'A file system path where uploaded files will be stored during previews.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '894', + 'location' => '/?q=fr/admin/settings/file-system', + 'textgroup' => 'default', + 'source' => 'Download method', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '895', + 'location' => '/?q=fr/admin/settings/file-system', + 'textgroup' => 'default', + 'source' => 'Public - files are available using HTTP directly.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '896', + 'location' => '/?q=fr/admin/settings/file-system', + 'textgroup' => 'default', + 'source' => 'Private - files are transferred by Drupal.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '897', + 'location' => '/?q=fr/admin/settings/file-system', + 'textgroup' => 'default', + 'source' => 'Choose the Public download method unless you wish to enforce fine-grained access controls over file downloads. Changing the download method will modify all download paths and may cause unexpected problems on an existing site.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '898', + 'location' => '/?q=fr/admin/settings/file-system', + 'textgroup' => 'default', + 'source' => 'The directory %directory does not exist.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '899', + 'location' => '/?q=fr/admin/settings/event', + 'textgroup' => 'default', + 'source' => 'Event overview', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '900', + 'location' => '/?q=fr/admin/settings/event', + 'textgroup' => 'default', + 'source' => 'Change how event summary information is displayed.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '901', + 'location' => '/?q=fr/admin/settings/event', + 'textgroup' => 'default', + 'source' => 'Timezone handling', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '902', + 'location' => '/?q=fr/admin/settings/event', + 'textgroup' => 'default', + 'source' => 'Change how timezone information is saved and displayed.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '903', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => 'Event time zone input', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '904', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => 'Use the sitewide time zone', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '905', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => 'Use the time zone of the user editing or creating the event', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '906', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => 'Allow users to set event time zones', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '907', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => 'date/time settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '908', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => "Events are saved with a time zone value. This setting allows you to determine how the time zone is determined when creating or editing an event. You must have 'Configurable time zones' enabled in the !url before you can enable user's time zones for this feature.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '909', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => 'Event time zone display', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '910', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => "Use the event's time zone", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '911', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => "Events are saved with a time zone value. This setting allows you to determine if the event's time zone, the sitewide time zone, or the user's personal time zone setting is used to display the time for an event. You must have 'Configurable time zones' enabled in the !url before you can enable user's time zones for this feature.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '912', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => 'Time notation preference', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '913', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => '24h', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '914', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => '12h', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '915', + 'location' => '/?q=fr/admin/settings/event/timezone', + 'textgroup' => 'default', + 'source' => 'The time notation system used for entering event times.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '916', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Upcoming event block limit', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '917', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Limit the amount of events displayed in the upcoming events block by this amount.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '918', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Default overview', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '919', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Day', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '920', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Week', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '921', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Month', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '922', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Table', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '923', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'The default event view to display when no format is specifically requested. This is also the view that will be displayed from the block calendar links.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '924', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Table view default period', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '925', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'here', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '926', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'The default number of days to display in the table view. You can specify a different number of days in the url. More info on the event url format !link', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '927', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Taxonomy filter controls', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '928', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Show taxonomy filter control on calendar views', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '929', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Only show taxonomy filter control when taxonomy filter view is requested', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '930', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Never show taxonomy filter control', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '931', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Content type filter controls', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '932', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Show content type filter control on calendar views', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '933', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Only show content type filter control when content type filter view is requested', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '934', + 'location' => '/?q=fr/admin/settings/event/overview', + 'textgroup' => 'default', + 'source' => 'Never show content type filter control', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '935', + 'location' => '/?q=fr/admin/settings/email', + 'textgroup' => 'default', + 'source' => 'Hourly threshold for a CCK Email contact form', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '936', + 'location' => '/?q=fr/admin/settings/email', + 'textgroup' => 'default', + 'source' => 'The maximum number of contact form submissions a user can perform per hour.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '937', + 'location' => '/?q=fr/admin/settings/admin', + 'textgroup' => 'default', + 'source' => 'System default', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '938', + 'location' => '/?q=fr/admin/settings/admin', + 'textgroup' => 'default', + 'source' => 'Choose which theme the administration pages should display in. If you choose "System default" the administration pages will use the same theme as the rest of the site.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '939', + 'location' => '/?q=fr/admin/settings/admin', + 'textgroup' => 'default', + 'source' => 'Use administration theme for content editing', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '940', + 'location' => '/?q=fr/admin/settings/admin', + 'textgroup' => 'default', + 'source' => 'Use the administration theme when editing existing posts or creating new ones.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '941', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Publish comment', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '942', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Unpublish comment', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '943', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Unpublish comment containing keyword(s)', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '944', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Publish post', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '945', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Unpublish post', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '946', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Make post sticky', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '947', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Make post unsticky', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '948', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Promote post to front page', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '949', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Remove post from front page', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '950', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Change the author of a post', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '951', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Save post', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '952', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Unpublish post containing keyword(s)', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '953', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Display a message to the user', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '954', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Send e-mail', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '955', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Redirect to URL', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '956', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Block current user', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '957', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Ban IP address of current user', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '958', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => "ImageCache: Flush ALL presets for this node's filefield images", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '959', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => "ImageCache: Generate ALL presets for this node's filefield images", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '960', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => "ImageCache: Generate configured preset(s) for this node's filefield images", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '961', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Choose an advanced action', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '962', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Action type', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '963', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => '« first', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '964', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => '‹ previous', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '965', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'next ›', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '966', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'last »', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '967', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Actions available to Drupal:', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '968', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'sort by @s', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '969', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'sort icon', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '970', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'sort descending', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '971', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Make a new advanced action available', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '972', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Create', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '973', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Configure an advanced action', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '974', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Remove orphans', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '975', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Manage actions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '976', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'Actions are individual tasks that the system can do, such as unpublishing a piece of content or banning a user. Modules, such as the trigger module, can fire these actions when certain system events happen; for example, when a new post is added or when a user logs in. Modules may also provide additional actions.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '977', + 'location' => '/?q=fr/admin/settings/actions', + 'textgroup' => 'default', + 'source' => 'There are two types of actions: simple and advanced. Simple actions do not require any additional configuration, and are listed here automatically. Advanced actions can do more than simple actions; for example, send an e-mail to a specified address, or check for certain words within a piece of content. These actions need to be created and configured first before they may be used. To create an advanced action, select the action from the drop-down below and click the Create button.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '978', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Create new account', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '979', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'role', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '980', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => '@module module', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '981', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'access news feeds', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '982', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer news feeds', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '983', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer blocks', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '984', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'use PHP for block visibility', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '985', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'access printer-friendly version', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '986', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'add content to books', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '987', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer book outlines', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '988', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'create new books', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '989', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'access comments', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '990', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer comments', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '991', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'post comments', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '992', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'post comments without approval', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '993', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'access site-wide contact form', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '994', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer site-wide contact form', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '995', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer filters', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '996', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer languages', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '997', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'translate interface', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '998', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer menu', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '999', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'access content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1000', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer content types', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1001', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer nodes', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1002', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'create article content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1003', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'create company content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1004', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'create employee content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1005', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'create sponsor content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1006', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'create story content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1007', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'create test_event content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1008', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'create test_page content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1009', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'create test_planet content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1010', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'create test_story content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1011', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete any article content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1012', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete any company content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1013', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete any employee content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1014', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete any sponsor content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1015', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete any story content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1016', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete any test_event content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1017', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete any test_page content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1018', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete any test_planet content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1019', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete any test_story content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1020', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete own article content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1021', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete own company content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1022', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete own employee content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1023', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete own sponsor content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1024', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete own story content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1025', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete own test_event content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1026', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete own test_page content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1027', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete own test_planet content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1028', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete own test_story content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1029', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'delete revisions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1030', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit any article content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1031', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit any company content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1032', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit any employee content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1033', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit any sponsor content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1034', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit any story content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1035', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit any test_event content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1036', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit any test_page content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1037', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit any test_planet content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1038', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit any test_story content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1039', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit own article content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1040', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit own company content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1041', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit own employee content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1042', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit own sponsor content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1043', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit own story content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1044', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit own test_event content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1045', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit own test_page content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1046', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit own test_planet content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1047', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'edit own test_story content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1048', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'revert revisions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1049', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'view revisions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1050', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer url aliases', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1051', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'create url aliases', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1052', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'access administration pages', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1053', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'access site reports', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1054', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer actions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1055', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer files', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1056', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer site configuration', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1057', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'select different theme', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1058', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer taxonomy', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1059', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'translate content', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1060', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'upload files', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1061', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'view uploaded files', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1062', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'access user profiles', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1063', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer permissions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1064', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer users', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1065', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'change own username', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1066', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'view date repeats', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1067', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer imageapi', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1068', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer imagecache', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1069', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'flush imagecache', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1070', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'view imagecache big_blue_cheese', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1071', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'view imagecache slackjaw_boys', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1072', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer all languages', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1073', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'administer translations', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1074', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'permission', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1075', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'status', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1076', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'active', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1077', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'blocked', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1078', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Show only users where', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1079', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Filter', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1080', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'is', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1081', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Username', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1082', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Status', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1083', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Member for', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1084', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Last access', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1085', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Update options', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1086', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Unblock the selected users', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1087', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Block the selected users', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1088', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Delete the selected users', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1089', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Add a role to the selected users', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1090', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Remove a role from the selected users', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1091', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Update', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1092', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => '@count years', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1093', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => '@count weeks', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1094', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => '@time ago', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1095', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => '@count sec', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1096', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'sort ascending', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1097', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Add user', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1098', + 'location' => '/?q=fr/admin/user/user', + 'textgroup' => 'default', + 'source' => 'Drupal allows users to register, login, log out, maintain user profiles, etc. Users of the site may not use their own names to post content until they have signed up for a user account.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1099', + 'location' => '/?q=fr/admin/user/roles', + 'textgroup' => 'default', + 'source' => 'Add role', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1100', + 'location' => '/?q=fr/admin/user/roles', + 'textgroup' => 'default', + 'source' => 'edit permissions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1101', + 'location' => '/?q=fr/admin/user/roles', + 'textgroup' => 'default', + 'source' => 'locked', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1102', + 'location' => '/?q=fr/admin/user/roles', + 'textgroup' => 'default', + 'source' => 'edit role', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1103', + 'location' => '/?q=fr/admin/user/roles', + 'textgroup' => 'default', + 'source' => 'Edit role', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1104', + 'location' => '/?q=fr/admin/user/roles', + 'textgroup' => 'default', + 'source' => "

                          Roles allow you to fine tune the security and administration of Drupal. A role defines a group of users that have certain privileges as defined in user permissions. Examples of roles include: anonymous user, authenticated user, moderator, administrator and so on. In this area you will define the role names of the various roles. To delete a role choose \"edit\".

                          By default, Drupal comes with two user roles:

                          \n
                            \n
                          • Anonymous user: this role is used for users that don't have a user account or that are not authenticated.
                          • \n
                          • Authenticated user: this role is automatically granted to all logged in users.
                          • \n
                          ", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1105', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'Add new field', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1106', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'single-line textfield', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1107', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'multi-line textfield', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1108', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'checkbox', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1109', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'list selection', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1110', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'freeform list', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1111', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'URL', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1112', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'date', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1113', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'No fields in this category. If this category remains empty when saved, it will be removed.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1114', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'Title', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1115', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'Category', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1116', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'Delete field', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1117', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'Edit field', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1118', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => 'Profile category autocomplete', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1119', + 'location' => '/?q=fr/admin/user/profile', + 'textgroup' => 'default', + 'source' => "This page displays a list of the existing custom profile fields to be displayed on a user's My Account page. To provide structure, similar or related fields may be placed inside a category. To add a new category (or edit an existing one), edit a profile field and provide a new category name. To change the category of a field or the order of fields within a category, grab a drag-and-drop handle under the Title column and drag the field to a new location in the list. (Grab a handle by clicking and holding the mouse while hovering over a handle icon.) Remember that your changes will not be saved until you click the Save configuration button at the bottom of the page.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1120', + 'location' => '/?q=fr/admin/user/permissions', + 'textgroup' => 'default', + 'source' => 'Save permissions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1121', + 'location' => '/?q=fr/admin/user/permissions', + 'textgroup' => 'default', + 'source' => 'Permission', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1122', + 'location' => '/?q=fr/admin/user/permissions', + 'textgroup' => 'default', + 'source' => 'Permissions let you control what users can do on your site. Each user role (defined on the user roles page) has its own set of permissions. For example, you could give users classified as "Administrators" permission to "administer nodes" but deny this power to ordinary, "authenticated" users. You can use permissions to reveal new features to privileged users (those with subscriptions, for example). Permissions also allow trusted users to share the administrative burden of running a busy site.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1123', + 'location' => '/?q=fr/admin/user/rules', + 'textgroup' => 'default', + 'source' => 'Access type', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1124', + 'location' => '/?q=fr/admin/user/rules', + 'textgroup' => 'default', + 'source' => 'Rule type', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1125', + 'location' => '/?q=fr/admin/user/rules', + 'textgroup' => 'default', + 'source' => 'Mask', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1126', + 'location' => '/?q=fr/admin/user/rules', + 'textgroup' => 'default', + 'source' => 'username', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1127', + 'location' => '/?q=fr/admin/user/rules', + 'textgroup' => 'default', + 'source' => 'e-mail', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1128', + 'location' => '/?q=fr/admin/user/rules', + 'textgroup' => 'default', + 'source' => 'host', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1129', + 'location' => '/?q=fr/admin/user/rules', + 'textgroup' => 'default', + 'source' => 'There are currently no access rules.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1130', + 'location' => '/?q=fr/admin/user/rules', + 'textgroup' => 'default', + 'source' => 'Delete rule', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1131', + 'location' => '/?q=fr/admin/user/rules', + 'textgroup' => 'default', + 'source' => 'Edit rule', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1132', + 'location' => '/?q=fr/admin/user/rules', + 'textgroup' => 'default', + 'source' => 'Add rule', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1133', + 'location' => '/?q=fr/admin/user/rules', + 'textgroup' => 'default', + 'source' => 'Check rules', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1134', + 'location' => '/?q=fr/admin/user/rules', + 'textgroup' => 'default', + 'source' => 'Set up username and e-mail address access rules for new and existing accounts (currently logged in accounts will not be logged out). If a username or e-mail address for an account matches any deny rule, but not an allow rule, then the account will not be allowed to be created or to log in. A host rule is effective for every page view, not just registrations.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1135', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'edit %title', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1136', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Field settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1137', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'The category the new field should be part of. Categories are used to group fields logically. An example category is "Personal information".', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1138', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'The title of the new field. The title will be shown to the user. An example title is "Favorite color".', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1139', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Form name', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1140', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => "The name of the field. The form name is not shown to the user but used internally in the HTML code and URLs.\nUnless you know what you are doing, it is highly recommended that you prefix the form name with profile_ to avoid name clashes with other fields. Spaces or any other special characters except dash (-) and underscore (_) are not allowed. An example name is \"profile_favorite_color\" or perhaps just \"profile_color\".", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1141', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Explanation', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1142', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'An optional explanation to go with the new field. The explanation will be shown to the user.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1143', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Selection options', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1144', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'A list of all options. Put each option on a separate line. Example options are "red", "blue", "green", etc.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1145', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Visibility', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1146', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Hidden profile field, only accessible by administrators, modules and themes.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1147', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Private field, content only available to privileged users.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1148', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Public field, content shown on profile page but not used on member list pages.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1149', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Public field, content shown on profile page and on member list pages.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1150', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Page title', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1151', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'To enable browsing this field by value, enter a title for the resulting page. The word %value will be substituted with the corresponding value. An example page title is "People whose favorite color is %value". This is only applicable for a public field.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1152', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'The weights define the order in which the form fields are shown. Lighter fields "float up" towards the top of the category.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1153', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Form will auto-complete while user is typing.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1154', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'For security, auto-complete will be disabled if the user does not have access to user profiles.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1155', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'The user must enter a value.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1156', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Visible in user registration form.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1157', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'Save field', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1158', + 'location' => '/?q=fr/admin/user/profile/edit/11', + 'textgroup' => 'default', + 'source' => 'The specified form name contains one or more illegal characters. Spaces or any other special characters except dash (-) and underscore (_) are not allowed.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1159', + 'location' => '/?q=fr/admin/settings/language/configure', + 'textgroup' => 'default', + 'source' => 'None.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1160', + 'location' => '/?q=fr/admin/settings/language/configure', + 'textgroup' => 'default', + 'source' => 'Path prefix only.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1161', + 'location' => '/?q=fr/admin/settings/language/configure', + 'textgroup' => 'default', + 'source' => 'Path prefix with language fallback.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1162', + 'location' => '/?q=fr/admin/settings/language/configure', + 'textgroup' => 'default', + 'source' => 'Domain name only.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1163', + 'location' => '/?q=fr/admin/settings/language/configure', + 'textgroup' => 'default', + 'source' => "Select the mechanism used to determine your site's presentation language. Modifying this setting may break all incoming URLs and should be used with caution in a production environment.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1164', + 'location' => '/?q=fr/admin/settings/language/configure', + 'textgroup' => 'default', + 'source' => 'Save settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1165', + 'location' => '/?q=fr/admin/settings/language/configure', + 'textgroup' => 'default', + 'source' => "Language negotiation settings determine the site's presentation language. Available options include:", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1166', + 'location' => '/?q=fr/admin/settings/language/configure', + 'textgroup' => 'default', + 'source' => 'None. The default language is used for site presentation, though users may (optionally) select a preferred language on the My Account page. (User language preferences will be used for site e-mails, if available.)', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1167', + 'location' => '/?q=fr/admin/settings/language/configure', + 'textgroup' => 'default', + 'source' => 'Path prefix only. The presentation language is determined by examining the path for a language code or other custom string that matches the path prefix (if any) specified for each language. If a suitable prefix is not identified, the default language is used. Example: "example.com/de/contact" sets presentation language to German based on the use of "de" within the path.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1168', + 'location' => '/?q=fr/admin/settings/language/configure', + 'textgroup' => 'default', + 'source' => "Path prefix with language fallback. The presentation language is determined by examining the path for a language code or other custom string that matches the path prefix (if any) specified for each language. If a suitable prefix is not identified, the display language is determined by the user's language preferences from the My Account page, or by the browser's language settings. If a presentation language cannot be determined, the default language is used.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1169', + 'location' => '/?q=fr/admin/settings/language/configure', + 'textgroup' => 'default', + 'source' => 'Domain name only. The presentation language is determined by examining the domain used to access the site, and comparing it to the language domain (if any) specified for each language. If a match is not identified, the default language is used. Example: "http://de.example.com/contact" sets presentation language to German based on the use of "http://de.example.com" in the domain.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1170', + 'location' => '/?q=fr/admin/settings/language/configure', + 'textgroup' => 'default', + 'source' => 'The path prefix or domain name for a language may be set by editing the available languages. In the absence of an appropriate match, the site is displayed in the default language.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1171', + 'location' => '/?q=fr/admin/settings/language/configure/strings', + 'textgroup' => 'default', + 'source' => 'Translatable input formats', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1172', + 'location' => '/?q=fr/admin/settings/language/configure/strings', + 'textgroup' => 'default', + 'source' => 'Only the strings that have the input formats selected will be allowed by the translation system. All the others will be deleted next time the strings are refreshed.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1173', + 'location' => '/?q=fr/admin/settings/language/configure/strings', + 'textgroup' => 'default', + 'source' => 'Built-in interface', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1174', + 'location' => '/?q=fr/admin/settings/language/configure/strings', + 'textgroup' => 'default', + 'source' => 'Blocks', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1175', + 'location' => '/?q=fr/admin/settings/language/configure/strings', + 'textgroup' => 'default', + 'source' => 'Menu', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1176', + 'location' => '/?q=fr/admin/settings/language/configure/strings', + 'textgroup' => 'default', + 'source' => 'Profile', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1177', + 'location' => '/?q=fr/admin/settings/language/configure/strings', + 'textgroup' => 'default', + 'source' => 'When translating user defined strings that have an Input format associated, translators will be able to edit the text before it is filtered which may be a security risk for some filters. An obvious example is when using the PHP filter but other filters may also be dangerous.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1178', + 'location' => '/?q=fr/admin/settings/language/configure/strings', + 'textgroup' => 'default', + 'source' => "As a general rule do not allow any filtered text to be translated unless the translators already have access to that Input format. However if you are doing all your translations through this site's translation UI or the Localization client, and never importing translations for other textgroups than default, filter access will be checked for translators on every translation page.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1179', + 'location' => '/?q=fr/admin/settings/language/configure/strings', + 'textgroup' => 'default', + 'source' => 'Important: After disallowing some Input format, use the refresh strings page so forbidden strings are deleted and not allowed anymore for translators.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1180', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'Content selection', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1181', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'Content selection mode', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1182', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'Current language and language neutral.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1183', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'Mixed current language (if available) or default language (if not) and language neutral.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1184', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'Only default language and language neutral.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1185', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'Only current language.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1186', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'All content. No language conditions apply.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1187', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'Determines which content to show depending on the current page language and the default language of the site.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1188', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'Content translation links', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1189', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'Hide content translation links', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1190', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'Hide the links to translations in content body and teasers. If you choose this option, switching language will only be available from the language switcher block.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1191', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'Switch interface for translating', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1192', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'Switch interface language to fit node language when creating or editing a translation. If not checked the interface language will be independent from node language.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1193', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'To set up multilingual options for vocabularies go to Taxonomy configuration page.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1194', + 'location' => '/?q=fr/admin/settings/language/i18n', + 'textgroup' => 'default', + 'source' => 'To enable multilingual support for specific content types go to configure content types.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1195', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Drupal', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1196', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Web server', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1197', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'PHP', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1198', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'PHP register globals', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1199', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'PHP memory limit', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1200', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'MySQL database', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1201', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Protected', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1202', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Configuration file', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1203', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Files directory', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1204', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Temporary files directory', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1205', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Not fully protected', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1206', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'See @url for information about the recommended .htaccess file which should be added to the %directory directory to help protect against arbitrary code execution.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1207', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'For more information, see the online handbook entry for configuring cron jobs.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1208', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Never run', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1209', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Cron has not run.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1210', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Cron maintenance tasks', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1211', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'You can run cron manually.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1212', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Not writable', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1213', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => "You may need to set the correct directory at the file system settings page or change the current directory's permissions so that it is writable.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1214', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Database updates', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1215', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Up to date', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1216', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Access to update.php', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1217', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Standard PHP', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1218', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'PHP Mbstring Extension', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1219', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Error', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1220', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Unicode library', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1221', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Not enabled', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1222', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Update notifications are not enabled. It is highly recommended that you enable the update status module from the module administration page in order to stay up-to-date on new releases. For more information please read the Update status handbook page.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1223', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Update notifications', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1224', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'set the site timezone name', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1225', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'The Date Timezone module requires you to !link.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1226', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Date Timezone requirements', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1227', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'MySQL database for event module', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1228', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Your server is capable of displaying file upload progress, but does not have the required libraries. It is recommended to install the PECL uploadprogress library (preferred) or to install APC.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1229', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Upload progress', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1230', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'ImageAPI Toolkit', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1231', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'No ImageAPI toolkits available', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1232', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'ImageAPI requires a Toolkit such as ImageAPI GD or ImageAPI ImageMagick to function. Go to !modules and enable one of them.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1233', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'ImageCache Directory', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1234', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => '%p is not a directory or is not readable by the webserver.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1235', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'One or more problems were detected with your Drupal installation. Check the status report for more information.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1236', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Comments', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1237', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'List and edit site comments and the comment moderation queue.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1238', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => "View, edit, and delete your site's content.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1239', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Manage posts by content type, including default status, front page promotion, etc.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1240', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Post settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1241', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Control posting behavior, such as teaser length, requiring previews before posting, and the number of posts on the front page.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1242', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'RSS publishing', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1243', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Configure the number of items per feed and whether feeds should be titles/teasers/full-text.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1244', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Manage tagging, categorization, and classification of your content.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1245', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => "Configure which content your site aggregates from other sites, how often it polls them, and how they're categorized.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1246', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => "Manage your site's book outlines.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1247', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Status report', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1248', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => "Get a status report about your site's operation and any detected problems.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1249', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => "Configure what block content appears in your site's sidebars and other regions.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1250', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Contact form', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1251', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Create a system contact form and set up categories for the form to use.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1252', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Menus', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1253', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => "Control your site's navigation menu, primary links and secondary links, as well as rename and reorganize menu items.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1254', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Modules', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1255', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Enable or disable add-on modules for your site.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1256', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Themes', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1257', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Change which theme your site uses or allows users to set.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1258', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'URL aliases', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1259', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => "Change your site's URL paths by aliasing them.", + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1260', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Translate interface', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1261', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Translate the built in interface and optionally other text.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1262', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Hide descriptions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1263', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Compress layout by hiding descriptions.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1264', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'By task', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1265', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'By module', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1266', + 'location' => '/?q=fr/admin', + 'textgroup' => 'default', + 'source' => 'Welcome to the administration section. Here you may control how your site functions.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1267', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Configure permissions', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1268', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Store a date in the database as an ISO date, recommended for historical or partial dates.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1269', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Store a date in the database as a timestamp, deprecated format to suppport legacy data.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1270', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Store a date in the database as a datetime field, recommended for complete dates and times that may need timezone conversion.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1271', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'File', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1272', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Store an arbitrary file.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1273', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Link', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1274', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Store a title, href, and attributes in the database to assemble a link.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1275', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - France', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1276', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Belgium', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1277', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Italy', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1278', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Greece', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1279', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Switzerland', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1280', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - US & Canada', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1281', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Costa Rica', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1282', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Panama', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1283', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Great Britain - United Kingdom', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1284', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Russia', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1285', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Ukraine - in Kiev', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1286', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Spain', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1287', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Australia', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1288', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Czech Republic', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1289', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Hungary', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1290', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Poland - mobiles only', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1291', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Netherland', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1292', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Sweden', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1293', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - South Africa', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1294', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Israel', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1295', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - New Zealand', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1296', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Brazil', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1297', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Chile', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1298', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - China', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1299', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Hong-Kong', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1300', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Macao', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1301', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - The Philippines', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1302', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Singapore', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1303', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Jordan', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1304', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Egypt', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1305', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - Pakistan', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1306', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Phone Numbers - International Phone Numbers per E.123', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1307', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Select List', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1308', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Select List with Repeat options', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1309', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Text Field with custom input format', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1310', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Text Field with Repeat options', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1311', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'As Time Ago', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1312', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Default email link', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1313', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Email contact form', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1314', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Email plain text', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1315', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'File Upload', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1316', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'A plain file upload widget.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1317', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Generic files', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1318', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Displays all kinds of files with an icon and a linked file description.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1319', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Path to file', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1320', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Displays the file system path to the file.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1321', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'URL to file', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1322', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Displays a full URL to the file.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1323', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => '@preset image', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1324', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => '@preset image linked to node', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1325', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => '@preset image linked to image', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1326', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => '@preset file path', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1327', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => '@preset URL', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1328', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Image', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1329', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'An edit widget for image files, including a preview of the image.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1330', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Displays image files in their original size.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1331', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Image linked to node', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1332', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Image linked to file', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1333', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Title, as link (default)', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1334', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Title, as plain text', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1335', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'URL, as link', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1336', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'URL, as plain text', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1337', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'URL, as absolute URL', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1338', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Short, as link with title "Link"', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1339', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Label, as link with label as title', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1340', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Separate title and URL', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1341', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Textfield', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1342', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Revision information', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1343', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Authoring information', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1344', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Publishing options', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1345', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Comment settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1346', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Comment module form.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1347', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Translation settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1348', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Translation module form.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1349', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Path settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1350', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Path module form.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1351', + 'location' => 'content.module:1897;1900, fuzzy', + 'textgroup' => 'default', + 'source' => 'Print', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1352', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Account settings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1353', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Aggregates syndicated content (RSS, RDF, and Atom feeds).', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1354', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Controls the boxes that are displayed around the main content.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1355', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Allows users to structure site pages in a hierarchy or outline.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1356', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Allows users to comment on and discuss published content.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1357', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Enables the use of both personal and site-wide contact forms.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1358', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Defines CCK date/time fields and widgets.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1359', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Defines an email field type for cck', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1360', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Calendaring API, calendar display and export', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1361', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Handles the filtering of content in preparation for display.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1362', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Extends Drupal support for multilingual features.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1363', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'ImageAPI supporting multiple toolkits.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1364', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Dynamic image manipulator and cache.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1365', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Adds language handling functionality and enables the translation of the user interface to languages other than English.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1366', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Allows administrators to customize the site navigation menu.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1367', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Allows content to be submitted to the site and displayed on pages.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1368', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Allows users to rename URLs.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1369', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Supports configurable user profiles.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1370', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Handles general site configuration for administrators.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1371', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Enables the categorization of content.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1372', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Allows content to be translated into different languages.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1373', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Allows users to upload and attach files to content.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1374', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Manages the user registration and login system.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1375', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'Variable API - Admin UI', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1376', + 'location' => '/?q=fr/admin/by-module', + 'textgroup' => 'default', + 'source' => 'This page shows you all available administration tasks for each module.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1377', + 'location' => 'field.php:102 number.module:82 text.module:80', + 'textgroup' => 'default', + 'source' => 'is equal to', + 'version' => 'none', +)) +->values(array( + 'lid' => '1378', + 'location' => 'field.php:103 number.module:83 text.module:81', + 'textgroup' => 'default', + 'source' => 'is not equal to', + 'version' => 'none', +)) +->values(array( + 'lid' => '1379', + 'location' => 'field.php:104 text.module:82', + 'textgroup' => 'default', + 'source' => 'matches the pattern', + 'version' => 'none', +)) +->values(array( + 'lid' => '1380', + 'location' => 'content_admin.inc:25 content.module:119', + 'textgroup' => 'default', + 'source' => 'duplicate', + 'version' => 'none', +)) +->values(array( + 'lid' => '1381', + 'location' => 'number.module:52, text.module:55', + 'textgroup' => 'default', + 'source' => 'The possible values this field can contain. Any other values will result in an error. Enter one value per line.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1382', + 'location' => 'content.module:73', + 'textgroup' => 'default', + 'source' => 'add content type', + 'version' => 'none', +)) +->values(array( + 'lid' => '1383', + 'location' => 'content.module:80', + 'textgroup' => 'default', + 'source' => 'fields', + 'version' => 'none', +)) +->values(array( + 'lid' => '1384', + 'location' => 'content.module:164', + 'textgroup' => 'default', + 'source' => 'remove field', + 'version' => 'none', +)) +->values(array( + 'lid' => '1385', + 'location' => 'nodereference.module:15', + 'textgroup' => 'default', + 'source' => 'Defines a field type for referencing one node from another. Note: Requires content.module.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1386', + 'location' => 'nodereference.module:26', + 'textgroup' => 'default', + 'source' => 'node reference autocomplete', + 'version' => 'none', +)) +->values(array( + 'lid' => '1387', + 'location' => 'nodereference.module:204', + 'textgroup' => 'default', + 'source' => 'No post with that title exists.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1388', + 'location' => 'number.module:15', + 'textgroup' => 'default', + 'source' => 'Defines numeric field types. Note: Requires content.module.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1389', + 'location' => 'optionwidgets.module:15', + 'textgroup' => 'default', + 'source' => 'Defines selection, check box and radio button widgets for text and numeric fields. Note: Requires content.module, text.module and number.module.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1390', + 'location' => 'text.module:15', + 'textgroup' => 'default', + 'source' => 'Defines simple text field types. Note: Requires content.module.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1391', + 'location' => 'userreference.module:15', + 'textgroup' => 'default', + 'source' => 'Defines a field type for referencing a user from a node. Note: Requires content.module.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1392', + 'location' => 'userreference.module:176', + 'textgroup' => 'default', + 'source' => 'Invalid user name.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1393', + 'location' => 'weburl.module:15', + 'textgroup' => 'default', + 'source' => 'Defines simple weburl field types. Note: Requires content.module.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1394', + 'location' => 'weburl.module:164;172', + 'textgroup' => 'default', + 'source' => 'Not a valid Web URL.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1395', + 'location' => 'weburl.module:0', + 'textgroup' => 'default', + 'source' => 'weburl', + 'version' => 'none', +)) +->values(array( + 'lid' => '1396', + 'location' => 'content_admin.inc:90', + 'textgroup' => 'default', + 'source' => 'The human-readable name of this content type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1397', + 'location' => 'content_admin.inc:98', + 'textgroup' => 'default', + 'source' => 'A brief description of the content type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1398', + 'location' => 'content_admin.inc:106', + 'textgroup' => 'default', + 'source' => 'Instructions to present to the user when adding new content of this type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1399', + 'location' => 'content_admin.inc:110', + 'textgroup' => 'default', + 'source' => 'Title field label', + 'version' => 'none', +)) +->values(array( + 'lid' => '1400', + 'location' => 'content_admin.inc:113', + 'textgroup' => 'default', + 'source' => 'The label for the title field.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1401', + 'location' => 'content_admin.inc:118', + 'textgroup' => 'default', + 'source' => 'Save content type', + 'version' => 'none', +)) +->values(array( + 'lid' => '1402', + 'location' => 'content_admin.inc:182', + 'textgroup' => 'default', + 'source' => 'Saved content type %type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1403', + 'location' => 'content_admin.inc:198', + 'textgroup' => 'default', + 'source' => 'Are you sure you want to delete the content type %type?', + 'version' => 'none', +)) +->values(array( + 'lid' => '1404', + 'location' => 'content_admin.inc:198', + 'textgroup' => 'default', + 'source' => 'If you have any content left in this content type, it will be permanently deleted. This action cannot be undone.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1405', + 'location' => 'content_admin.inc:220', + 'textgroup' => 'default', + 'source' => 'Deleted content type %type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1406', + 'location' => 'content_admin.inc:251', + 'textgroup' => 'default', + 'source' => 'remove', + 'version' => 'none', +)) +->values(array( + 'lid' => '1407', + 'location' => 'content_admin.inc:313', + 'textgroup' => 'default', + 'source' => 'The human-readable name of this field.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1408', + 'location' => 'content_admin.inc:326', + 'textgroup' => 'default', + 'source' => 'Create field', + 'version' => 'none', +)) +->values(array( + 'lid' => '1409', + 'location' => 'content_admin.inc:335', + 'textgroup' => 'default', + 'source' => 'No field modules are enabled. You need to enable one, such as text.module, before you can add new fields.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1410', + 'location' => 'content_admin.inc:487', + 'textgroup' => 'default', + 'source' => 'The field %field no longer exists in any content type, so it was deleted.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1411', + 'location' => 'content_admin.inc:522', + 'textgroup' => 'default', + 'source' => 'Widget settings', + 'version' => 'none', +)) +->values(array( + 'lid' => '1412', + 'location' => 'content_admin.inc:526', + 'textgroup' => 'default', + 'source' => 'Widget', + 'version' => 'none', +)) +->values(array( + 'lid' => '1413', + 'location' => 'content_admin.inc:541', + 'textgroup' => 'default', + 'source' => 'In the node editing form, the heavier fields will sink and the lighter fields will be positioned nearer the top.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1414', + 'location' => 'content_admin.inc:552', + 'textgroup' => 'default', + 'source' => 'Instructions to present to the user below this field on the editing form.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1415', + 'location' => 'content_admin.inc:569', + 'textgroup' => 'default', + 'source' => 'Data settings', + 'version' => 'none', +)) +->values(array( + 'lid' => '1416', + 'location' => 'content_admin.inc:579', + 'textgroup' => 'default', + 'source' => 'Multiple values', + 'version' => 'none', +)) +->values(array( + 'lid' => '1417', + 'location' => 'content_admin.inc:652', + 'textgroup' => 'default', + 'source' => 'Saved field %field.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1418', + 'location' => 'content_admin.inc:882;971', + 'textgroup' => 'default', + 'source' => 'No PostgreSQL mapping found for %type data type.', + 'version' => 'none', +)) +->values(array( + 'lid' => '1419', + 'location' => 'content_admin.inc:896;985', + 'textgroup' => 'default', + 'source' => 'database', 'version' => 'none', )) ->values(array( - 'lid' => '211', - 'location' => 'includes/content.token.inc:84', + 'lid' => '1420', + 'location' => 'date.module:15', 'textgroup' => 'default', - 'source' => 'Formatted html link to the referenced node.', + 'source' => 'Defines a date/time field type. Note: Requires content.module.', 'version' => 'none', )) ->values(array( - 'lid' => '212', - 'location' => 'includes/content.token.inc:85', + 'lid' => '1421', + 'location' => 'date.module:36', 'textgroup' => 'default', - 'source' => 'Relative path alias to the referenced node.', + 'source' => 'Year', 'version' => 'none', )) ->values(array( - 'lid' => '213', - 'location' => 'includes/content.token.inc:86', + 'lid' => '1422', + 'location' => 'date.module:37', 'textgroup' => 'default', - 'source' => 'Absolute path alias to the referenced node.', + 'source' => 'Year and month', 'version' => 'none', )) ->values(array( - 'lid' => '214', - 'location' => 'includes/content.token.inc:86', + 'lid' => '1423', + 'location' => 'date.module:40', 'textgroup' => 'default', - 'source' => 'Raw number value', + 'source' => 'Time only', 'version' => 'none', )) ->values(array( - 'lid' => '215', - 'location' => 'includes/content.token.inc:87', + 'lid' => '1424', + 'location' => 'date.module:44', 'textgroup' => 'default', - 'source' => 'Formatted number value', + 'source' => 'Granularity', 'version' => 'none', )) ->values(array( - 'lid' => '216', - 'location' => 'includes/content.token.inc:110', + 'lid' => '1425', + 'location' => 'date.module:102', 'textgroup' => 'default', - 'source' => 'Raw, unfiltered text', + 'source' => "Times are entered and displayed with site's time zone", 'version' => 'none', )) ->values(array( - 'lid' => '217', - 'location' => 'includes/content.token.inc:111', + 'lid' => '1426', + 'location' => 'date.module:103', 'textgroup' => 'default', - 'source' => 'Formatted and filtered text', + 'source' => "Times are entered and displayed with user's time zone", 'version' => 'none', )) ->values(array( - 'lid' => '218', - 'location' => 'includes/content.token.inc:133', + 'lid' => '1427', + 'location' => 'date.module:107', 'textgroup' => 'default', - 'source' => 'Referenced user ID', + 'source' => 'Time zone handling', 'version' => 'none', )) ->values(array( - 'lid' => '219', - 'location' => 'includes/content.token.inc:134', + 'lid' => '1428', + 'location' => 'date.module:153', 'textgroup' => 'default', - 'source' => 'Referenced user name', + 'source' => '%name must be entered in ISO 8601 format (YYYYMMDDThh:mm:ss).', 'version' => 'none', )) ->values(array( - 'lid' => '220', - 'location' => 'includes/content.token.inc:135', + 'lid' => '1429', + 'location' => 'content.module:61', 'textgroup' => 'default', - 'source' => 'Formatted HTML link to referenced user', + 'source' => 'content types', 'version' => 'none', )) ->values(array( - 'lid' => '221', - 'location' => 'includes/content.token.inc:164', + 'lid' => '1430', + 'location' => 'content.module:67', 'textgroup' => 'default', - 'source' => 'Relative path alias to the referenced user.', + 'source' => 'list', 'version' => 'none', )) ->values(array( - 'lid' => '222', - 'location' => 'includes/content.token.inc:165', + 'lid' => '1431', + 'location' => 'modules/optionwidgets/optionwidgets.module:326', 'textgroup' => 'default', - 'source' => 'Absolute path alias to the referenced user.', + 'source' => '%name: this field cannot hold more than @count values.', 'version' => 'none', )) ->values(array( - 'lid' => '223', - 'location' => 'includes/views/content.views.inc:245;261', + 'lid' => '1432', + 'location' => 'includes/panels/content_types/content_field.inc:37', 'textgroup' => 'default', - 'source' => '@label (!name)', + 'source' => '@type: (@field_type) @field', 'version' => 'none', )) ->values(array( - 'lid' => '224', - 'location' => 'includes/views/content.views.inc:249', + 'lid' => '1433', + 'location' => 'includes/panels/content_types/content_field.inc:44', 'textgroup' => 'default', - 'source' => '@label (!name) - !column', + 'source' => 'Field on the referenced node.', 'version' => 'none', )) ->values(array( - 'lid' => '225', - 'location' => 'includes/views/content.views.inc:250', + 'lid' => '1434', + 'location' => 'includes/panels/content_types/content_field.inc:128', 'textgroup' => 'default', - 'source' => '@label-truncated - !column', + 'source' => 'Formatter', 'version' => 'none', )) ->values(array( - 'lid' => '226', - 'location' => 'includes/content.views.inc:93', + 'lid' => '1435', + 'location' => 'includes/panels/content_types/content_field.inc:131', 'textgroup' => 'default', - 'source' => 'Appears in: @types', + 'source' => 'Select a formatter.', 'version' => 'none', )) ->values(array( - 'lid' => '227', - 'location' => 'includes/views/handlers/content_handler_field.inc:59', + 'lid' => '1436', + 'location' => 'includes/panels/content_types/content_field.inc:147', 'textgroup' => 'default', - 'source' => 'None', + 'source' => '"@s" field (@name)', 'version' => 'none', )) ->values(array( - 'lid' => '228', - 'location' => 'includes/views/handlers/content_handler_field.inc:60', + 'lid' => '1437', + 'location' => '/?q=zu/admin/settingsjhkjg', 'textgroup' => 'default', - 'source' => 'Widget label (@label)', - 'version' => 'none', + 'source' => 'Page not found', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '229', - 'location' => 'includes/views/handlers/content_handler_field.inc:61', + 'lid' => '1438', + 'location' => '/?q=zu/admin/settingsjhkjg', 'textgroup' => 'default', - 'source' => 'Custom', - 'version' => 'none', + 'source' => 'The requested page could not be found.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '230', - 'location' => 'includes/views/handlers/content_handler_field.inc:67', + 'lid' => '1439', + 'location' => '/?q=zu/admin/build/translate', 'textgroup' => 'default', - 'source' => 'Custom label', - 'version' => 'none', + 'source' => 'English (built-in)', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '231', - 'location' => 'includes/content.views.inc:291', + 'lid' => '1440', + 'location' => '/?q=zu/admin/build/translate', 'textgroup' => 'default', - 'source' => 'Format', - 'version' => 'none', + 'source' => 'n/a', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1441', + 'location' => '/?q=zu/admin/build/translate', + 'textgroup' => 'default', + 'source' => 'Zulu', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1442', + 'location' => '/?q=zu/admin/build/translate', + 'textgroup' => 'default', + 'source' => 'Overview', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1443', + 'location' => '/?q=zu/admin/build/translate', + 'textgroup' => 'default', + 'source' => 'Refresh', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1444', + 'location' => '/?q=zu/admin/build/translate', + 'textgroup' => 'default', + 'source' => 'This page provides an overview of available translatable strings. Drupal displays translatable strings in text groups; modules may define additional text groups containing other translatable strings. Because text groups provide a method of grouping related strings, they are often used to focus translation efforts on specific areas of the Drupal interface.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1445', + 'location' => '/?q=zu/admin/build/translate', + 'textgroup' => 'default', + 'source' => 'Review the languages page for more information on adding support for additional languages.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1446', + 'location' => '/?q=zu/admin/build/translate/search', + 'textgroup' => 'default', + 'source' => 'English', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1447', + 'location' => '/?q=zu/admin/build/translate/search', + 'textgroup' => 'default', + 'source' => 'String contains', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1448', + 'location' => '/?q=zu/admin/build/translate/search', + 'textgroup' => 'default', + 'source' => 'Leave blank to show all strings. The search is case sensitive.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1449', + 'location' => '/?q=zu/admin/build/translate/search', + 'textgroup' => 'default', + 'source' => 'All languages', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1450', + 'location' => '/?q=zu/admin/build/translate/search', + 'textgroup' => 'default', + 'source' => 'English (provided by Drupal)', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1451', + 'location' => '/?q=zu/admin/build/translate/search', + 'textgroup' => 'default', + 'source' => 'Search in', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1452', + 'location' => '/?q=zu/admin/build/translate/search', + 'textgroup' => 'default', + 'source' => 'Both translated and untranslated strings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1453', + 'location' => '/?q=zu/admin/build/translate/search', + 'textgroup' => 'default', + 'source' => 'Only translated strings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1454', + 'location' => '/?q=zu/admin/build/translate/search', + 'textgroup' => 'default', + 'source' => 'Only untranslated strings', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1455', + 'location' => '/?q=zu/admin/build/translate/search', + 'textgroup' => 'default', + 'source' => 'Limit search to', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1456', + 'location' => '/?q=zu/admin/build/translate/search', + 'textgroup' => 'default', + 'source' => 'All text groups', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1457', + 'location' => '/?q=zu/admin/build/translate/search', + 'textgroup' => 'default', + 'source' => 'This page allows a translator to search for specific translated and untranslated strings, and is used when creating or editing translations. (Note: For translation tasks involving many strings, it may be more convenient to export strings for off-line editing in a desktop Gettext translation editor.) Searches may be limited to strings found within a specific text group or in a specific language.', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1458', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Already added languages', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1459', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Languages not yet added', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1460', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Afar', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1461', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Abkhazian', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1462', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Avestan', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1463', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Afrikaans', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1464', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Akan', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1465', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Amharic', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1466', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Arabic', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1467', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Assamese', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1468', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Avar', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1469', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Aymara', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1470', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Azerbaijani', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1471', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Bashkir', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1472', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Belarusian', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1473', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Bulgarian', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1474', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Bihari', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1475', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Bislama', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1476', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Bambara', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1477', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Bengali', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1478', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Tibetan', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1479', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Breton', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1480', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Bosnian', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1481', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Catalan', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1482', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Chechen', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1483', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Chamorro', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1484', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Corsican', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1485', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Cree', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1486', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Czech', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1487', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Old Slavonic', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1488', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Chuvash', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1489', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Welsh', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1490', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Danish', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1491', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'German', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1492', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Maldivian', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1493', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Bhutani', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1494', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Ewe', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1495', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Greek', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1496', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Esperanto', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1497', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Spanish', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1498', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Estonian', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1499', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Basque', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1500', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Persian', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1501', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Fulah', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1502', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Finnish', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1503', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Fiji', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1504', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Faeroese', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1505', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Frisian', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1506', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Irish', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1507', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Scots Gaelic', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '232', - 'location' => 'includes/content.views.inc:372', + 'lid' => '1508', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Group multiple values', - 'version' => 'none', + 'source' => 'Galician', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '233', - 'location' => 'includes/views/handlers/content_handler_field_multiple.inc:61', + 'lid' => '1509', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'If unchecked, each item in the field will create a new row, which may appear to cause duplicates. This setting is not compatible with click-sorting in table displays.', - 'version' => 'none', + 'source' => 'Guarani', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '234', - 'location' => 'includes/content.views.inc:378', + 'lid' => '1510', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Show @count value(s)', - 'version' => 'none', + 'source' => 'Gujarati', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '235', - 'location' => 'includes/content.views.inc:387', + 'lid' => '1511', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'starting from @count', - 'version' => 'none', + 'source' => 'Manx', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '236', - 'location' => 'includes/content.views.inc:396', + 'lid' => '1512', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Reversed (start from last values)', - 'version' => 'none', + 'source' => 'Hausa', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '237', - 'location' => 'includes/views/handlers/content_handler_relationship.inc:40, includes/views/handlers/content_handler_sort.inc:41', + 'lid' => '1513', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'All', - 'version' => 'none', + 'source' => 'Hebrew', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '238', - 'location' => 'includes/views/handlers/content_handler_relationship.inc:48, includes/views/handlers/content_handler_sort.inc:49', + 'lid' => '1514', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Delta', - 'version' => 'none', + 'source' => 'Hindi', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '239', - 'location' => 'includes/views/handlers/content_handler_relationship.inc:49', + 'lid' => '1515', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The delta allows you to select which item in a multiple value field to key the relationship off of. Select "1" to use the first item, "2" for the second item, and so on. If you select "All", each item in the field will create a new row, which may appear to cause duplicates.', - 'version' => 'none', + 'source' => 'Hiri Motu', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '240', - 'location' => 'includes/views/handlers/content_handler_sort.inc:50', + 'lid' => '1516', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The delta allows you to select which item in a multiple value field will be used for sorting. Select "1" to use the first item, "2" for the second item, and so on. If you select "All", each item in the field will create a new row, which may appear to cause duplicates.', - 'version' => 'none', + 'source' => 'Croatian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '241', - 'location' => 'modules/content_copy/content_copy_export_form.tpl.php:9, modules/content_copy/content_copy.module:191;38', + 'lid' => '1517', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Export', - 'version' => 'none', + 'source' => 'Hungarian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '242', - 'location' => 'modules/content_copy/content_copy.module:97', + 'lid' => '1518', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'This form will process a content type and one or more fields from that type and export the settings. The export created by this process can be copied and pasted as an import into the current or any other database. The import will add the fields to into an existing content type or create a new content type that includes the selected fields.', - 'version' => 'none', + 'source' => 'Armenian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '243', - 'location' => 'modules/content_copy/content_copy.module:103', + 'lid' => '1519', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Types', - 'version' => 'none', + 'source' => 'Herero', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '244', - 'location' => 'modules/content_copy/content_copy.module:107', + 'lid' => '1520', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Select the content type to export.', - 'version' => 'none', + 'source' => 'Interlingua', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '245', - 'location' => 'modules/content_copy/content_copy.module:175', + 'lid' => '1521', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Export data', - 'version' => 'none', + 'source' => 'Indonesian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '246', - 'location' => 'modules/content_copy/content_copy.module:180', + 'lid' => '1522', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Copy the export text and paste it into another content type using the import function.', - 'version' => 'none', + 'source' => 'Interlingue', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '247', - 'location' => 'modules/content_copy/content_copy.module:184', + 'lid' => '1523', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Content types', - 'version' => 'none', + 'source' => 'Igbo', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '248', - 'location' => 'modules/content_copy/content_copy.module:308', + 'lid' => '1524', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Content type', - 'version' => 'none', + 'source' => 'Inupiak', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '249', - 'location' => 'modules/content_copy/content_copy.module:309', + 'lid' => '1525', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Select the content type to import these fields into.
                          Select <Create> to create a new content type to contain the fields.', - 'version' => 'none', + 'source' => 'Icelandic', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '250', - 'location' => 'modules/content_copy/content_copy.module:314', + 'lid' => '1526', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Import data', - 'version' => 'none', + 'source' => 'Italian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '251', - 'location' => 'modules/content_copy/content_copy.module:316', + 'lid' => '1527', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Paste the text created by a content export into this field.', - 'version' => 'none', + 'source' => 'Inuktitut', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '252', - 'location' => 'modules/content_copy/content_copy.module:320;46', + 'lid' => '1528', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Import', - 'version' => 'none', + 'source' => 'Japanese', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '253', - 'location' => 'modules/content_copy/content_copy.module:328', + 'lid' => '1529', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'A file has been pre-loaded for import.', - 'version' => 'none', + 'source' => 'Javanese', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '254', - 'location' => 'modules/content_copy/content_copy.module:354', + 'lid' => '1530', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The import data is not valid import text.', - 'version' => 'none', + 'source' => 'Georgian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '255', - 'location' => 'modules/content_copy/content_copy.module:403', + 'lid' => '1531', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The following modules must be enabled for this import to work: %modules.', - 'version' => 'none', + 'source' => 'Kongo', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '256', - 'location' => 'modules/content_copy/content_copy.module:411', + 'lid' => '1532', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The content type %type already exists in this database.', - 'version' => 'none', + 'source' => 'Kikuyu', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '257', - 'location' => 'modules/content_copy/content_copy.module:418', + 'lid' => '1533', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Exiting. No import performed.', - 'version' => 'none', + 'source' => 'Kwanyama', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '258', - 'location' => 'modules/content_copy/content_copy.module:442', + 'lid' => '1534', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'An error has occurred adding the content type %type.
                          Please check the errors displayed for more details.', - 'version' => 'none', + 'source' => 'Kazakh', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '259', - 'location' => 'modules/content_copy/content_copy.module:467', + 'lid' => '1535', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The imported field %field_label (%field_name) was not added to %type because that field already exists in %type.', - 'version' => 'none', + 'source' => 'Greenlandic', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '260', - 'location' => 'modules/content_copy/content_copy.module:476', + 'lid' => '1536', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The field %field_label (%field_name) was added to the content type %type.', - 'version' => 'none', + 'source' => 'Cambodian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '261', - 'location' => 'modules/content_copy/content_copy.module:0', + 'lid' => '1537', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'content_copy', - 'version' => 'none', + 'source' => 'Kannada', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '262', - 'location' => 'modules/content_copy/content_copy.info:0', + 'lid' => '1538', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Content Copy', - 'version' => 'none', + 'source' => 'Korean', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '263', - 'location' => 'modules/content_copy/content_copy.info:0', + 'lid' => '1539', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Enables ability to import/export field definitions.', - 'version' => 'none', + 'source' => 'Kanuri', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '264', - 'location' => 'modules/content_multigroup/content_multigroup.module:12', + 'lid' => '1540', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The fields in a Standard group are independent of each other and each can have either single or multiple values. The fields in a Multigroup are treated as a repeating collection of single value fields.', - 'version' => 'none', + 'source' => 'Kashmiri', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '265', - 'location' => 'modules/content_multigroup/content_multigroup.module:65;135', + 'lid' => '1541', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Multigroup', - 'version' => 'none', + 'source' => 'Kurdish', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '266', - 'location' => 'modules/content_multigroup/content_multigroup.module:134', + 'lid' => '1542', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Standard', - 'version' => 'none', + 'source' => 'Komi', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '267', - 'location' => 'modules/content_multigroup/content_multigroup.module:138', + 'lid' => '1543', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Type of group.', - 'version' => 'none', + 'source' => 'Cornish', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '268', - 'location' => 'modules/content_multigroup/content_multigroup.module:215', + 'lid' => '1544', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The field %field has been updated to use %multiple values, to match the multiple value setting of the Multigroup %group.', - 'version' => 'none', + 'source' => 'Kirghiz', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '269', - 'location' => 'modules/content_multigroup/content_multigroup.module:248', + 'lid' => '1545', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'This change is not allowed. The field %field already has %multiple values in the database but the group %group only allows %group_max. Making this change would result in the loss of data.', - 'version' => 'none', + 'source' => 'Latin', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '270', - 'location' => 'modules/content_multigroup/content_multigroup.module:272', + 'lid' => '1546', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'This change is not allowed. The field %field handles multiple values differently than the Content module. Making this change could result in the loss of data.', - 'version' => 'none', + 'source' => 'Luxembourgish', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '271', - 'location' => 'modules/content_multigroup/content_multigroup.module:287', + 'lid' => '1547', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'You are moving the field %field into a Multigroup.', - 'version' => 'none', + 'source' => 'Luganda', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '272', - 'location' => 'modules/content_multigroup/content_multigroup.module:320', + 'lid' => '1548', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'This change is not allowed. The field %field already has data created and uses a widget that stores data differently in a Standard group than in a Multigroup. Making this change could result in the loss of data.', - 'version' => 'none', + 'source' => 'Lingala', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '273', - 'location' => 'modules/content_multigroup/content_multigroup.module:334', + 'lid' => '1549', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'You are moving the field %field out of a Multigroup.', - 'version' => 'none', + 'source' => 'Laothian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '274', - 'location' => 'modules/content_multigroup/content_multigroup.module:369', + 'lid' => '1550', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Simple', - 'version' => 'none', + 'source' => 'Lithuanian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '275', - 'location' => 'modules/content_multigroup/content_multigroup.module:370', + 'lid' => '1551', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Fieldset', - 'version' => 'none', + 'source' => 'Latvian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '276', - 'location' => 'modules/content_multigroup/content_multigroup.module:371', + 'lid' => '1552', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Horizontal line', - 'version' => 'none', + 'source' => 'Malagasy', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '277', - 'location' => 'modules/content_multigroup/content_multigroup.module:372', + 'lid' => '1553', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Table - Single column', - 'version' => 'none', + 'source' => 'Marshallese', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '278', - 'location' => 'modules/content_multigroup/content_multigroup.module:373', + 'lid' => '1554', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Table - Multiple columns', - 'version' => 'none', + 'source' => 'Maori', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '279', - 'location' => 'modules/content_multigroup/content_multigroup.module:384', + 'lid' => '1555', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => '[Subgroup format]', - 'version' => 'none', + 'source' => 'Macedonian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '280', - 'location' => 'modules/content_multigroup/content_multigroup.module:461', + 'lid' => '1556', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Multigroup settings', - 'version' => 'none', + 'source' => 'Malayalam', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '281', - 'location' => 'modules/content_multigroup/content_multigroup.module:476', + 'lid' => '1557', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Multiple columns', - 'version' => 'none', + 'source' => 'Mongolian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '282', - 'location' => 'modules/content_multigroup/content_multigroup.module:478', + 'lid' => '1558', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Enable this option to render each field on a separate column on the node edit form.', - 'version' => 'none', + 'source' => 'Moldavian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '283', - 'location' => 'modules/content_multigroup/content_multigroup.module:485', + 'lid' => '1559', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Enable this option to require a minimum of one collection of fields in this Multigroup.', - 'version' => 'none', + 'source' => 'Marathi', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '284', - 'location' => 'modules/content_multigroup/content_multigroup.module:488', + 'lid' => '1560', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Number of times to repeat the collection of Multigroup fields.', - 'version' => 'none', + 'source' => 'Malay', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '285', - 'location' => 'modules/content_multigroup/content_multigroup.module:489', + 'lid' => '1561', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => "'Unlimited' will provide an 'Add more' button so the users can add items as many times as they like.", - 'version' => 'none', + 'source' => 'Maltese', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '286', - 'location' => 'modules/content_multigroup/content_multigroup.module:490', + 'lid' => '1562', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'All fields in this group will automatically be set to allow this number of values.', - 'version' => 'none', + 'source' => 'Burmese', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '287', - 'location' => 'modules/content_multigroup/content_multigroup.module:495', + 'lid' => '1563', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Number of repeats', - 'version' => 'none', + 'source' => 'Nauru', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '288', - 'location' => 'modules/content_multigroup/content_multigroup.module:503', + 'lid' => '1564', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Labels', - 'version' => 'none', + 'source' => 'North Ndebele', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '289', - 'location' => 'modules/content_multigroup/content_multigroup.module:504', + 'lid' => '1565', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => "Labels for each subgroup of fields. Labels can be hidden or shown in various contexts using the 'Display fields' screen.", - 'version' => 'none', + 'source' => 'Nepali', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '290', - 'location' => 'modules/content_multigroup/content_multigroup.module:512', + 'lid' => '1566', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Subgroup %number label', - 'version' => 'none', + 'source' => 'Ndonga', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '291', - 'location' => 'modules/content_multigroup/content_multigroup.module:539', + 'lid' => '1567', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The field %field in this group already has %multiple values in the database. To prevent the loss of data you cannot set the number of Multigroup values to less than this.', - 'version' => 'none', + 'source' => 'Dutch', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '292', - 'location' => 'modules/content_multigroup/content_multigroup.module:932', + 'lid' => '1568', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => '!name field is required in group @group.', - 'version' => 'none', + 'source' => 'Norwegian Bokmål', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '293', - 'location' => 'modules/content_multigroup/content_multigroup.module:946', + 'lid' => '1569', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Group @name requires one collection of fields minimum.', - 'version' => 'none', + 'source' => 'Norwegian Nynorsk', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '294', - 'location' => 'modules/content_multigroup/content_multigroup.module:1145', + 'lid' => '1570', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Add more values', - 'version' => 'none', + 'source' => 'South Ndebele', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '295', - 'location' => 'modules/content_multigroup/content_multigroup.module:0', + 'lid' => '1571', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'content_multigroup', - 'version' => 'none', + 'source' => 'Navajo', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '296', - 'location' => 'modules/content_multigroup/content_multigroup.info:0', + 'lid' => '1572', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Content Multigroup', - 'version' => 'none', + 'source' => 'Chichewa', + 'version' => '6.38-dev', +)) +->values(array( + 'lid' => '1573', + 'location' => '/?q=zu/admin/build/translate/import', + 'textgroup' => 'default', + 'source' => 'Occitan', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '297', - 'location' => 'modules/content_multigroup/content_multigroup.info:0', + 'lid' => '1574', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Combine multiple CCK fields into repeating field collections that work in unison.', - 'version' => 'none', + 'source' => 'Oromo', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '298', - 'location' => 'modules/content_permissions/content_permissions.module:10', + 'lid' => '1575', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'edit ', - 'version' => 'none', + 'source' => 'Oriya', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '299', - 'location' => 'modules/content_permissions/content_permissions.module:10;11', + 'lid' => '1576', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'field_name', - 'version' => 'none', + 'source' => 'Ossetian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '300', - 'location' => 'modules/content_permissions/content_permissions.module:11', + 'lid' => '1577', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'view ', - 'version' => 'none', + 'source' => 'Punjabi', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '301', - 'location' => 'modules/content_permissions/content_permissions.module:0', + 'lid' => '1578', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'content_permissions', - 'version' => 'none', + 'source' => 'Pali', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '302', - 'location' => 'modules/content_permissions/content_permissions.install:9', + 'lid' => '1579', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Please configure your field permissions immediately. All fields are inaccessible by default.', - 'version' => 'none', + 'source' => 'Polish', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '303', - 'location' => 'modules/content_permissions/content_permissions.info:0', + 'lid' => '1580', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Content Permissions', - 'version' => 'none', + 'source' => 'Pashto', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '304', - 'location' => 'modules/content_permissions/content_permissions.info:0', + 'lid' => '1581', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Set field-level permissions for CCK fields.', - 'version' => 'none', + 'source' => 'Portuguese, Portugal', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '305', - 'location' => 'modules/fieldgroup/fieldgroup.panels.inc:10;27', + 'lid' => '1582', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Content fieldgroup', - 'version' => 'none', + 'source' => 'Portuguese, Brazil', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '306', - 'location' => 'modules/fieldgroup/fieldgroup.panels.inc:30', + 'lid' => '1583', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'All fields from a fieldgroup on the referenced node.', - 'version' => 'none', + 'source' => 'Quechua', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '307', - 'location' => 'modules/fieldgroup/fieldgroup.panels.inc:91', + 'lid' => '1584', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => '@group_label (@group_type_name)', - 'version' => 'none', + 'source' => 'Rhaeto-Romance', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '308', - 'location' => 'modules/fieldgroup/fieldgroup.panels.inc:102, modules/fieldgroup/fieldgroup.info:0', + 'lid' => '1585', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Fieldgroup', - 'version' => 'none', + 'source' => 'Kirundi', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '309', - 'location' => 'modules/fieldgroup/fieldgroup.panels.inc:112', + 'lid' => '1586', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Text to display if group has no data. Note that title will not display unless overridden.', - 'version' => 'none', + 'source' => 'Romanian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '310', - 'location' => 'modules/fieldgroup/fieldgroup.panels.inc:128', + 'lid' => '1587', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => '"@s" fieldgroup @name', - 'version' => 'none', + 'source' => 'Russian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '311', - 'location' => 'modules/fieldgroup/fieldgroup.module:124', + 'lid' => '1588', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Form settings', - 'version' => 'none', + 'source' => 'Kinyarwanda', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '312', - 'location' => 'modules/fieldgroup/fieldgroup.module:125', + 'lid' => '1589', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'These settings apply to the group in the node editing form.', - 'version' => 'none', + 'source' => 'Sanskrit', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '313', - 'location' => 'modules/fieldgroup/fieldgroup.module:129', + 'lid' => '1590', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Style', - 'version' => 'none', + 'source' => 'Sardinian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '314', - 'location' => 'modules/fieldgroup/fieldgroup.module:132', + 'lid' => '1591', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'always open', - 'version' => 'none', + 'source' => 'Sindhi', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '315', - 'location' => 'modules/fieldgroup/fieldgroup.module:133', + 'lid' => '1592', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'collapsible', - 'version' => 'none', + 'source' => 'Northern Sami', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '316', - 'location' => 'modules/fieldgroup/fieldgroup.module:134', + 'lid' => '1593', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'collapsed', - 'version' => 'none', + 'source' => 'Sango', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '317', - 'location' => 'modules/fieldgroup/fieldgroup.module:142', + 'lid' => '1594', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Instructions to present to the user on the editing form.', - 'version' => 'none', + 'source' => 'Serbo-Croatian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '318', - 'location' => 'modules/fieldgroup/fieldgroup.module:147', + 'lid' => '1595', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Display settings', - 'version' => 'none', + 'source' => 'Sinhala', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '319', - 'location' => 'modules/fieldgroup/fieldgroup.module:148', + 'lid' => '1596', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'These settings apply to the group on node display.', - 'version' => 'none', + 'source' => 'Slovak', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '320', - 'location' => 'modules/fieldgroup/fieldgroup.module:155', + 'lid' => '1597', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'A description of the group.', - 'version' => 'none', + 'source' => 'Slovenian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '321', - 'location' => 'modules/fieldgroup/fieldgroup.module:200', + 'lid' => '1598', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Are you sure you want to remove the group %label?', - 'version' => 'none', + 'source' => 'Samoan', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '322', - 'location' => 'modules/fieldgroup/fieldgroup.module:202', + 'lid' => '1599', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'This action cannot be undone.', - 'version' => 'none', + 'source' => 'Shona', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '323', - 'location' => 'modules/fieldgroup/fieldgroup.module:211', + 'lid' => '1600', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The group %group_name has been removed.', - 'version' => 'none', + 'source' => 'Somali', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '324', - 'location' => 'modules/fieldgroup/fieldgroup.module:293, modules/nodereference/nodereference.module:544, modules/userreference/userreference.module:439', + 'lid' => '1601', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'none', - 'version' => 'none', + 'source' => 'Albanian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '325', - 'location' => 'modules/fieldgroup/fieldgroup.module:353', + 'lid' => '1602', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'You need to provide a label.', - 'version' => 'none', + 'source' => 'Serbian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '326', - 'location' => 'modules/fieldgroup/fieldgroup.module:358', + 'lid' => '1603', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'You need to provide a group name.', - 'version' => 'none', + 'source' => 'Siswati', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '327', - 'location' => 'modules/fieldgroup/fieldgroup.module:372', + 'lid' => '1604', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The group name %group_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', - 'version' => 'none', + 'source' => 'Sesotho', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '328', - 'location' => 'modules/fieldgroup/fieldgroup.module:375', + 'lid' => '1605', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => "The group name %group_name is too long. The name is limited to 32 characters, including the 'group_' prefix.", - 'version' => 'none', + 'source' => 'Sudanese', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '329', - 'location' => 'modules/fieldgroup/fieldgroup.module:381', + 'lid' => '1606', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The group name %group_name already exists.', - 'version' => 'none', + 'source' => 'Swedish', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '330', - 'location' => 'modules/fieldgroup/fieldgroup.module:400;403', + 'lid' => '1607', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Add new group:', - 'version' => 'none', + 'source' => 'Swahili', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '331', - 'location' => 'modules/fieldgroup/fieldgroup.module:418', + 'lid' => '1608', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Add new group: you need to provide a label.', - 'version' => 'none', + 'source' => 'Tamil', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '332', - 'location' => 'modules/fieldgroup/fieldgroup.module:419', + 'lid' => '1609', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Add new group: you need to provide a group name.', - 'version' => 'none', + 'source' => 'Telugu', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '333', - 'location' => 'modules/fieldgroup/fieldgroup.module:648', + 'lid' => '1610', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Standard group', - 'version' => 'none', + 'source' => 'Tajik', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '334', - 'location' => 'modules/fieldgroup/fieldgroup.module:39;46', + 'lid' => '1611', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Edit group', - 'version' => 'none', + 'source' => 'Thai', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '335', - 'location' => 'modules/fieldgroup/fieldgroup.module:0', + 'lid' => '1612', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'fieldgroup', - 'version' => 'none', + 'source' => 'Tigrinya', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '336', - 'location' => 'modules/fieldgroup/fieldgroup.info:0', + 'lid' => '1613', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Create display groups for CCK fields.', - 'version' => 'none', + 'source' => 'Turkmen', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '337', - 'location' => 'modules/nodereference/nodereference.rules.inc:15', + 'lid' => '1614', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Load a referenced node', - 'version' => 'none', + 'source' => 'Tagalog', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '338', - 'location' => 'modules/nodereference/nodereference.rules.inc:19', + 'lid' => '1615', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Content containing the node reference field', - 'version' => 'none', + 'source' => 'Setswana', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '339', - 'location' => 'modules/nodereference/nodereference.rules.inc:25', + 'lid' => '1616', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Referenced content', - 'version' => 'none', + 'source' => 'Tonga', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '340', - 'location' => 'modules/nodereference/nodereference.rules.inc:29', + 'lid' => '1617', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Note that if the field has multiple values, only the first content node will be loaded.', - 'version' => 'none', + 'source' => 'Turkish', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '341', - 'location' => 'modules/nodereference/nodereference.rules.inc:50', + 'lid' => '1618', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'There are no nodereference fields defined.', - 'version' => 'none', + 'source' => 'Tsonga', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '342', - 'location' => 'modules/nodereference/nodereference.module:60', + 'lid' => '1619', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Node reference', - 'version' => 'none', + 'source' => 'Tatar', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '343', - 'location' => 'modules/nodereference/nodereference.module:61', + 'lid' => '1620', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Store the ID of a related node as an integer value.', - 'version' => 'none', + 'source' => 'Twi', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '344', - 'location' => 'modules/nodereference/nodereference.module:75', + 'lid' => '1621', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Content types that can be referenced', - 'version' => 'none', + 'source' => 'Tahitian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '345', - 'location' => 'modules/nodereference/nodereference.module:87, modules/userreference/userreference.module:84', + 'lid' => '1622', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Default Views', - 'version' => 'none', + 'source' => 'Uighur', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '346', - 'location' => 'modules/nodereference/nodereference.module:90, modules/userreference/userreference.module:87', + 'lid' => '1623', + 'location' => '/?q=zu/admin/build/translate/refresh', 'textgroup' => 'default', - 'source' => 'Existing Views', - 'version' => 'none', + 'source' => 'Select text groups', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '347', - 'location' => 'modules/nodereference/nodereference.module:97', + 'lid' => '1624', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Advanced - Nodes that can be referenced (View)', - 'version' => 'none', + 'source' => 'Ukrainian', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '348', - 'location' => 'modules/nodereference/nodereference.module:104', + 'lid' => '1625', + 'location' => '/?q=zu/admin/build/translate/refresh', 'textgroup' => 'default', - 'source' => 'View used to select the nodes', - 'version' => 'none', + 'source' => 'If a text group is no showing up here it means this feature is not implemented for it.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '349', - 'location' => 'modules/nodereference/nodereference.module:107', + 'lid' => '1626', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => '

                          Choose the "Views module" view that selects the nodes that can be referenced.
                          Note:

                          ', - 'version' => 'none', + 'source' => 'Urdu', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '350', - 'location' => 'modules/nodereference/nodereference.module:108;121', + 'lid' => '1627', + 'location' => '/?q=zu/admin/build/translate/refresh', 'textgroup' => 'default', - 'source' => "
                          • Only views that have fields will work for this purpose.
                          • This will discard the \"Content types\" settings above. Use the view's \"filters\" section instead.
                          • Use the view's \"fields\" section to display additional informations about candidate nodes on node creation/edition form.
                          • Use the view's \"sort criteria\" section to determine the order in which candidate nodes will be displayed.
                          ", - 'version' => 'none', + 'source' => 'Refresh strings', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '351', - 'location' => 'modules/nodereference/nodereference.module:112, modules/userreference/userreference.module:109', + 'lid' => '1628', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'View arguments', - 'version' => 'none', + 'source' => 'Uzbek', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '352', - 'location' => 'modules/nodereference/nodereference.module:115, modules/userreference/userreference.module:112', + 'lid' => '1629', + 'location' => '/?q=zu/admin/build/translate/refresh', 'textgroup' => 'default', - 'source' => 'Provide a comma separated list of arguments to pass to the view.', - 'version' => 'none', + 'source' => 'This will create all the missing strings for the selected text groups.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '353', - 'location' => 'modules/nodereference/nodereference.module:120', + 'lid' => '1630', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => '

                          The list of nodes that can be referenced can be based on a "Views module" view but no appropriate views were found.
                          Note:

                          ', - 'version' => 'none', + 'source' => 'Venda', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '354', - 'location' => 'modules/nodereference/nodereference.module:205, modules/userreference/userreference.module:184', + 'lid' => '1631', + 'location' => '/?q=zu/admin/build/translate/refresh', 'textgroup' => 'default', - 'source' => '%name: invalid input.', - 'version' => 'none', + 'source' => 'Select languages', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '355', - 'location' => 'modules/nodereference/nodereference.module:217', + 'lid' => '1632', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => "%name: this post can't be referenced.", - 'version' => 'none', + 'source' => 'Vietnamese', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '356', - 'location' => 'modules/nodereference/nodereference.module:242', + 'lid' => '1633', + 'location' => '/?q=zu/admin/build/translate/refresh', 'textgroup' => 'default', - 'source' => 'Title (link)', - 'version' => 'none', + 'source' => 'Update translations', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '357', - 'location' => 'modules/nodereference/nodereference.module:247', + 'lid' => '1634', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Title (no link)', - 'version' => 'none', + 'source' => 'Wolof', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '358', - 'location' => 'modules/nodereference/nodereference.module:303, modules/optionwidgets/optionwidgets.module:62, modules/userreference/userreference.module:198', + 'lid' => '1635', + 'location' => '/?q=zu/admin/build/translate/refresh', 'textgroup' => 'default', - 'source' => 'Select list', - 'version' => 'none', + 'source' => 'This will fetch all existing translations from the localization tables for the selected text groups and languages.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '359', - 'location' => 'modules/optionwidgets/optionwidgets.module:70', + 'lid' => '1636', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Check boxes/radio buttons', - 'version' => 'none', + 'source' => 'Xhosa', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '360', - 'location' => 'modules/nodereference/nodereference.module:311, modules/userreference/userreference.module:206', + 'lid' => '1637', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Autocomplete text field', - 'version' => 'none', + 'source' => 'Yiddish', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '361', - 'location' => 'modules/nodereference/nodereference.module:417, modules/userreference/userreference.module:343', + 'lid' => '1638', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Autocomplete matching', - 'version' => 'none', + 'source' => 'Yoruba', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '362', - 'location' => 'modules/nodereference/nodereference.module:420, modules/userreference/userreference.module:346', + 'lid' => '1639', + 'location' => '/?q=zu/admin/build/translate/refresh', 'textgroup' => 'default', - 'source' => 'Starts with', - 'version' => 'none', + 'source' => 'On this page you can refresh and update values for user defined strings.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '363', - 'location' => 'modules/nodereference/nodereference.module:421, modules/userreference/userreference.module:347', + 'lid' => '1640', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Contains', - 'version' => 'none', + 'source' => 'Zhuang', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '364', - 'location' => 'modules/nodereference/nodereference.module:423', + 'lid' => '1641', + 'location' => '/?q=zu/admin/build/translate/refresh', 'textgroup' => 'default', - 'source' => 'Select the method used to collect autocomplete suggestions. Note that Contains can cause performance issues on sites with thousands of nodes.', - 'version' => 'none', + 'source' => 'Use the refresh option when you are missing strings to translate for a given text group. All the strings will be re-created keeping existing translations.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '365', - 'location' => 'modules/nodereference/nodereference.module:671', + 'lid' => '1642', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => '%name: title mismatch. Please check your selection.', - 'version' => 'none', + 'source' => 'Chinese, Simplified', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '366', - 'location' => 'modules/nodereference/nodereference.module:678', + 'lid' => '1643', + 'location' => '/?q=zu/admin/build/translate/refresh', 'textgroup' => 'default', - 'source' => '%name: found no valid post with that title.', - 'version' => 'none', + 'source' => 'Use the update option when some of the strings had been previously translated with the localization system, but the translations are not showing up for the configurable strings.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '367', - 'location' => 'modules/nodereference/nodereference.module:15', + 'lid' => '1644', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Nodereference autocomplete', - 'version' => 'none', + 'source' => 'Chinese, Traditional', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '368', - 'location' => 'modules/nodereference/nodereference.module:0', + 'lid' => '1645', + 'location' => '/?q=zu/admin/build/translate/refresh', 'textgroup' => 'default', - 'source' => 'nodereference', - 'version' => 'none', + 'source' => 'To search and translate strings, use the translation interface pages.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '369', - 'location' => 'modules/nodereference/nodereference.info:0', + 'lid' => '1646', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Node Reference', - 'version' => 'none', + 'source' => 'Import translation', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '370', - 'location' => 'modules/nodereference/nodereference.info:0', + 'lid' => '1647', + 'location' => '/?q=zu/admin/build/translate/refresh', 'textgroup' => 'default', - 'source' => 'Defines a field type for referencing one node from another.', - 'version' => 'none', + 'source' => 'Important: To configure which Input formats are safe for translation, visit the configure strings page before refreshing your strings.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '371', - 'location' => 'modules/number/number.module:41', + 'lid' => '1648', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Integer', - 'version' => 'none', + 'source' => 'Language file', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '372', - 'location' => 'modules/number/number.module:42', + 'lid' => '1649', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Store a number in the database as an integer.', - 'version' => 'none', + 'source' => 'A Gettext Portable Object (.po) file.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '373', - 'location' => 'modules/number/number.module:49', + 'lid' => '1650', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Decimal', - 'version' => 'none', + 'source' => 'Import into', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '374', - 'location' => 'modules/number/number.module:50', + 'lid' => '1651', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Store a number in the database in a fixed decimal format.', - 'version' => 'none', + 'source' => 'Choose the language you want to add strings into. If you choose a language which is not yet set up, it will be added.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '375', - 'location' => 'modules/number/number.module:57', + 'lid' => '1652', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Float', - 'version' => 'none', + 'source' => 'Text group', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '376', - 'location' => 'modules/number/number.module:58', + 'lid' => '1653', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Store a number in the database in a floating point format.', - 'version' => 'none', + 'source' => 'Imported translations will be added to this text group.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '377', - 'location' => 'modules/number/number.module:76', + 'lid' => '1654', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Minimum', - 'version' => 'none', + 'source' => 'Mode', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '378', - 'location' => 'modules/number/number.module:81', + 'lid' => '1655', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Maximum', - 'version' => 'none', + 'source' => 'Strings in the uploaded file replace existing ones, new ones are added', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '379', - 'location' => 'modules/number/number.module:88', + 'lid' => '1656', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Precision', - 'version' => 'none', + 'source' => 'Existing strings are kept, only new strings are added', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '380', - 'location' => 'modules/number/number.module:89', + 'lid' => '1657', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'The total number of digits to store in the database, including those to the right of the decimal.', - 'version' => 'none', + 'source' => 'This page imports the translated strings contained in an individual Gettext Portable Object (.po) file. Normally distributed as part of a translation package (each translation package may contain several .po files), a .po file may need to be imported after off-line editing in a Gettext translation editor. Importing an individual .po file may be a lengthy process.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '381', - 'location' => 'modules/number/number.module:95', + 'lid' => '1658', + 'location' => '/?q=zu/admin/build/translate/import', 'textgroup' => 'default', - 'source' => 'Scale', - 'version' => 'none', + 'source' => 'Note that the .po files within a translation package are imported automatically (if available) when new modules or themes are enabled, or as new languages are added. Since this page only allows the import of one .po file at a time, it may be simpler to download and extract a translation package into your Drupal installation directory and add the language (which automatically imports all .po files within the package). Translation packages are available for download on the Drupal translation page.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '382', - 'location' => 'modules/number/number.module:96', + 'lid' => '1659', + 'location' => '/?q=zu/admin/settings/language/i18n/variables', 'textgroup' => 'default', - 'source' => 'The number of digits to the right of the decimal.', - 'version' => 'none', + 'source' => 'Variable name', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '383', - 'location' => 'modules/number/number.module:102', + 'lid' => '1660', + 'location' => '/?q=zu/admin/settings/language/i18n/variables', 'textgroup' => 'default', - 'source' => 'Decimal marker', - 'version' => 'none', + 'source' => 'Is multilingual', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '384', - 'location' => 'modules/number/number.module:103', + 'lid' => '1661', + 'location' => '/?q=zu/admin/settings/language/i18n/variables', 'textgroup' => 'default', - 'source' => 'The character users will input to mark the decimal point in forms.', - 'version' => 'none', + 'source' => 'Has translations', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '385', - 'location' => 'modules/number/number.module:109', + 'lid' => '1662', + 'location' => '/?q=zu/admin/settings/language/i18n/variables', 'textgroup' => 'default', - 'source' => 'Prefix', - 'version' => 'none', + 'source' => 'Delete all existing translations for variables.', + 'version' => '6.38-dev', )) ->values(array( - 'lid' => '386', - 'location' => 'modules/number/number.module:112', + 'lid' => '1663', + 'location' => '/?q=zu/admin/settings/language/i18n/variables', 'textgroup' => 'default', - 'source' => 'Define a string that should be prefixed to the value, like $ or €. Leave blank for none. Separate singular and plural values with a pipe (pound|pounds).', - 'version' => 'none', + 'source' => 'Delete all translations', + 'version' => '6.38-dev', +)) +->execute(); + +$connection->schema()->createTable('locales_target', array( + 'fields' => array( + 'lid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ), + 'translation' => array( + 'type' => 'blob', + 'not null' => TRUE, + 'size' => 'normal', + ), + 'language' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '12', + 'default' => '', + ), + 'plid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ), + 'plural' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ), + 'i18n_status' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ), + ), + 'primary key' => array( + 'language', + 'lid', + 'plural', + ), + 'indexes' => array( + 'lid' => array( + 'lid', + ), + 'plid' => array( + 'plid', + ), + 'plural' => array( + 'plural', + ), + ), + 'mysql_character_set' => 'utf8', +)); + +$connection->insert('locales_target') +->fields(array( + 'lid', + 'translation', + 'language', + 'plid', + 'plural', + 'i18n_status', )) ->values(array( - 'lid' => '387', - 'location' => 'modules/number/number.module:116', - 'textgroup' => 'default', - 'source' => 'Suffix', - 'version' => 'none', + 'lid' => '5', + 'translation' => "Le module Content, composant obligatoire du kit CCK (Content Construction Kit) permet aux administrateurs d'associer des champs personnalisés à des types de contenus. Au sein de Drupal, les types de contenus servent à définir les caractéristiques d'une publication, y compris le titre et la description des champs affichés sur ses pages \"ajouter\" et \"éditer\". Le module Content (et les modules auxiliaires inclus dans CCK) permet d'ajouter des champs personnalisés en plus des champs par défaut \"Titre\" et \"Corps\". Les fonctionnalités de CCK sont accessible via différents onglets sur la page d'administration des types de contenus. (Voir la page d'aide du module Node pour plus d'informations sur les types de contenus).", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '6', + 'translation' => "Lorsque vous ajoutez un champ personnalisé à un type de contenu, vous déterminez son type (c'est-à-dire s'il doit contenir du texte, des nombres ou des références à d'autres objets) et la façon dont il doit être affiché (en tant que champ ou zone de texte, liste de sélection, case à cocher, bouton radio, ou champ à auto-complètement). Un champ peut présenter plusieurs valeurs (par exemple, une \"personne\" peut disposer de plusieurs adresses courriel) ou une seule (par exemple, un \"employé\" possède un numéro d'identification unique). À mesure que vous ajoutez et modifiez des champs, CCK ajuste automatiquement la structure de la base de données en fonction. CCK propose également un certain nombre d'autres fonctionnalités, par exemple un cache intelligent pour vos données personnalisées, des fonctionnalités d'import et d'export pour les définitions de types de contenus, ainsi qu'une intégration à d'autres modules provenant des contributions.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '7', + 'translation' => "Des types de champs personnalisés sont proposés par plusieurs modules optionnels inclus dans CCK (chaque module fournissant un type différent). La page des modules vous permet d'activer ou de désactiver des composants CCK. Une installation par défaut de CCK inclut :", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '8', + 'translation' => "Number, qui ajoute des types de champs numériques (formats entier, décimal ou réel à virgule flottante). Vous pouvez définir un jeu ou un intervalle de valeurs autorisées. Divers formats sont disponibles pour l'affichage des données numériques.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '9', + 'translation' => "Text, qui ajoute des types de champs de texte. Un champ texte peut contenir du texte brut uniquement ou, optionnellement, utiliser les filtres des formats d'entrée que propose Drupal pour gérer en toute sécurité des textes enrichis. Les champs de saisie de texte peuvent être constitués d'une seule ligne (champ texte), de plusieurs lignes (zone de texte) ou, pour un meilleur contrôle des valeurs saisies, une liste de sélection, des cases à cocher ou des boutons radio. Si besoin, CCK peut valider les saisies sur la base d'un ensemble de valeurs autorisées.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '10', + 'translation' => 'Node Reference, qui crée des références personnalisées entre nœuds de Drupal. En ajoutant, par exemple, un champ nodereference et deux types de contenus différents, vous pouvez facilement créer des relations complexes de type parent/enfant entre données (par exemple plusieurs nœuds "employé" peuvent présenter un champ nodereference pointant vers un même nœud "employeur").', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '11', + 'translation' => "User reference, qui crée des références personnalisées vers les comptes des utilisateurs de votre site. En ajoutant un champ userreference, vous pouvez créer des relations complexes entre les utilisateurs de votre site et des publications. Ainsi, pour tracer l'implication d'un utilisateur dans une publication (au delà du champ Drupal standard Écrit par), vous pouvez ajouter à un type de contenu un champ userreference intitulé \"Édité par\" pour enregistrer un lien vers la page du compte utilisateur ayant édité la publication.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '12', + 'translation' => "Fieldgroup, qui crée des groupes de champs liés. Les groupe de champ peuvent être repliés, et vous pouvez choisir qu'ils soient dépliés ou repliés par défaut. L'ordre des groupes de champs, ainsi que l'ordre des champs au sein d'un groupe, est géré grâce à l'interface par glisser-déposer fournie par le module Content.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '13', + 'translation' => "Pour plus d'informations, reportez-vous à l'entrée de manuel en ligne relative à CCK ou à la page du projet CCK.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '14', + 'translation' => 'Configurez ici la manière dont les champs et étiquettes de champs de ce type de contenu doivent être affichées, lorsque le contenu est vu en mode résumé ou en pleine page.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '15', + 'translation' => "Configurez ici la façon dont les champs de ce type de contenu doivent être affichés lorsqu'il est rendu dans les contextes suivants.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '16', + 'translation' => "Contrôlez ici l'ordre des champs dans le formulaire de saisie.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '17', + 'translation' => 'Ce champ est obligatoire.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '18', + 'translation' => '!title : !required', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '19', + 'translation' => 'Ordre', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '20', + 'translation' => 'Élément de flux RSS', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', +)) +->values(array( + 'lid' => '21', + 'translation' => 'Index de recherche', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '388', - 'location' => 'modules/number/number.module:119', - 'textgroup' => 'default', - 'source' => 'Define a string that should suffixed to the value, like m², m/s², kb/s. Leave blank for none. Separate singular and plural values with a pipe (pound|pounds).', - 'version' => 'none', + 'lid' => '22', + 'translation' => 'Résultat de recherche', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '389', - 'location' => 'modules/number/number.module:123 modules/text/text.module:82', - 'textgroup' => 'default', - 'source' => 'Allowed values', - 'version' => 'none', + 'lid' => '23', + 'translation' => 'Langue', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '390', - 'location' => 'examples/example_field.php:174 modules/number/number.module:129, modules/text/text.module:88', - 'textgroup' => 'default', - 'source' => 'Allowed values list', - 'version' => 'none', + 'lid' => '24', + 'translation' => 'Taxonomie', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '391', - 'location' => 'modules/number/number.module:116, modules/text/text.module:81', - 'textgroup' => 'default', - 'source' => 'The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database, and it must match the field storage type (%type). The label is optional, and the key will be used as the label if no label is specified.
                          Allowed HTML tags: @tags', - 'version' => 'none', + 'lid' => '25', + 'translation' => 'Fichiers attachés', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '392', - 'location' => 'examples/example_field.php:191 modules/number/number.module:147, modules/text/text.module:106', - 'textgroup' => 'default', - 'source' => 'Advanced usage only: PHP code that returns a keyed array of allowed values. Should not include <?php ?> delimiters. If this field is filled out, the array returned by this code will override the allowed values list above.', - 'version' => 'none', + 'lid' => '26', + 'translation' => 'Mise à jour du type de champ %type avec le module %module.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '393', - 'location' => 'modules/number/number.module:155 modules/text/text.module:114', - 'textgroup' => 'default', - 'source' => 'This PHP code was set by an administrator and will override the allowed values list above.', - 'version' => 'none', + 'lid' => '27', + 'translation' => 'Mise à jour du type de widget %widget avec le module %module.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '394', - 'location' => 'modules/number/number.module:178, modules/text/text.module:132', - 'textgroup' => 'default', - 'source' => '@label (!name) - Allowed values', - 'version' => 'none', + 'lid' => '28', + 'translation' => "Utiliser du code PHP pour le paramétrage des champs (dangereux - à n'autoriser qu'avec précautions)", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '395', - 'location' => 'modules/number/number.module:162', - 'textgroup' => 'default', - 'source' => '"Minimum" must be a number.', - 'version' => 'none', + 'lid' => '29', + 'translation' => 'Gérer les champs', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '396', - 'location' => 'modules/number/number.module:165', - 'textgroup' => 'default', - 'source' => '"Maximum" must be a number.', - 'version' => 'none', + 'lid' => '30', + 'translation' => 'Afficher les champs', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '397', - 'location' => 'modules/number/number.module:219', - 'textgroup' => 'default', - 'source' => '%name: the value may be no smaller than %min.', - 'version' => 'none', + 'lid' => '31', + 'translation' => 'Général', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '398', - 'location' => 'modules/number/number.module:222', - 'textgroup' => 'default', - 'source' => '%name: the value may be no larger than %max.', - 'version' => 'none', + 'lid' => '32', + 'translation' => 'Avancé', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '399', - 'location' => 'modules/number/number.module:235, modules/text/text.module:156', - 'textgroup' => 'default', - 'source' => '%name: illegal value.', - 'version' => 'none', + 'lid' => '33', + 'translation' => 'Supprimer un champ', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '400', - 'location' => 'modules/number/number.module:263', - 'textgroup' => 'default', - 'source' => 'unformatted', - 'version' => 'none', + 'lid' => '34', + 'translation' => 'Content', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '401', - 'location' => 'examples/example_field.php:476 examples/simple_field.php:400, modules/number/number.module:342 modules/text/text.module:264', - 'textgroup' => 'default', - 'source' => 'Text field', - 'version' => 'none', + 'lid' => '35', + 'translation' => 'Permet aux administrateurs de définir des nouveaux types de contenu.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '402', - 'location' => 'modules/number/number.module:512', - 'textgroup' => 'default', - 'source' => 'Only numbers and decimals are allowed in %field.', - 'version' => 'none', + 'lid' => '36', + 'translation' => 'CCK', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '403', - 'location' => 'modules/number/number.module:535', - 'textgroup' => 'default', - 'source' => 'Only numbers are allowed in %field.', - 'version' => 'none', + 'lid' => '37', + 'translation' => 'Texte', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '404', - 'location' => 'modules/number/number.module:559', - 'textgroup' => 'default', - 'source' => 'Only numbers and the decimal character (%decimal) are allowed in %field.', - 'version' => 'none', + 'lid' => '38', + 'translation' => "Les valeurs possibles pour ce champ. Saisissez une valeur par ligne, sous la forme clé|libellé. La clé est la valeur enregistrée dans la base de données, et elle doit correspondre au type de stockage du champ, %type. Le libellé est optionnel et, s'il n'est pas spécifié, la clé sera utilisée comme libellé.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '405', - 'location' => 'modules/number/number.module:0', - 'textgroup' => 'default', - 'source' => 'number', - 'version' => 'none', + 'lid' => '39', + 'translation' => 'Zone de texte', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '406', - 'location' => 'modules/number/number.info:0', - 'textgroup' => 'default', - 'source' => 'Number', - 'version' => 'none', + 'lid' => '40', + 'translation' => 'Ôter', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '407', - 'location' => 'modules/number/number.info:0', - 'textgroup' => 'default', - 'source' => 'Defines numeric field types.', - 'version' => 'none', + 'lid' => '41', + 'translation' => 'Basique', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '408', - 'location' => 'modules/optionwidgets/optionwidgets.module:19', - 'textgroup' => 'default', - 'source' => 'Create a list of options as a list in Allowed values list or as an array in PHP code. These values will be the same for %field in all content types.', - 'version' => 'none', + 'lid' => '42', + 'translation' => 'Résumé', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '409', - 'location' => 'modules/optionwidgets/optionwidgets.module:12', - 'textgroup' => 'default', - 'source' => "For a 'single on/off checkbox' widget, define the 'off' value first, then the 'on' value in the Allowed values section. Note that the checkbox will be labeled with the label of the 'on' value.", - 'version' => 'none', + 'lid' => '43', + 'translation' => 'Nœud complet', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '410', - 'location' => 'modules/optionwidgets/optionwidgets.module:15', - 'textgroup' => 'default', - 'source' => "The 'checkboxes/radio buttons' widget will display checkboxes if the multiple values option is selected for this field, otherwise radios will be displayed.", - 'version' => 'none', + 'lid' => '44', + 'translation' => 'RSS', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '411', - 'location' => 'modules/optionwidgets/optionwidgets.module:37', - 'textgroup' => 'default', - 'source' => "You need to specify the 'allowed values' for this field.", - 'version' => 'none', + 'lid' => '45', + 'translation' => 'Recherche', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '412', - 'location' => 'modules/optionwidgets/optionwidgets.module:78', - 'textgroup' => 'default', - 'source' => 'Single on/off checkbox', - 'version' => 'none', + 'lid' => '46', + 'translation' => 'Formulaire du module node.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '413', - 'location' => 'modules/optionwidgets/optionwidgets.module:331', - 'textgroup' => 'default', - 'source' => '%name: this field cannot hold more that @count values.', - 'version' => 'none', + 'lid' => '47', + 'translation' => 'Formulaire du module locale.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '414', - 'location' => 'modules/optionwidgets/optionwidgets.module:364', - 'textgroup' => 'default', - 'source' => 'N/A', - 'version' => 'none', + 'lid' => '48', + 'translation' => 'Paramètres du menu', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '415', - 'location' => 'modules/optionwidgets/optionwidgets.module:420', - 'textgroup' => 'default', - 'source' => '- None -', - 'version' => 'none', + 'lid' => '49', + 'translation' => 'Formulaire du module menu.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '416', - 'location' => 'modules/optionwidgets/optionwidgets.module:0', - 'textgroup' => 'default', - 'source' => 'optionwidgets', - 'version' => 'none', + 'lid' => '50', + 'translation' => 'Formulaire du module taxonomy.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '417', - 'location' => 'modules/optionwidgets/optionwidgets.info:0', - 'textgroup' => 'default', - 'source' => 'Option Widgets', - 'version' => 'none', + 'lid' => '51', + 'translation' => 'Livre', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '418', - 'location' => 'modules/optionwidgets/optionwidgets.info:0', - 'textgroup' => 'default', - 'source' => 'Defines selection, check box and radio button widgets for text and numeric fields.', - 'version' => 'none', + 'lid' => '52', + 'translation' => 'Formulaire du module livre (book).', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '419', - 'location' => 'modules/text/text.module:50', - 'textgroup' => 'default', - 'source' => 'Store text in the database.', - 'version' => 'none', + 'lid' => '53', + 'translation' => 'Titre du sondage', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '420', - 'location' => 'examples/example_field.php:158;388 examples/simple_field.php:332, modules/text/text.module:66;209, modules/userreference/userreference.module:151', - 'textgroup' => 'default', - 'source' => 'Plain text', - 'version' => 'none', + 'lid' => '54', + 'translation' => 'Titre du module sondage (Poll)', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '421', - 'location' => 'examples/example_field.php:158 modules/text/text.module:66', - 'textgroup' => 'default', - 'source' => 'Filtered text (user selects input format)', - 'version' => 'none', + 'lid' => '55', + 'translation' => 'Choix du sondage', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '422', - 'location' => 'examples/example_field.php:161 modules/text/text.module:69', - 'textgroup' => 'default', - 'source' => 'Text processing', - 'version' => 'none', + 'lid' => '56', + 'translation' => 'Choix du module sondage (poll).', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '423', - 'location' => 'examples/example_field.php:167 examples/simple_field.php:164, modules/text/text.module:75', - 'textgroup' => 'default', - 'source' => 'Maximum length', - 'version' => 'none', + 'lid' => '57', + 'translation' => 'Paramètrage du sondage', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '424', - 'location' => 'examples/example_field.php:170 examples/simple_field.php:167, modules/text/text.module:78', - 'textgroup' => 'default', - 'source' => 'The maximum length of the field in characters. Leave blank for an unlimited size.', - 'version' => 'none', + 'lid' => '58', + 'translation' => 'Paramètres du module sondage (poll).', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '425', - 'location' => 'modules/text/text.module:159', - 'textgroup' => 'default', - 'source' => '%name: the value may not be longer than %max characters.', - 'version' => 'none', + 'lid' => '59', + 'translation' => 'Formulaire du module upload.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '426', - 'location' => 'examples/example_field.php:383 examples/simple_field.php:327, modules/text/text.module:204 modules/userreference/userreference.module:146', - 'textgroup' => 'default', - 'source' => 'Default', - 'version' => 'none', + 'lid' => '60', + 'translation' => 'contenu', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '427', - 'location' => 'examples/example_field.php:393 modules/text/text.module:214', - 'textgroup' => 'default', - 'source' => 'Trimmed', - 'version' => 'none', + 'lid' => '61', + 'translation' => 'Champs', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '428', - 'location' => 'modules/text/text.module:272', - 'textgroup' => 'default', - 'source' => 'Text area (multiple rows)', - 'version' => 'none', + 'lid' => '62', + 'translation' => "Les mises à jour des modules liés à CCK ne sont pas exécutées tant que les modules ne sont pas activés sur la page d'administration des modules. Lorsque vous les activerez, vous devrez retourner sur la page update.php et exécuter les mises à jour restantes.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '429', - 'location' => 'modules/text/text.module:316', - 'textgroup' => 'default', - 'source' => 'Size of textfield', - 'version' => 'none', + 'lid' => '63', + 'translation' => "!module.module possède des mises à jour mais ne peut pas être mis à jour car content.module n'est pas activé.
                          Le cas échéant, lors de l'activation de content.module, vous devrez exécuter à nouveau le script de mise à jour. Vous continuerez à voir ce message jusqu'à ce que le module soit activé et les mises à jour exécutées.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '430', - 'location' => 'examples/example_field.php:551 examples/simple_field.php:430, modules/text/text.module:324', - 'textgroup' => 'default', - 'source' => 'Rows', - 'version' => 'none', + 'lid' => '64', + 'translation' => "!module.module possède des mises à jour et est disponible dans le répertoire des modules, mais n'est pas activé.
                          Le cas échéant, lorsque vous l'aurez activé, vous devrez ré-exécuter le script de mise à jour. Vous continuerez à voir ce message jusqu'à l'activation du module et l'exécution des mises à jour. ", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '431', - 'location' => 'modules/text/text.module:0', - 'textgroup' => 'default', - 'source' => 'text', - 'version' => 'none', + 'lid' => '65', + 'translation' => 'Des mises à jour sont toujours en attente. Veuillez retourner sur update.php et exécuter les mises à jour restarntes.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '432', - 'location' => 'modules/text/text.info:0', - 'textgroup' => 'default', - 'source' => 'Defines simple text field types.', - 'version' => 'none', + 'lid' => '66', + 'translation' => 'CCK - Aucune Intégration aux Vues', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '433', - 'location' => 'modules/userreference/userreference.rules.inc:15', - 'textgroup' => 'default', - 'source' => 'Load a referenced user', - 'version' => 'none', + 'lid' => '67', + 'translation' => 'L"intégration de CCK avec le module Views requiert Views 6.x-2.0-rc2 ou une version supérieure.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '434', - 'location' => 'modules/userreference/userreference.rules.inc:19', - 'textgroup' => 'default', - 'source' => 'Content containing the user reference field', - 'version' => 'none', + 'lid' => '68', + 'translation' => 'Nom', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '435', - 'location' => 'modules/userreference/userreference.rules.inc:25', - 'textgroup' => 'default', - 'source' => 'Referenced user', - 'version' => 'none', + 'lid' => '69', + 'translation' => 'Type', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '436', - 'location' => 'modules/userreference/userreference.rules.inc:29', - 'textgroup' => 'default', - 'source' => 'Note that if the field has multiple values, only the first user will be loaded.', - 'version' => 'none', + 'lid' => '70', + 'translation' => 'Description', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '437', - 'location' => 'modules/userreference/userreference.rules.inc:52', - 'textgroup' => 'default', - 'source' => 'There are no userreference fields defined.', - 'version' => 'none', + 'lid' => '71', + 'translation' => 'Opérations', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '438', - 'location' => 'modules/userreference/userreference.module:52', - 'textgroup' => 'default', - 'source' => 'User reference', - 'version' => 'none', + 'lid' => '72', + 'translation' => 'éditer', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '439', - 'location' => 'modules/userreference/userreference.module:53', - 'textgroup' => 'default', - 'source' => 'Store the ID of a related user as an integer value.', - 'version' => 'none', + 'lid' => '73', + 'translation' => 'gérer les champs', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '440', - 'location' => 'modules/userreference/userreference.module:67', - 'textgroup' => 'default', - 'source' => 'User roles that can be referenced', - 'version' => 'none', + 'lid' => '74', + 'translation' => 'supprimer', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '441', - 'location' => 'modules/userreference/userreference.module:73', - 'textgroup' => 'default', - 'source' => 'User status that can be referenced', - 'version' => 'none', + 'lid' => '75', + 'translation' => 'Aucun type de contenu disponible.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '442', - 'location' => 'modules/userreference/userreference.module:75', - 'textgroup' => 'default', - 'source' => 'Active', - 'version' => 'none', + 'lid' => '76', + 'translation' => '» Ajouter un nouveau type de contenu', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '443', - 'location' => 'modules/userreference/userreference.module:75', - 'textgroup' => 'default', - 'source' => 'Blocked', - 'version' => 'none', + 'lid' => '77', + 'translation' => 'Nom du champ', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '444', - 'location' => 'modules/userreference/userreference.module:94', - 'textgroup' => 'default', - 'source' => 'Advanced - Users that can be referenced (View)', - 'version' => 'none', + 'lid' => '78', + 'translation' => 'Type de champ', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '445', - 'location' => 'modules/userreference/userreference.module:101', - 'textgroup' => 'default', - 'source' => 'View used to select the users', - 'version' => 'none', + 'lid' => '79', + 'translation' => 'Utilisé dans', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '446', - 'location' => 'modules/userreference/userreference.module:104', - 'textgroup' => 'default', - 'source' => '

                          Choose the "Views module" view that selects the users that can be referenced.
                          Note:

                          ', - 'version' => 'none', + 'lid' => '80', + 'translation' => '@field_name (Verrouillé)', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '447', - 'location' => 'modules/userreference/userreference.module:105;118', - 'textgroup' => 'default', - 'source' => "
                          • Only views that have fields will work for this purpose.
                          • This will discard the \"Referenceable Roles\" and \"Referenceable Status\" settings above. Use the view's \"filters\" section instead.
                          • Use the view's \"fields\" section to display additional informations about candidate users on user creation/edition form.
                          • Use the view's \"sort criteria\" section to determine the order in which candidate users will be displayed.
                          ", - 'version' => 'none', + 'lid' => '81', + 'translation' => "Aucun champ n'est pour l'instant défini sur l'ensemble des types de contenu.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '448', - 'location' => 'modules/userreference/userreference.module:117', - 'textgroup' => 'default', - 'source' => '

                          The list of user that can be referenced can be based on a "Views module" view but no appropriate views were found.
                          Note:

                          ', - 'version' => 'none', + 'lid' => '82', + 'translation' => "Ce type de contenu possède des champs inactifs. Les champs inactifs ne sont pas inclus dans la liste de champs disponibles, jusqu'à l'activation des modules correspondants.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '449', - 'location' => 'modules/userreference/userreference.module:196', - 'textgroup' => 'default', - 'source' => '%name: invalid user.', - 'version' => 'none', + 'lid' => '83', + 'translation' => '!field (!field_name) est un champ inactif de type !field_type, qui utilise un widget de type !widget_type.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '450', - 'location' => 'modules/userreference/userreference.module:349', - 'textgroup' => 'default', - 'source' => 'Select the method used to collect autocomplete suggestions. Note that Contains can cause performance issues on sites with thousands of users.', - 'version' => 'none', + 'lid' => '84', + 'translation' => 'Configurer', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '451', - 'location' => 'modules/userreference/userreference.module:357', - 'textgroup' => 'default', - 'source' => 'Reverse link', - 'version' => 'none', + 'lid' => '85', + 'translation' => 'Verrouillé', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '452', - 'location' => 'modules/userreference/userreference.module:359', - 'textgroup' => 'default', - 'source' => 'If selected, a reverse link back to the referencing node will displayed on the referenced user record.', - 'version' => 'none', + 'lid' => '86', + 'translation' => '- Sélectionnez un type de champ -', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '453', - 'location' => 'modules/userreference/userreference.module:594', - 'textgroup' => 'default', - 'source' => '%name: found no valid user with that name.', - 'version' => 'none', + 'lid' => '87', + 'translation' => '- Sélectionnez un widget -', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '454', - 'location' => 'modules/userreference/userreference.module:887', - 'textgroup' => 'default', - 'source' => 'Related content', - 'version' => 'none', + 'lid' => '88', + 'translation' => 'Étiquette', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '455', - 'location' => 'modules/userreference/userreference.module:15', - 'textgroup' => 'default', - 'source' => 'Userreference autocomplete', - 'version' => 'none', + 'lid' => '89', + 'translation' => 'Nom du champ (a-z, 0-9, _)', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '456', - 'location' => 'modules/userreference/userreference.module:0', - 'textgroup' => 'default', - 'source' => 'userreference', - 'version' => 'none', + 'lid' => '90', + 'translation' => 'Type de données à stocker.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '457', - 'location' => 'modules/userreference/userreference.info:0', - 'textgroup' => 'default', - 'source' => 'User Reference', - 'version' => 'none', + 'lid' => '91', + 'translation' => "Elément du formulaire pour l'édition des données.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '458', - 'location' => 'modules/userreference/userreference.info:0', - 'textgroup' => 'default', - 'source' => 'Defines a field type for referencing a user from a node.', - 'version' => 'none', + 'lid' => '92', + 'translation' => '- Sélectionnez un champ existant -', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '459', - 'location' => 'includes/content.admin.inc:212', - 'textgroup' => 'default', - 'source' => 'Weight', - 'version' => 'none', + 'lid' => '93', + 'translation' => 'Champ à partager', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '460', - 'location' => 'theme/content-admin-field-overview-form.tpl.php:53', - 'textgroup' => 'default', - 'source' => 'Add', - 'version' => 'none', + 'lid' => '94', + 'translation' => 'Nom du groupe (a-z, 0-9, _)', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '461', - 'location' => 'theme/content-admin-field-overview-form.tpl.php:59', - 'textgroup' => 'default', - 'source' => 'New field', - 'version' => 'none', + 'lid' => '95', + 'translation' => 'Enregistrer', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '462', - 'location' => 'theme/content-admin-field-overview-form.tpl.php:72', - 'textgroup' => 'default', - 'source' => 'Existing field', - 'version' => 'none', + 'lid' => '96', + 'translation' => 'Ajouter un nouveau champ : vous devez fournir une étiquette.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '463', - 'location' => 'theme/content-admin-field-overview-form.tpl.php:84', - 'textgroup' => 'default', - 'source' => 'New group', - 'version' => 'none', + 'lid' => '97', + 'translation' => 'Ajouter un nouveau champ : vous devez fournir un nom de champ.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '464', - 'location' => 'theme/theme.inc:11', - 'textgroup' => 'default', - 'source' => 'Add fields and groups to the content type, and arrange them on content display and input forms.', - 'version' => 'none', + 'lid' => '98', + 'translation' => "Ajouter un nouveau champ : le nom de champ %field_name n'est pas valide. Le nom doit seulement contenir des lettre minuscules non accentuées, des nombres, et des underscores. ", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '465', - 'location' => 'theme/theme.inc:13', - 'textgroup' => 'default', - 'source' => 'You can add a field to a group by dragging it below and to the right of the group.', - 'version' => 'none', + 'lid' => '99', + 'translation' => "Ajouter un nouveau champ : le nom de champ %field_name est trop long. Le nom est limité à 32 caractères, en comptant le préfixe 'field_'.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '466', - 'location' => 'theme/theme.inc:16', - 'textgroup' => 'default', - 'source' => 'Note: Installing the Advanced help module will let you access more and better help.', - 'version' => 'none', + 'lid' => '100', + 'translation' => "Ajouter un nouveau champ : le nom 'field_instance' est un nom réservé.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) -->values(array( - 'lid' => '467', - 'location' => 'theme/theme.inc:116', - 'textgroup' => 'default', - 'source' => "Use the 'Exclude' checkbox to exclude an item from the !content value passed to the node template.", - 'version' => 'none', +->values(array( + 'lid' => '101', + 'translation' => 'Ajouter un nouveau champ : le nom du champ %field_name existe déjà.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '468', - 'location' => 'theme/content-edit.js:0', - 'textgroup' => 'default', - 'source' => 'Remove this item', - 'version' => 'none', + 'lid' => '102', + 'translation' => 'Ajouter un nouveau champ : vous devez sélectionner un type de champ.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '469', - 'location' => 'content.module:156 includes/content.admin.inc:528', - 'textgroup' => 'default', - 'source' => 'Add field', - 'version' => 'none', + 'lid' => '103', + 'translation' => 'Ajouter un nouveau champ : vous devez sélectionner un widget.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '470', - 'location' => 'examples/example_field.php:278 modules/number/number.module:228, modules/text/text.module:160', - 'textgroup' => 'default', - 'source' => 'Illegal value for %name.', - 'version' => 'none', + 'lid' => '104', + 'translation' => 'Ajouter un nouveau champ : widget non valide.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '471', - 'location' => 'examples/example_field.php:287 examples/simple_field.php:231, modules/text/text.module:169', - 'textgroup' => 'default', - 'source' => '%label is longer than %max characters.', - 'version' => 'none', + 'lid' => '105', + 'translation' => 'Ajouter un champ existant : vous devez fournir une étiquette.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '472', - 'location' => 'examples/example_field.php:560 examples/simple_field.php:438, modules/text/text.module:333', - 'textgroup' => 'default', - 'source' => '"Rows" must be a positive integer.', - 'version' => 'none', + 'lid' => '106', + 'translation' => 'Ajouter un champ existant : vous devez sélectionner un champ.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '473', - 'location' => 'modules/number/number.module:133 modules/text/text.module:92', - 'textgroup' => 'default', - 'source' => 'The possible values this field can contain. Enter one value per line, in the format key|label. The key is the value that will be stored in the database and it must match the field storage type, %type. The label is optional and the key will be used as the label if no label is specified.
                          Allowed HTML tags: @tags', - 'version' => 'none', + 'lid' => '107', + 'translation' => 'Ajouter un champ existant: vous devez sélectionner un widget.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '474', - 'location' => 'includes/content.admin.inc:33', - 'textgroup' => 'default', - 'source' => 'add field', - 'version' => 'none', + 'lid' => '108', + 'translation' => 'Ajouter un champ existant : widget non valide.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '475', - 'location' => 'includes/content.admin.inc:112;291', - 'textgroup' => 'default', - 'source' => 'There are no fields configured for this content type. You can !link.', - 'version' => 'none', + 'lid' => '109', + 'translation' => "Un problème est survenu à la création du champ '%label'.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '476', - 'location' => 'includes/content.admin.inc:113;292', - 'textgroup' => 'default', - 'source' => 'Add a new field', - 'version' => 'none', + 'lid' => '110', + 'translation' => "Le champ %label n'a pas pu être ajouté au type de contenu car il est verrouillé.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '477', - 'location' => 'includes/content.admin.inc:137', - 'textgroup' => 'default', - 'source' => 'To change the order of a field, grab a drag-and-drop handle under the Label column and drag the field to a new location in the list. (Grab a handle by clicking and holding the mouse while hovering over a handle icon.) Remember that your changes will not be saved until you click the Save button at the bottom of the page.', - 'version' => 'none', + 'lid' => '111', + 'translation' => "Un problème est survenu lors de l'ajout du champ '%label'.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '478', - 'location' => 'includes/content.admin.inc:477', - 'textgroup' => 'default', - 'source' => 'No field modules are enabled. You need to enable one, such as text.module, before you can add new fields.', - 'version' => 'none', + 'lid' => '112', + 'translation' => "Il n'y a aucun champ configuré pour ce type de contenu. Vous pouvez ajouter de nouveaux champs sur la page Gérer les champs.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '479', - 'location' => 'includes/content.admin.inc:519', - 'textgroup' => 'default', - 'source' => 'Add existing field', - 'version' => 'none', + 'lid' => '113', + 'translation' => 'Au dessus', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '480', - 'location' => 'includes/content.admin.inc:590', - 'textgroup' => 'default', - 'source' => 'Create new field', - 'version' => 'none', + 'lid' => '114', + 'translation' => 'Sur la même ligne', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '481', - 'location' => 'includes/content.admin.inc:606', - 'textgroup' => 'default', - 'source' => 'The machine-readable name of the field.', - 'version' => 'none', + 'lid' => '115', + 'translation' => 'Inclure', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '482', - 'location' => 'includes/content.admin.inc:610', - 'textgroup' => 'default', - 'source' => 'This name cannot be changed.', - 'version' => 'none', + 'lid' => '116', + 'translation' => 'Exclure', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '483', - 'location' => 'includes/content.admin.inc:618', - 'textgroup' => 'default', - 'source' => "This name cannot be changed later! The name will be prefixed with 'field_' and can include lowercase unaccented letters, numbers, and underscores. The length of the name, including the prefix, is limited to no more than 32 letters.", - 'version' => 'none', + 'lid' => '117', + 'translation' => 'aucune mise en forme', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '484', - 'location' => 'includes/content.admin.inc:636', - 'textgroup' => 'default', - 'source' => 'The type of data you would like to store in the database with this field.', - 'version' => 'none', + 'lid' => '118', + 'translation' => 'simple', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '485', - 'location' => 'includes/content.admin.inc:692', - 'textgroup' => 'default', - 'source' => 'The field name %field_name is invalid. The name must include only lowercase unaccentuated letters, numbers, and underscores.', - 'version' => 'none', + 'lid' => '119', + 'translation' => 'groupe de champs', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '486', - 'location' => 'includes/content.admin.inc:695', - 'textgroup' => 'default', - 'source' => "The field name %field_name is too long. The name is limited to 32 characters, including the 'field_' prefix.", - 'version' => 'none', + 'lid' => '120', + 'translation' => 'groupe de champs - repliable', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '487', - 'location' => 'includes/content.admin.inc:706', - 'textgroup' => 'default', - 'source' => 'The field name %field_name already exists.', - 'version' => 'none', + 'lid' => '121', + 'translation' => 'groupe de champs - replié', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '488', - 'location' => 'includes/content.admin.inc:709', - 'textgroup' => 'default', - 'source' => "The name 'field_instance' is a reserved name.", - 'version' => 'none', + 'lid' => '122', + 'translation' => 'Vos paramètres ont été enregistrés.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '489', - 'location' => 'includes/content.admin.inc:741', - 'textgroup' => 'default', - 'source' => 'Created field %label.', - 'version' => 'none', + 'lid' => '123', + 'translation' => '@type : @field (@label)', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '490', - 'location' => 'includes/content.admin.inc:754', - 'textgroup' => 'default', - 'source' => 'Update field %label.', - 'version' => 'none', + 'lid' => '124', + 'translation' => 'Éditer les informations de base', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '491', - 'location' => 'includes/content.admin.inc:758', - 'textgroup' => 'default', - 'source' => 'There was a problem updating field %label.', - 'version' => 'none', + 'lid' => '125', + 'translation' => 'Le nom lisible par une machine du champ. Ce nom ne peut être changé.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '492', - 'location' => 'includes/content.admin.inc:955', - 'textgroup' => 'default', - 'source' => "Advanced usage only: PHP code that returns a default value. Should not include <?php ?> delimiters. If this field is filled out, the value returned by this code will override any value specified above. Expected format:
                          !sample
                          Using !link_devel's 'devel load' tab on a %type content page might help you figure out the expected format.", - 'version' => 'none', + 'lid' => '126', + 'translation' => "Nom lisible par une personne, destiné à servir d'étiquette pour ce champ au sein du type de contenu '%type'.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '493', - 'location' => 'includes/content.admin.inc:986', - 'textgroup' => 'default', - 'source' => "Select a specific number of values for this field, or 'Unlimited' to provide an 'Add more' button so the users can add as many values as they like.", - 'version' => 'none', + 'lid' => '127', + 'translation' => 'Type de données que vous souhaitez enregistrer, par le biais de ce champ, dans la base de données. Cette option ne peut être modifiée.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '494', - 'location' => 'includes/content.admin.inc:1131', - 'textgroup' => 'default', - 'source' => 'The default value PHP code created @value which is invalid.', - 'version' => 'none', + 'lid' => '128', + 'translation' => 'Type de widget', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '495', - 'location' => 'includes/content.token.inc:62', - 'textgroup' => 'default', - 'source' => 'Formatted HTML link to the node', - 'version' => 'none', + 'lid' => '129', + 'translation' => "Type d'élément de formulaire que vous souhaitez présenter à l'utilisateur lorsqu'il renseigne ce champ dans le type de contenu '%type'.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '496', - 'location' => 'modules/number/number.module:222', - 'textgroup' => 'default', - 'source' => 'The value of %name may be no smaller than %min.', - 'version' => 'none', + 'lid' => '130', + 'translation' => 'Continuer', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '497', - 'location' => 'modules/number/number.module:225', - 'textgroup' => 'default', - 'source' => 'The value of %name may be no larger than %max.', - 'version' => 'none', + 'lid' => '131', + 'translation' => 'Les paramètres basiques du champ %label ont été mis à jour.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '498', - 'location' => 'modules/number/number.module:476', - 'textgroup' => 'default', - 'source' => 'Only numbers and decimals are allowed in %field. %start was changed to %value.', - 'version' => 'none', + 'lid' => '132', + 'translation' => 'Un problème a été rencontré lors de la mise à jour des paramètres basiques du champ %label.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '499', - 'location' => 'modules/number/number.module:494', - 'textgroup' => 'default', - 'source' => 'Only numbers are allowed in %field. %start was changed to %value.', - 'version' => 'none', + 'lid' => '133', + 'translation' => "Êtes-vous certain de vouloir enlever le champ '%field' ?", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '500', - 'location' => 'modules/number/number.module:513', - 'textgroup' => 'default', - 'source' => 'Only numbers and the decimal character (%decimal) are allowed in %field. %start was changed to %value.', - 'version' => 'none', + 'lid' => '134', + 'translation' => 'Si vous avez encore du contenu dans ce champ, il sera perdu. Cette action est irréversible.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '501', - 'location' => 'modules/optionwidgets/optionwidgets.module:10', - 'textgroup' => 'default', - 'source' => 'Create a list of options as a list in Allowed values or as an array in PHP code. These values will be the same for %field in all content types.', - 'version' => 'none', + 'lid' => '135', + 'translation' => 'Annuler', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '502', - 'location' => 'misc/tabledrag.js', - 'textgroup' => 'default', - 'source' => 'Drag to re-order', - 'version' => 'none', + 'lid' => '136', + 'translation' => 'Ce champ est verrouillé et ne peut être supprimé.', + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '503', - 'location' => 'misc/tabledrag.js', - 'textgroup' => 'default', - 'source' => 'Changes made in this table will not be saved until the form is submitted.', - 'version' => 'none', + 'lid' => '137', + 'translation' => "Le champ '%field' de '%type' a été enlevé.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) -->execute(); - -$connection->schema()->createTable('locales_target', array( - 'fields' => array( - 'lid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'size' => 'normal', - 'default' => '0', - ), - 'translation' => array( - 'type' => 'blob', - 'not null' => TRUE, - 'size' => 'normal', - ), - 'language' => array( - 'type' => 'varchar', - 'not null' => TRUE, - 'length' => '12', - 'default' => '', - ), - 'plid' => array( - 'type' => 'int', - 'not null' => TRUE, - 'size' => 'normal', - 'default' => '0', - ), - 'plural' => array( - 'type' => 'int', - 'not null' => TRUE, - 'size' => 'normal', - 'default' => '0', - ), - ), - 'primary key' => array( - 'language', - 'lid', - 'plural', - ), - 'indexes' => array( - 'lid' => array( - 'lid', - ), - 'plid' => array( - 'plid', - ), - 'plural' => array( - 'plural', - ), - ), - 'mysql_character_set' => 'utf8', -)); - -$connection->insert('locales_target') -->fields(array( - 'lid', - 'translation', - 'language', - 'plid', - 'plural', +->values(array( + 'lid' => '138', + 'translation' => "Un problème est survenu à la suppression du champ '%field' du type '%type'.", + 'language' => 'fr', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '5', - 'translation' => "Le module Content, composant obligatoire du kit CCK (Content Construction Kit) permet aux administrateurs d'associer des champs personnalisés à des types de contenus. Au sein de Drupal, les types de contenus servent à définir les caractéristiques d'une publication, y compris le titre et la description des champs affichés sur ses pages \"ajouter\" et \"éditer\". Le module Content (et les modules auxiliaires inclus dans CCK) permet d'ajouter des champs personnalisés en plus des champs par défaut \"Titre\" et \"Corps\". Les fonctionnalités de CCK sont accessible via différents onglets sur la page d'administration des types de contenus. (Voir la page d'aide du module Node pour plus d'informations sur les types de contenus).", + 'lid' => '139', + 'translation' => 'Le champ %field est verouillé et ne peut être édité.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '6', - 'translation' => "Lorsque vous ajoutez un champ personnalisé à un type de contenu, vous déterminez son type (c'est-à-dire s'il doit contenir du texte, des nombres ou des références à d'autres objets) et la façon dont il doit être affiché (en tant que champ ou zone de texte, liste de sélection, case à cocher, bouton radio, ou champ à auto-complètement). Un champ peut présenter plusieurs valeurs (par exemple, une \"personne\" peut disposer de plusieurs adresses courriel) ou une seule (par exemple, un \"employé\" possède un numéro d'identification unique). À mesure que vous ajoutez et modifiez des champs, CCK ajuste automatiquement la structure de la base de données en fonction. CCK propose également un certain nombre d'autres fonctionnalités, par exemple un cache intelligent pour vos données personnalisées, des fonctionnalités d'import et d'export pour les définitions de types de contenus, ainsi qu'une intégration à d'autres modules provenant des contributions.", + 'lid' => '140', + 'translation' => "Informations de base pour '%type'", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '7', - 'translation' => "Des types de champs personnalisés sont proposés par plusieurs modules optionnels inclus dans CCK (chaque module fournissant un type différent). La page des modules vous permet d'activer ou de désactiver des composants CCK. Une installation par défaut de CCK inclut :", + 'lid' => '141', + 'translation' => 'Modifier les informations de base', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '8', - 'translation' => "Number, qui ajoute des types de champs numériques (formats entier, décimal ou réel à virgule flottante). Vous pouvez définir un jeu ou un intervalle de valeurs autorisées. Divers formats sont disponibles pour l'affichage des données numériques.", + 'lid' => '142', + 'translation' => "Paramètres de '%type'", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '9', - 'translation' => "Text, qui ajoute des types de champs de texte. Un champ texte peut contenir du texte brut uniquement ou, optionnellement, utiliser les filtres des formats d'entrée que propose Drupal pour gérer en toute sécurité des textes enrichis. Les champs de saisie de texte peuvent être constitués d'une seule ligne (champ texte), de plusieurs lignes (zone de texte) ou, pour un meilleur contrôle des valeurs saisies, une liste de sélection, des cases à cocher ou des boutons radio. Si besoin, CCK peut valider les saisies sur la base d'un ensemble de valeurs autorisées.", + 'lid' => '143', + 'translation' => "Ces paramètres ne s'applique qu'au champ '%field' tel qu'il apparaît dans le type contenu '%type'.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '10', - 'translation' => 'Node Reference, qui crée des références personnalisées entre nœuds de Drupal. En ajoutant, par exemple, un champ nodereference et deux types de contenus différents, vous pouvez facilement créer des relations complexes de type parent/enfant entre données (par exemple plusieurs nœuds "employé" peuvent présenter un champ nodereference pointant vers un même nœud "employeur").', + 'lid' => '144', + 'translation' => "Texte d'aide", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '11', - 'translation' => "User reference, qui crée des références personnalisées vers les comptes des utilisateurs de votre site. En ajoutant un champ userreference, vous pouvez créer des relations complexes entre les utilisateurs de votre site et des publications. Ainsi, pour tracer l'implication d'un utilisateur dans une publication (au delà du champ Drupal standard Écrit par), vous pouvez ajouter à un type de contenu un champ userreference intitulé \"Édité par\" pour enregistrer un lien vers la page du compte utilisateur ayant édité la publication.", + 'lid' => '145', + 'translation' => "Instructions à présenter à l'utilisateur sous ce champ, dans le formulaire d'édition.
                          Balises HTML autorisées : @tags", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '12', - 'translation' => "Fieldgroup, qui crée des groupes de champs liés. Les groupe de champ peuvent être repliés, et vous pouvez choisir qu'ils soient dépliés ou repliés par défaut. L'ordre des groupes de champs, ainsi que l'ordre des champs au sein d'un groupe, est géré grâce à l'interface par glisser-déposer fournie par le module Content.", + 'lid' => '146', + 'translation' => 'Valeur par défaut', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '13', - 'translation' => "Pour plus d'informations, reportez-vous à l'entrée de manuel en ligne relative à CCK ou à la page du projet CCK.", + 'lid' => '147', + 'translation' => 'Code PHP', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '14', - 'translation' => 'Configurez ici la manière dont les champs et étiquettes de champs de ce type de contenu doivent être affichées, lorsque le contenu est vu en mode résumé ou en pleine page.', + 'lid' => '148', + 'translation' => "'@column' => valeur de @column", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '15', - 'translation' => "Configurez ici la façon dont les champs de ce type de contenu doivent être affichés lorsqu'il est rendu dans les contextes suivants.", + 'lid' => '149', + 'translation' => "return array(\n 0 => array(@columns),\n // Vous voudrez vous arrêter là dans la plupart des cas. Fournir plus de valeurs\n // si vous souhaitez que votre 'valeur par défaut' ait des valeurs multiples :\n 1 => array(@columns),\n 2 => ...\n);", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '16', - 'translation' => "Contrôlez ici l'ordre des champs dans le formulaire de saisie.", + 'lid' => '150', + 'translation' => 'Code', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '17', - 'translation' => 'Ce champ est obligatoire.', + 'lid' => '151', + 'translation' => "Usage avancé seulement : code PHP retournant une valeur par défaut. Ne doit pas contenir les délimiteurs <?php ?>. Si ce champ est rempli, la valeur retournée par ce code écrasera toute valeur spécifiée ci-dessus. Format attendu :
                          !sample
                          . Pour vous faire une idée du format attendu, vous pouvez utiliser l'onglet devel load fourni par le module devel sur une page de contenu de type %type.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '18', - 'translation' => '!title : !required', + 'lid' => '152', + 'translation' => '<aucun>', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '19', - 'translation' => 'Ordre', + 'lid' => '153', + 'translation' => "Vous n'êtes pas autorisé à saisir du code PHP.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '20', - 'translation' => 'Élément de flux RSS', + 'lid' => '154', + 'translation' => 'Ce code PHP a été inséré par un administrateur et supplantera toute valeur spécifiée ci-dessus.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '21', - 'translation' => 'Index de recherche', + 'lid' => '155', + 'translation' => 'Paramètres globaux', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '22', - 'translation' => 'Résultat de recherche', + 'lid' => '156', + 'translation' => "Ces paramètres s'appliquent au champ '%field' dans tous les types de contenu où il apparaît.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '23', - 'translation' => 'Langue', + 'lid' => '157', + 'translation' => 'Obligatoire', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '24', - 'translation' => 'Taxonomie', + 'lid' => '158', + 'translation' => "Le nombre maximum de valeurs qu'un utilisateur peut entrer pour ce champ.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '25', - 'translation' => 'Fichiers attachés', + 'lid' => '159', + 'translation' => "'Illimité' fournira un bouton 'Ajouter plus' pour que les utilisateurs puissent ajouter autant de valeurs qu'ils le souhaitent.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '26', - 'translation' => 'Mise à jour du type de champ %type avec le module %module.', + 'lid' => '160', + 'translation' => 'Attention ! Changer ce paramètre alors que des données ont déjà été créées peut conduire à perdre des données !', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '27', - 'translation' => 'Mise à jour du type de widget %widget avec le module %module.', + 'lid' => '161', + 'translation' => 'Nombre de valeurs', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '28', - 'translation' => "Utiliser du code PHP pour le paramétrage des champs (dangereux - à n'autoriser qu'avec précautions)", + 'lid' => '162', + 'translation' => 'Illimité', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '29', - 'translation' => 'Gérer les champs', + 'lid' => '163', + 'translation' => 'Enregistrer les paramètres du champ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '30', - 'translation' => 'Afficher les champs', + 'lid' => '164', + 'translation' => "Le code PHP pour la 'valeur par défaut' a retourné @value, qui n'est pas valide.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '31', - 'translation' => 'Général', + 'lid' => '165', + 'translation' => 'La valeur par défaut est invalide.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '32', - 'translation' => 'Avancé', + 'lid' => '166', + 'translation' => "Le champ '%label' a été ajouté.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '33', - 'translation' => 'Supprimer un champ', + 'lid' => '167', + 'translation' => "Champ '%label' enregistré.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '34', - 'translation' => 'Content', + 'lid' => '168', + 'translation' => 'Exécution', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '35', - 'translation' => 'Permet aux administrateurs de définir des nouveaux types de contenu.', + 'lid' => '169', + 'translation' => 'La mise à jour a échoué.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '36', - 'translation' => 'CCK', + 'lid' => '170', + 'translation' => 'La base de données a été modifiée et des données ont été déplacées ou supprimées.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '37', - 'translation' => 'Texte', + 'lid' => '171', + 'translation' => 'Une erreur est survenue et a interrompu la modification de la base de données.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '38', - 'translation' => "Les valeurs possibles pour ce champ. Saisissez une valeur par ligne, sous la forme clé|libellé. La clé est la valeur enregistrée dans la base de données, et elle doit correspondre au type de stockage du champ, %type. Le libellé est optionnel et, s'il n'est pas spécifié, la clé sera utilisée comme libellé.", + 'lid' => '172', + 'translation' => "'%title' en cours de traitement", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '39', - 'translation' => 'Zone de texte', + 'lid' => '173', + 'translation' => '%name doit être un entier.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '40', - 'translation' => 'Ôter', + 'lid' => '174', + 'translation' => '%name doit être un entier positif.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '41', - 'translation' => 'Basique', + 'lid' => '175', + 'translation' => '%name doit être un nombre.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '42', - 'translation' => 'Résumé', + 'lid' => '176', + 'translation' => '1 élément traité avec succès :', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '43', - 'translation' => 'Nœud complet', + 'lid' => '177', + 'translation' => '@count éléments traités avec succès :', 'language' => 'fr', - 'plid' => '0', - 'plural' => '0', + 'plid' => '176', + 'plural' => '1', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '44', - 'translation' => 'RSS', + 'lid' => '178', + 'translation' => "La table de champs a été renommée de '%old_name' à '%new_name' et les instances des champs ont été mises à jour.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '45', - 'translation' => 'Recherche', + 'lid' => '179', + 'translation' => "La table de champs '%name' a été supprimée.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '46', - 'translation' => 'Formulaire du module node.', + 'lid' => '180', + 'translation' => 'Ajouter un autre élément', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '47', - 'translation' => 'Formulaire du module locale.', + 'lid' => '181', + 'translation' => 'Contenu du champ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '48', - 'translation' => 'Paramètres du menu', + 'lid' => '182', + 'translation' => 'Un champ de contenu du node référencé.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '49', - 'translation' => 'Formulaire du module menu.', + 'lid' => '183', + 'translation' => 'Noeud', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '50', - 'translation' => 'Formulaire du module taxonomy.', + 'lid' => '184', + 'translation' => 'Contexte du noeud', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '51', - 'translation' => 'Livre', + 'lid' => '185', + 'translation' => 'Titre du bloc', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '52', - 'translation' => 'Formulaire du module livre (book).', + 'lid' => '186', + 'translation' => 'Caché', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '53', - 'translation' => 'Titre du sondage', + 'lid' => '187', + 'translation' => "Configurer la manière dont l'étiquette est affichée.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '54', - 'translation' => 'Titre du module sondage (Poll)', + 'lid' => '188', + 'translation' => 'Champ / Formateur', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '55', - 'translation' => 'Choix du sondage', + 'lid' => '189', + 'translation' => 'Sélectionner un champ et un formateur.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '56', - 'translation' => 'Choix du module sondage (poll).', + 'lid' => '190', + 'translation' => '"@s" champ @name', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '57', - 'translation' => 'Paramètrage du sondage', + 'lid' => '191', + 'translation' => 'Remplir un champ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '58', - 'translation' => 'Paramètres du module sondage (poll).', + 'lid' => '192', + 'translation' => 'Vous devez vous assurer que le champ existe pour le type de contenu donné.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '59', - 'translation' => 'Formulaire du module upload.', + 'lid' => '193', + 'translation' => 'Champ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '60', - 'translation' => 'contenu', + 'lid' => '194', + 'translation' => 'Sélectionnez le nom-machine du champ.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '61', - 'translation' => 'Champs', + 'lid' => '195', + 'translation' => 'Avancé : Préciser les valeurs des champs avec du code PHP', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '62', - 'translation' => "Les mises à jour des modules liés à CCK ne sont pas exécutées tant que les modules ne sont pas activés sur la page d'administration des modules. Lorsque vous les activerez, vous devrez retourner sur la page update.php et exécuter les mises à jour restantes.", + 'lid' => '196', + 'translation' => "Usage avancé seulement : code PHP retournant la valeur à définir. Ne doit pas contenir les délimiteurs <?php ?>. Si ce champ est rempli, la valeur retournée par ce code écrasera toute valeur spécifiée ci-dessus. Format attendu :
                          !sample
                          . Pour vous faire une idée du format attendu, vous pouvez utiliser l'onglet devel load fourni par le module devel sur une page de contenu de type %type.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '63', - 'translation' => "!module.module possède des mises à jour mais ne peut pas être mis à jour car content.module n'est pas activé.
                          Le cas échéant, lors de l'activation de content.module, vous devrez exécuter à nouveau le script de mise à jour. Vous continuerez à voir ce message jusqu'à ce que le module soit activé et les mises à jour exécutées.", + 'lid' => '197', + 'translation' => 'Vous devez retourner la valeur par défaut dans le format attendu.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '64', - 'translation' => "!module.module possède des mises à jour et est disponible dans le répertoire des modules, mais n'est pas activé.
                          Le cas échéant, lorsque vous l'aurez activé, vous devrez ré-exécuter le script de mise à jour. Vous continuerez à voir ce message jusqu'à l'activation du module et l'exécution des mises à jour. ", + 'lid' => '198', + 'translation' => "Remplir le champ '@field' de @node", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '65', - 'translation' => 'Des mises à jour sont toujours en attente. Veuillez retourner sur update.php et exécuter les mises à jour restarntes.', + 'lid' => '199', + 'translation' => 'Le champ possède une valeur', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '66', - 'translation' => 'CCK - Aucune Intégration aux Vues', + 'lid' => '200', + 'translation' => 'Vous devez vous assurer que le champ utilisé existe dans le type de contenu donné. La condition retourne TRUE, si le champ sélectionné possède la valeur donnée.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '67', - 'translation' => 'L"intégration de CCK avec le module Views requiert Views 6.x-2.0-rc2 ou une version supérieure.', + 'lid' => '201', + 'translation' => 'Le champ a été modifié', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '68', - 'translation' => 'Nom', + 'lid' => '202', + 'translation' => 'Contenu contenant des modifications', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '69', - 'translation' => 'Type', + 'lid' => '203', + 'translation' => 'Contenu ne contenant pas de modification', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '70', - 'translation' => 'Description', + 'lid' => '204', + 'translation' => "Le champ '@field' de @node possède une valeur", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '71', - 'translation' => 'Opérations', + 'lid' => '205', + 'translation' => 'Sélectionnez le nom-machine du champ à voir.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '72', - 'translation' => 'éditer', + 'lid' => '206', + 'translation' => "Le champ '@field' de @node a été modifié", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '73', - 'translation' => 'gérer les champs', + 'lid' => '207', + 'translation' => 'Jeton (Token)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '74', - 'translation' => 'supprimer', + 'lid' => '208', + 'translation' => 'Identifiant du nœud référencé', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '75', - 'translation' => 'Aucun type de contenu disponible.', + 'lid' => '209', + 'translation' => 'Titre du nœud référencé', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '76', - 'translation' => '» Ajouter un nouveau type de contenu', + 'lid' => '210', + 'translation' => 'Titre non filtré du noeud référencé. ATTENTION - saisie brute utilisateur.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '77', - 'translation' => 'Nom du champ', + 'lid' => '211', + 'translation' => 'Lien html formaté vers le noeud référencé.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '78', - 'translation' => 'Type de champ', + 'lid' => '212', + 'translation' => 'Alias de chemin relatif vers le noeud référencé.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '79', - 'translation' => 'Utilisé dans', + 'lid' => '213', + 'translation' => 'Alias de chemin absolu vers le noeud référencé.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '80', - 'translation' => '@field_name (Verrouillé)', + 'lid' => '214', + 'translation' => 'Valeur numérique brute', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '81', - 'translation' => "Aucun champ n'est pour l'instant défini sur l'ensemble des types de contenu.", + 'lid' => '215', + 'translation' => 'Valeur numérique mise en forme', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '82', - 'translation' => "Ce type de contenu possède des champs inactifs. Les champs inactifs ne sont pas inclus dans la liste de champs disponibles, jusqu'à l'activation des modules correspondants.", + 'lid' => '216', + 'translation' => 'Texte brut, non filtré', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '83', - 'translation' => '!field (!field_name) est un champ inactif de type !field_type, qui utilise un widget de type !widget_type.', + 'lid' => '217', + 'translation' => 'Texte filtré et mis en forme', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '84', - 'translation' => 'Configurer', + 'lid' => '218', + 'translation' => "Identifiant de l'utilisateur référencé", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '85', - 'translation' => 'Verrouillé', + 'lid' => '219', + 'translation' => "Nom de l'utilisateur référencé", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '86', - 'translation' => '- Sélectionnez un type de champ -', + 'lid' => '220', + 'translation' => "Lien HTML mis en forme vers l'utilisateur référencé", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '87', - 'translation' => '- Sélectionnez un widget -', + 'lid' => '221', + 'translation' => "Alias de chemin relatif vers l'utilisateur référencé.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '88', - 'translation' => 'Étiquette', + 'lid' => '222', + 'translation' => "Alias de chemin absolu vers l'utilisateur référencé.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '89', - 'translation' => 'Nom du champ (a-z, 0-9, _)', + 'lid' => '223', + 'translation' => '@label (!name)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '90', - 'translation' => 'Type de données à stocker.', + 'lid' => '224', + 'translation' => '@label (!name) - !column', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '91', - 'translation' => "Elément du formulaire pour l'édition des données.", + 'lid' => '225', + 'translation' => '@label-truncated - !column', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '92', - 'translation' => '- Sélectionnez un champ existant -', + 'lid' => '226', + 'translation' => 'Apparaît dans : @types', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '93', - 'translation' => 'Champ à partager', + 'lid' => '227', + 'translation' => 'Aucun', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '94', - 'translation' => 'Nom du groupe (a-z, 0-9, _)', + 'lid' => '228', + 'translation' => 'Étiquette du widget (@label)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '95', - 'translation' => 'Enregistrer', + 'lid' => '229', + 'translation' => 'Personnalisé', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '96', - 'translation' => 'Ajouter un nouveau champ : vous devez fournir une étiquette.', + 'lid' => '230', + 'translation' => 'Étiquette personnalisée', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '97', - 'translation' => 'Ajouter un nouveau champ : vous devez fournir un nom de champ.', + 'lid' => '231', + 'translation' => 'Format', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '98', - 'translation' => "Ajouter un nouveau champ : le nom de champ %field_name n'est pas valide. Le nom doit seulement contenir des lettre minuscules non accentuées, des nombres, et des underscores. ", + 'lid' => '232', + 'translation' => 'Grouper plusieurs valeurs', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '99', - 'translation' => "Ajouter un nouveau champ : le nom de champ %field_name est trop long. Le nom est limité à 32 caractères, en comptant le préfixe 'field_'.", + 'lid' => '233', + 'translation' => "Si non coché, chaque élément du champ créera une nouvelle ligne, ce qui pourrait apparemment entraîner des doublons. Ce paramètre n'est pas compatible avec le tri par clic dans l'affichage du tableau. ", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '100', - 'translation' => "Ajouter un nouveau champ : le nom 'field_instance' est un nom réservé.", + 'lid' => '234', + 'translation' => 'Afficher @count valeur(s)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '101', - 'translation' => 'Ajouter un nouveau champ : le nom du champ %field_name existe déjà.', + 'lid' => '235', + 'translation' => 'en commençant à @count', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '102', - 'translation' => 'Ajouter un nouveau champ : vous devez sélectionner un type de champ.', + 'lid' => '236', + 'translation' => 'Inversé (commencer à partir des dernières valeurs)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '103', - 'translation' => 'Ajouter un nouveau champ : vous devez sélectionner un widget.', + 'lid' => '237', + 'translation' => 'Tous / Toutes', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '104', - 'translation' => 'Ajouter un nouveau champ : widget non valide.', + 'lid' => '238', + 'translation' => 'Delta', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '105', - 'translation' => 'Ajouter un champ existant : vous devez fournir une étiquette.', + 'lid' => '239', + 'translation' => "Le delta vous permet de sélectionner quel élément d'un champ à valeur multiple sera la clé de la relation. Sélectionnez \"1\" pour utiliser le premier élément, \"2\" pour le second et ainsi de suite. Si vous sélectionnez \"Tous\", chaque élément du champ créera une nouvelle ligne, ce qui pourrait causer des doublons.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '106', - 'translation' => 'Ajouter un champ existant : vous devez sélectionner un champ.', + 'lid' => '240', + 'translation' => "Le delta vous permet de sélectionner quel élément d'un champ à valeur multiple sera utilisé pour les tris. Sélectionnez \"1\" pour utiliser le premier élément, \"2\" pour le second et ainsi de suite. Si vous sélectionnez \"Tous\", chaque élément du champ créera une nouvelle ligne, ce qui pourrait causer des doublons.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '107', - 'translation' => 'Ajouter un champ existant: vous devez sélectionner un widget.', + 'lid' => '241', + 'translation' => 'Exporter', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '108', - 'translation' => 'Ajouter un champ existant : widget non valide.', + 'lid' => '242', + 'translation' => "Ce formulaire traitera un type de contenu et un ou plusieurs champs de ce type, pour en exporter les paramètres. Le code d'export ainsi généré peut être copié et collé dans la page d'import, vers la base de données courante ou vers une autre base de données. L'opération d'import ajoutera les champs à un type de contenu existant ou créera un nouveau type de contenu intégrant les champs sélectionnés.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '109', - 'translation' => "Un problème est survenu à la création du champ '%label'.", + 'lid' => '243', + 'translation' => 'Types', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '110', - 'translation' => "Le champ %label n'a pas pu être ajouté au type de contenu car il est verrouillé.", + 'lid' => '244', + 'translation' => 'Sélectionner le type de contenu à exporter.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '111', - 'translation' => "Un problème est survenu lors de l'ajout du champ '%label'.", + 'lid' => '245', + 'translation' => 'Données exportée', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '112', - 'translation' => "Il n'y a aucun champ configuré pour ce type de contenu. Vous pouvez ajouter de nouveaux champs sur la page Gérer les champs.", + 'lid' => '246', + 'translation' => "Copiez le texte exporté et collez-le dans le type de contenu de votre choix, à l'aide de la fonction d'import.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '113', - 'translation' => 'Au dessus', + 'lid' => '247', + 'translation' => 'Types de contenu', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '114', - 'translation' => 'Sur la même ligne', + 'lid' => '248', + 'translation' => 'Type de contenu', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '115', - 'translation' => 'Inclure', + 'lid' => '249', + 'translation' => 'Séléctionnez le type de contenu dans lequel importer ces champs.
                          Sélectionnez <Créer> pour créer un nouveau type de contenu qui contiendra ces champs.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '116', - 'translation' => 'Exclure', + 'lid' => '250', + 'translation' => 'Données à importer', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '117', - 'translation' => 'aucune mise en forme', + 'lid' => '251', + 'translation' => 'Collez dans ce champ le texte créé par un export de contenu.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '118', - 'translation' => 'simple', + 'lid' => '252', + 'translation' => 'Importer', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '119', - 'translation' => 'groupe de champs', + 'lid' => '253', + 'translation' => "Un fichier a été préchargé pour l'import.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '120', - 'translation' => 'groupe de champs - repliable', + 'lid' => '254', + 'translation' => "Les données d'import ne sont valides.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '121', - 'translation' => 'groupe de champs - replié', + 'lid' => '255', + 'translation' => "Les modules suivants doivent être activés pour que l'import fonctionne : '%modules'.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '122', - 'translation' => 'Vos paramètres ont été enregistrés.', + 'lid' => '256', + 'translation' => "Le type de contenu '%type' existe déjà dans cette base de données.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '123', - 'translation' => '@type : @field (@label)', + 'lid' => '257', + 'translation' => "Abandon. L'import n'a pas été réalisé.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '124', - 'translation' => 'Éditer les informations de base', + 'lid' => '258', + 'translation' => "Une erreur s'est produite lors de l'ajout du nouveau type de contenu %type.
                          Veuillez vérifier les erreurs affichées pour plus de détails.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '125', - 'translation' => 'Le nom lisible par une machine du champ. Ce nom ne peut être changé.', + 'lid' => '259', + 'translation' => "Le champ importé '%field_label' (%field_name) n'a pas été ajouté à '%type' car ce champ existe déjà.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '126', - 'translation' => "Nom lisible par une personne, destiné à servir d'étiquette pour ce champ au sein du type de contenu '%type'.", + 'lid' => '260', + 'translation' => "Le champ importé '%field_label' (%field_name) a été ajouté au type de contenu '%type'.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '127', - 'translation' => 'Type de données que vous souhaitez enregistrer, par le biais de ce champ, dans la base de données. Cette option ne peut être modifiée.', + 'lid' => '261', + 'translation' => 'content_copy', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '128', - 'translation' => 'Type de widget', + 'lid' => '262', + 'translation' => 'Content Copy', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '129', - 'translation' => "Type d'élément de formulaire que vous souhaitez présenter à l'utilisateur lorsqu'il renseigne ce champ dans le type de contenu '%type'.", + 'lid' => '263', + 'translation' => "Permet d'importer et d'exporter des définitions de champs.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '130', - 'translation' => 'Continuer', + 'lid' => '264', + 'translation' => "Les champs d'un groupe Standard sont indépendants les uns des autres, et chacun peut soit avoir une valeur unique, soit des valeurs multiples. Les champs d'un Multigroupe sont traités comme une ensemble répétable de champs à valeurs uniques.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '131', - 'translation' => 'Les paramètres basiques du champ %label ont été mis à jour.', + 'lid' => '265', + 'translation' => 'Multigroupe', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '132', - 'translation' => 'Un problème a été rencontré lors de la mise à jour des paramètres basiques du champ %label.', + 'lid' => '266', + 'translation' => 'Standard', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '133', - 'translation' => "Êtes-vous certain de vouloir enlever le champ '%field' ?", + 'lid' => '267', + 'translation' => 'Type de groupe.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '134', - 'translation' => 'Si vous avez encore du contenu dans ce champ, il sera perdu. Cette action est irréversible.', + 'lid' => '268', + 'translation' => "Le champ %field a été mis à jour pour l'utilisation de valeurs %multiple, afin de correspondre au paramètre de valeur multipe du Multigroup %group.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '135', - 'translation' => 'Annuler', + 'lid' => '269', + 'translation' => "Cette modification n'est pas autorisée. Le champ %field possède déjà des valeurs multiples dans la base de données, mais le groupe %group en autorise seulement %group_max. Effectuer cette modification pourrait entraîner la perte de données. ", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '136', - 'translation' => 'Ce champ est verrouillé et ne peut être supprimé.', + 'lid' => '270', + 'translation' => "Cette modification n'est pas autorisée. Le champ %field manipule les valeurs multiples différemment du module Content. Effectuer cette modification pourrait entraîner la perte de données. ", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '137', - 'translation' => "Le champ '%field' de '%type' a été enlevé.", + 'lid' => '271', + 'translation' => "Vous êtes en train d'inclure le champ %field dans un Multigroupe", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '138', - 'translation' => "Un problème est survenu à la suppression du champ '%field' du type '%type'.", + 'lid' => '272', + 'translation' => "Cette modification n'est pas autorisée. Le champ %field possède déjà des données créées, et utilise un widget qui stocke les données différemment dans un groupe Standard que dans un Multigroupe. Effectuer ce changement pourrait entraîner la perte de données.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '139', - 'translation' => 'Le champ %field est verouillé et ne peut être édité.', + 'lid' => '273', + 'translation' => "Vous êtes en train de retirer le champ %field d'un Multigroupe", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '140', - 'translation' => "Informations de base pour '%type'", + 'lid' => '274', + 'translation' => 'Simple', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '141', - 'translation' => 'Modifier les informations de base', + 'lid' => '275', + 'translation' => 'Groupe de champs', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '142', - 'translation' => "Paramètres de '%type'", + 'lid' => '276', + 'translation' => 'Ligne horizontale', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '143', - 'translation' => "Ces paramètres ne s'applique qu'au champ '%field' tel qu'il apparaît dans le type contenu '%type'.", + 'lid' => '277', + 'translation' => 'Tableau - Colonne unique', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '144', - 'translation' => "Texte d'aide", + 'lid' => '278', + 'translation' => 'Tableau - Colonnes multiples', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '145', - 'translation' => "Instructions à présenter à l'utilisateur sous ce champ, dans le formulaire d'édition.
                          Balises HTML autorisées : @tags", + 'lid' => '279', + 'translation' => '[Format du sous-groupe]', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '146', - 'translation' => 'Valeur par défaut', + 'lid' => '280', + 'translation' => 'Paramètres multigroupe', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '147', - 'translation' => 'Code PHP', + 'lid' => '281', + 'translation' => 'Colonnes multiples', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '148', - 'translation' => "'@column' => valeur de @column", + 'lid' => '282', + 'translation' => "Activez cette option pour rendre chaque champs dans une colonne disctincte sur le formulaire d'édition de noeud.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '149', - 'translation' => "return array(\n 0 => array(@columns),\n // Vous voudrez vous arrêter là dans la plupart des cas. Fournir plus de valeurs\n // si vous souhaitez que votre 'valeur par défaut' ait des valeurs multiples :\n 1 => array(@columns),\n 2 => ...\n);", + 'lid' => '283', + 'translation' => "Activez cette option pour rendre obligatoire un minimum d'une collection de champs dans ce Multigroupe.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '150', - 'translation' => 'Code', + 'lid' => '284', + 'translation' => "Nombre de fois où répéter l'ensemble Multigroupe de champs.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '151', - 'translation' => "Usage avancé seulement : code PHP retournant une valeur par défaut. Ne doit pas contenir les délimiteurs <?php ?>. Si ce champ est rempli, la valeur retournée par ce code écrasera toute valeur spécifiée ci-dessus. Format attendu :
                          !sample
                          . Pour vous faire une idée du format attendu, vous pouvez utiliser l'onglet devel load fourni par le module devel sur une page de contenu de type %type.", + 'lid' => '285', + 'translation' => "'Illimité' fournira un bouton 'Ajouter plus' pour que les utilisateurs puissent ajouter des éléments autant de fois qu'ils le souhaitent.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '152', - 'translation' => '<aucun>', + 'lid' => '286', + 'translation' => 'Tous les champs de ce groupe seront automatiquement paramétrés pour autoriser ce nombre de valeurs.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '153', - 'translation' => "Vous n'êtes pas autorisé à saisir du code PHP.", + 'lid' => '287', + 'translation' => 'Nombre de répétitions', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '154', - 'translation' => 'Ce code PHP a été inséré par un administrateur et supplantera toute valeur spécifiée ci-dessus.', + 'lid' => '288', + 'translation' => 'Etiquettes', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '155', - 'translation' => 'Paramètres globaux', + 'lid' => '289', + 'translation' => "Etiquettes pour chaque sous-groupe de champs. Les étiquettes peuvent être cachées ou affichées dans des contextes divers en utilisant l'écran 'Afficher les champs'.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '156', - 'translation' => "Ces paramètres s'appliquent au champ '%field' dans tous les types de contenu où il apparaît.", + 'lid' => '290', + 'translation' => 'Etiquette du sous-groupe %number', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '157', - 'translation' => 'Obligatoire', + 'lid' => '291', + 'translation' => 'Le champ %field dans ce groupe possède déjà %multiple valeurs dans la base de données. Pour éviter la perte de données, vous ne pouvez définir le nombre de valeurs du Multigroupe à une valeur inférieure.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '158', - 'translation' => "Le nombre maximum de valeurs qu'un utilisateur peut entrer pour ce champ.", + 'lid' => '292', + 'translation' => 'Le champ !name est obligatoire dans le groupe @group.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '159', - 'translation' => "'Illimité' fournira un bouton 'Ajouter plus' pour que les utilisateurs puissent ajouter autant de valeurs qu'ils le souhaitent.", + 'lid' => '293', + 'translation' => 'Le groupe @name requiert au minimum un groupe de champs.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '160', - 'translation' => 'Attention ! Changer ce paramètre alors que des données ont déjà été créées peut conduire à perdre des données !', + 'lid' => '294', + 'translation' => 'Ajouter plus de valeurs', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '161', - 'translation' => 'Nombre de valeurs', + 'lid' => '295', + 'translation' => 'content_multigroup', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '162', - 'translation' => 'Illimité', + 'lid' => '296', + 'translation' => 'Contenu Multigroupe', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '163', - 'translation' => 'Enregistrer les paramètres du champ', + 'lid' => '297', + 'translation' => "Combinez de multiples champs CCK au sein de collections de champs qui fonctionnent à l'unisson.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '164', - 'translation' => "Le code PHP pour la 'valeur par défaut' a retourné @value, qui n'est pas valide.", + 'lid' => '298', + 'translation' => 'éditer', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '165', - 'translation' => 'La valeur par défaut est invalide.', + 'lid' => '299', + 'translation' => 'field_name', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '166', - 'translation' => "Le champ '%label' a été ajouté.", + 'lid' => '300', + 'translation' => 'voir', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '167', - 'translation' => "Champ '%label' enregistré.", + 'lid' => '301', + 'translation' => 'content_permissions', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '168', - 'translation' => 'Exécution', + 'lid' => '302', + 'translation' => 'Veuillez configurer vos permissions sur les champs immédiatement. Tous les champs sont inaccessibles par défaut.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '169', - 'translation' => 'La mise à jour a échoué.', + 'lid' => '303', + 'translation' => 'Permissions sur les Contenus', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '170', - 'translation' => 'La base de données a été modifiée et des données ont été déplacées ou supprimées.', + 'lid' => '304', + 'translation' => 'Définit un niveau de permission par champ pour les champs CCK.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '171', - 'translation' => 'Une erreur est survenue et a interrompu la modification de la base de données.', + 'lid' => '305', + 'translation' => 'Contenu du groupe de champ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '172', - 'translation' => "'%title' en cours de traitement", + 'lid' => '306', + 'translation' => "Tous les champs d'un groupe de champs sur le node référencé.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '173', - 'translation' => '%name doit être un entier.', + 'lid' => '307', + 'translation' => '@group_label (@group_type_name)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '174', - 'translation' => '%name doit être un entier positif.', + 'lid' => '308', + 'translation' => 'Fieldgroup', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '175', - 'translation' => '%name doit être un nombre.', + 'lid' => '309', + 'translation' => "Texte à afficher si un groupe n'a pas de données. Notez que le titre ne s'affichera pas sauf s'il est surclassé.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '176', - 'translation' => '1 élément traité avec succès :', + 'lid' => '310', + 'translation' => '"@s" groupe de champs @name', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '177', - 'translation' => '@count éléments traités avec succès :', + 'lid' => '311', + 'translation' => 'Paramètres du formulaire', 'language' => 'fr', - 'plid' => '176', - 'plural' => '1', + 'plid' => '0', + 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '178', - 'translation' => "La table de champs a été renommée de '%old_name' à '%new_name' et les instances des champs ont été mises à jour.", + 'lid' => '312', + 'translation' => "Ces paramètres s'appliquent au groupe dans le formulaire d'édition de nœud.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '179', - 'translation' => "La table de champs '%name' a été supprimée.", + 'lid' => '313', + 'translation' => 'Style', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '180', - 'translation' => 'Ajouter un autre élément', + 'lid' => '314', + 'translation' => 'toujours déplié', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '181', - 'translation' => 'Contenu du champ', + 'lid' => '315', + 'translation' => 'repliable', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '182', - 'translation' => 'Un champ de contenu du node référencé.', + 'lid' => '316', + 'translation' => 'replié', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '183', - 'translation' => 'Noeud', + 'lid' => '317', + 'translation' => "Instructions à présenter à l'utilisateur dans le formulaire d'édition.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '184', - 'translation' => 'Contexte du noeud', + 'lid' => '318', + 'translation' => "Paramètres d'affichage", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '185', - 'translation' => 'Titre du bloc', + 'lid' => '319', + 'translation' => "Ces paramètres s'appliquent au groupe à l'affichage du nœud.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '186', - 'translation' => 'Caché', + 'lid' => '320', + 'translation' => 'Description du groupe.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '187', - 'translation' => "Configurer la manière dont l'étiquette est affichée.", + 'lid' => '321', + 'translation' => "Êtes-vous sûr(e) de vouloir supprimer le groupe '%label' ?", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '188', - 'translation' => 'Champ / Formateur', + 'lid' => '322', + 'translation' => 'Cette action est irréversible.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '189', - 'translation' => 'Sélectionner un champ et un formateur.', + 'lid' => '323', + 'translation' => "Le groupe '%group_name' a été supprimé.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '190', - 'translation' => '"@s" champ @name', + 'lid' => '324', + 'translation' => 'aucun', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '191', - 'translation' => 'Remplir un champ', + 'lid' => '325', + 'translation' => 'Vous devez fournir une étiquette.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '192', - 'translation' => 'Vous devez vous assurer que le champ existe pour le type de contenu donné.', + 'lid' => '326', + 'translation' => 'Vous devez fournir un nom de groupe', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '193', - 'translation' => 'Champ', + 'lid' => '327', + 'translation' => "Le nom de groupe %group_name n'est pas valide. Le nom ne doit contenir que des lettres sans accents, des nombres, et des underscores.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '194', - 'translation' => 'Sélectionnez le nom-machine du champ.', + 'lid' => '328', + 'translation' => "Le nom de groupe %group_name est trop long. Le nom est limité à 32 caractères, le préfixe 'group_' compris.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '195', - 'translation' => 'Avancé : Préciser les valeurs des champs avec du code PHP', + 'lid' => '329', + 'translation' => 'Le nom de groupe %group_name existe déjà.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '196', - 'translation' => "Usage avancé seulement : code PHP retournant la valeur à définir. Ne doit pas contenir les délimiteurs <?php ?>. Si ce champ est rempli, la valeur retournée par ce code écrasera toute valeur spécifiée ci-dessus. Format attendu :
                          !sample
                          . Pour vous faire une idée du format attendu, vous pouvez utiliser l'onglet devel load fourni par le module devel sur une page de contenu de type %type.", + 'lid' => '330', + 'translation' => 'Ajouter un nouveau groupe :', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '197', - 'translation' => 'Vous devez retourner la valeur par défaut dans le format attendu.', + 'lid' => '331', + 'translation' => 'Ajouter un nouveau groupe : vous devez fournir une étiquette.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '198', - 'translation' => "Remplir le champ '@field' de @node", + 'lid' => '332', + 'translation' => 'Ajouter un nouveau groupe : vous devez fournir un nom de groupe.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '199', - 'translation' => 'Le champ possède une valeur', + 'lid' => '333', + 'translation' => 'Groupe standard', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '200', - 'translation' => 'Vous devez vous assurer que le champ utilisé existe dans le type de contenu donné. La condition retourne TRUE, si le champ sélectionné possède la valeur donnée.', + 'lid' => '334', + 'translation' => 'Éditer le groupe', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '201', - 'translation' => 'Le champ a été modifié', + 'lid' => '335', + 'translation' => 'fieldgroup', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '202', - 'translation' => 'Contenu contenant des modifications', + 'lid' => '336', + 'translation' => "Créée des groupes d'affichage pour les champs CCK.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '203', - 'translation' => 'Contenu ne contenant pas de modification', + 'lid' => '337', + 'translation' => 'Charge un noeud référencé', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '204', - 'translation' => "Le champ '@field' de @node possède une valeur", + 'lid' => '338', + 'translation' => 'Contenu contenant le champ node reference', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '205', - 'translation' => 'Sélectionnez le nom-machine du champ à voir.', + 'lid' => '339', + 'translation' => 'Contenu référencé', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '206', - 'translation' => "Le champ '@field' de @node a été modifié", + 'lid' => '340', + 'translation' => 'Notez que si le champs possède des valeurs multiples, seul le premier contenu sera chargé.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '207', - 'translation' => 'Jeton (Token)', + 'lid' => '341', + 'translation' => "Il n'y a aucun champ nodereference défini.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '208', - 'translation' => 'Identifiant du nœud référencé', + 'lid' => '342', + 'translation' => 'Node référence', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '209', - 'translation' => 'Titre du nœud référencé', + 'lid' => '343', + 'translation' => "Stocker l'ID du noeud lié en tant que valeur entière.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '210', - 'translation' => 'Titre non filtré du noeud référencé. ATTENTION - saisie brute utilisateur.', + 'lid' => '344', + 'translation' => 'Types de contenu pouvant être référencés', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '211', - 'translation' => 'Lien html formaté vers le noeud référencé.', + 'lid' => '345', + 'translation' => 'Vues par défaut', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '212', - 'translation' => 'Alias de chemin relatif vers le noeud référencé.', + 'lid' => '346', + 'translation' => 'Vues éxistantes', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '213', - 'translation' => 'Alias de chemin absolu vers le noeud référencé.', + 'lid' => '347', + 'translation' => 'Avancé - Nœuds pouvant être référencés (Vue)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '214', - 'translation' => 'Valeur numérique brute', + 'lid' => '348', + 'translation' => 'Vue utilisée pour sélectionner les noeuds', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '215', - 'translation' => 'Valeur numérique mise en forme', + 'lid' => '349', + 'translation' => '

                          Choisissez la vue du "module Views" qui sélectionne les noeuds pouvant être référencés.
                          Note :

                          ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '216', - 'translation' => 'Texte brut, non filtré', + 'lid' => '350', + 'translation' => "
                          • Seules les vues qui possèdent des champs fonctionneront à cet effet.
                          • Ceci annulera les paramètres \"Types de contenu\" ci-dessus. Utilisez la section \"filtres\" de la vue à la place.
                          • Utilisez la section \"champs\" de la vue pour afficher des informations supplémentaires à propos des noeuds candidats sur le formulaire de création/édition.
                          • Utilisez la section \"critère de tri\" de la vue pour déterminer l'ordre dans lequel les noeuds candidats seront affichés.
                          ", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '217', - 'translation' => 'Texte filtré et mis en forme', + 'lid' => '351', + 'translation' => 'Arguments de la vue', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '218', - 'translation' => "Identifiant de l'utilisateur référencé", + 'lid' => '352', + 'translation' => "Fournit une liste d'arguments, séparés par des virgules, à transmettre à la vue.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '219', - 'translation' => "Nom de l'utilisateur référencé", + 'lid' => '353', + 'translation' => "

                          La liste des noeuds pouvant être référencés peut s'appuyer sur une vue du \"module Views\" mais aucune vue appropriée n'a été trouvée.
                          Note :

                          ", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '220', - 'translation' => "Lien HTML mis en forme vers l'utilisateur référencé", + 'lid' => '354', + 'translation' => '%name : saisie non valide.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '221', - 'translation' => "Alias de chemin relatif vers l'utilisateur référencé.", + 'lid' => '355', + 'translation' => '%name : ce contenu ne peut être référencé.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '222', - 'translation' => "Alias de chemin absolu vers l'utilisateur référencé.", + 'lid' => '356', + 'translation' => 'Titre (avec lien)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '223', - 'translation' => '@label (!name)', + 'lid' => '357', + 'translation' => 'Titre (sans lien)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '224', - 'translation' => '@label (!name) - !column', + 'lid' => '358', + 'translation' => 'Liste de sélection', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '225', - 'translation' => '@label-truncated - !column', + 'lid' => '359', + 'translation' => 'Cases à cocher/boutons radio', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '226', - 'translation' => 'Apparaît dans : @types', + 'lid' => '360', + 'translation' => 'Champ texte à auto-complètement', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '227', - 'translation' => 'Aucun', + 'lid' => '361', + 'translation' => "Correspondance de l'autocomplétion", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '228', - 'translation' => 'Étiquette du widget (@label)', + 'lid' => '362', + 'translation' => 'Commence par', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '229', - 'translation' => 'Personnalisé', + 'lid' => '363', + 'translation' => 'Contient', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '230', - 'translation' => 'Étiquette personnalisée', + 'lid' => '364', + 'translation' => "Séléctionnez la méthode utilisée pour collecter les suggestions de l'autocomplétion. Notez que Contient peut engendrer des problèmes de performances sur des sites avec des milliers de noeuds", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '231', - 'translation' => 'Format', + 'lid' => '365', + 'translation' => '%name : différence de titre. Veuillez vérifier votre sélection.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '232', - 'translation' => 'Grouper plusieurs valeurs', + 'lid' => '366', + 'translation' => "%name : aucun contenu valide n'a été trouvé pour ce titre.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '233', - 'translation' => "Si non coché, chaque élément du champ créera une nouvelle ligne, ce qui pourrait apparemment entraîner des doublons. Ce paramètre n'est pas compatible avec le tri par clic dans l'affichage du tableau. ", + 'lid' => '367', + 'translation' => 'Autocomplétion de nodereference', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '234', - 'translation' => 'Afficher @count valeur(s)', + 'lid' => '368', + 'translation' => 'nodereference', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '235', - 'translation' => 'en commençant à @count', + 'lid' => '369', + 'translation' => 'Node Reference', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '236', - 'translation' => 'Inversé (commencer à partir des dernières valeurs)', + 'lid' => '370', + 'translation' => 'Définit un type de champ pour référencer un noeud depuis un autre noeud.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '237', - 'translation' => 'Tous / Toutes', + 'lid' => '371', + 'translation' => 'Entier', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '238', - 'translation' => 'Delta', + 'lid' => '372', + 'translation' => 'Stocke un nombre dans la base de données en format entier.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '239', - 'translation' => "Le delta vous permet de sélectionner quel élément d'un champ à valeur multiple sera la clé de la relation. Sélectionnez \"1\" pour utiliser le premier élément, \"2\" pour le second et ainsi de suite. Si vous sélectionnez \"Tous\", chaque élément du champ créera une nouvelle ligne, ce qui pourrait causer des doublons.", + 'lid' => '373', + 'translation' => 'Décimal', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '240', - 'translation' => "Le delta vous permet de sélectionner quel élément d'un champ à valeur multiple sera utilisé pour les tris. Sélectionnez \"1\" pour utiliser le premier élément, \"2\" pour le second et ainsi de suite. Si vous sélectionnez \"Tous\", chaque élément du champ créera une nouvelle ligne, ce qui pourrait causer des doublons.", + 'lid' => '374', + 'translation' => 'Stocke un nombre dans la base de données en format décimal fixe.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '241', - 'translation' => 'Exporter', + 'lid' => '375', + 'translation' => 'Réel (Float)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '242', - 'translation' => "Ce formulaire traitera un type de contenu et un ou plusieurs champs de ce type, pour en exporter les paramètres. Le code d'export ainsi généré peut être copié et collé dans la page d'import, vers la base de données courante ou vers une autre base de données. L'opération d'import ajoutera les champs à un type de contenu existant ou créera un nouveau type de contenu intégrant les champs sélectionnés.", + 'lid' => '376', + 'translation' => 'Stocke un nombre dans la base de données en format réel.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '243', - 'translation' => 'Types', + 'lid' => '377', + 'translation' => 'Minimum', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '244', - 'translation' => 'Sélectionner le type de contenu à exporter.', + 'lid' => '378', + 'translation' => 'Maximum', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '245', - 'translation' => 'Données exportée', + 'lid' => '379', + 'translation' => 'Précision', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '246', - 'translation' => "Copiez le texte exporté et collez-le dans le type de contenu de votre choix, à l'aide de la fonction d'import.", + 'lid' => '380', + 'translation' => 'Le nombre total de chiffres à stocker dans la base de données, en incluant ceux à droite de la virgule.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '247', - 'translation' => 'Types de contenu', + 'lid' => '381', + 'translation' => 'Echelle', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '248', - 'translation' => 'Type de contenu', + 'lid' => '382', + 'translation' => 'Le nombre de chiffres à droite de la virgule', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '249', - 'translation' => 'Séléctionnez le type de contenu dans lequel importer ces champs.
                          Sélectionnez <Créer> pour créer un nouveau type de contenu qui contiendra ces champs.', + 'lid' => '383', + 'translation' => 'Séparateur de décimales', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '250', - 'translation' => 'Données à importer', + 'lid' => '384', + 'translation' => 'Le caractère que les utilisateurs saisiront pour séparer les décimales dans les formulaires.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '251', - 'translation' => 'Collez dans ce champ le texte créé par un export de contenu.', + 'lid' => '385', + 'translation' => 'Préfixe', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '252', - 'translation' => 'Importer', + 'lid' => '386', + 'translation' => 'Définissez une chaîne de caractères à utiliser pour préfixer la valeur, par exemple $ ou €. Laissez vide pour ne rien afficher de plus. Séparez les valeurs singulier et pluriel par une barre verticale (euro|euros).', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '253', - 'translation' => "Un fichier a été préchargé pour l'import.", + 'lid' => '387', + 'translation' => 'Suffixe', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '254', - 'translation' => "Les données d'import ne sont valides.", + 'lid' => '388', + 'translation' => 'Définissez une chaîne qui sera ajoutée en suffixe à la valeur, comme m², m/s², kb/s. Laisser vide pour aucun suffixe. Séparez les singulier et pluriel avec un pipe (mètre|mètres).', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '255', - 'translation' => "Les modules suivants doivent être activés pour que l'import fonctionne : '%modules'.", + 'lid' => '389', + 'translation' => 'Valeurs autorisées', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '256', - 'translation' => "Le type de contenu '%type' existe déjà dans cette base de données.", + 'lid' => '390', + 'translation' => 'Liste des valeurs autorisées', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '257', - 'translation' => "Abandon. L'import n'a pas été réalisé.", + 'lid' => '391', + 'translation' => "Les valeurs possibles que ce champ peut contenir. Entrez une valeur par ligne, sous la forme clé|étiquette. La clé est une valeur qui sera stocker dans la base de données, elle doit correspondre au type de champ défini (%type). L'étiquette est optionnelle, si elle n'est pas précisée, la clé sera utilisée également comme étiquette.
                          Balises HTML autorisées : @tags", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '258', - 'translation' => "Une erreur s'est produite lors de l'ajout du nouveau type de contenu %type.
                          Veuillez vérifier les erreurs affichées pour plus de détails.", + 'lid' => '392', + 'translation' => 'Pour usage avancé seulement : code PHP fournissant un tableau par clé des valeurs autorisées. Ne doit pas inclure les délimiteurs <?php ?>. Si ce champ est rempli, le tableau renvoyé par le code prendra le pas sur la liste des valeurs autorisées apparaissant ci-dessus.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '259', - 'translation' => "Le champ importé '%field_label' (%field_name) n'a pas été ajouté à '%type' car ce champ existe déjà.", + 'lid' => '393', + 'translation' => 'Ce code PHP a été saisi par un administrateur et supplantera la liste des valeurs permises ci-dessus.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '260', - 'translation' => "Le champ importé '%field_label' (%field_name) a été ajouté au type de contenu '%type'.", + 'lid' => '394', + 'translation' => '@label (!name) - Valeurs autorisées', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '261', - 'translation' => 'content_copy', + 'lid' => '395', + 'translation' => '"Minimum" doit être un nombre.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '262', - 'translation' => 'Content Copy', + 'lid' => '396', + 'translation' => '"Maximum" doit être un nombre.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '263', - 'translation' => "Permet d'importer et d'exporter des définitions de champs.", + 'lid' => '397', + 'translation' => '%name : la valeur ne peut être inférieure à %min.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '264', - 'translation' => "Les champs d'un groupe Standard sont indépendants les uns des autres, et chacun peut soit avoir une valeur unique, soit des valeurs multiples. Les champs d'un Multigroupe sont traités comme une ensemble répétable de champs à valeurs uniques.", + 'lid' => '398', + 'translation' => '%name : la valeur ne peut être supérieure à %max.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '265', - 'translation' => 'Multigroupe', + 'lid' => '399', + 'translation' => '%name : valeur illégale.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '266', - 'translation' => 'Standard', + 'lid' => '400', + 'translation' => 'non mis en forme', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '267', - 'translation' => 'Type de groupe.', + 'lid' => '401', + 'translation' => 'Champ texte', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '268', - 'translation' => "Le champ %field a été mis à jour pour l'utilisation de valeurs %multiple, afin de correspondre au paramètre de valeur multipe du Multigroup %group.", + 'lid' => '402', + 'translation' => 'Seuls les nombres et les décimaux sont autorisés dans %field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '269', - 'translation' => "Cette modification n'est pas autorisée. Le champ %field possède déjà des valeurs multiples dans la base de données, mais le groupe %group en autorise seulement %group_max. Effectuer cette modification pourrait entraîner la perte de données. ", + 'lid' => '403', + 'translation' => 'Seuls les nombres sont autorisés dans %field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '270', - 'translation' => "Cette modification n'est pas autorisée. Le champ %field manipule les valeurs multiples différemment du module Content. Effectuer cette modification pourrait entraîner la perte de données. ", + 'lid' => '404', + 'translation' => 'Seuls les nombres et le caractère décimal (%decimal) sont autorisés dans %field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '271', - 'translation' => "Vous êtes en train d'inclure le champ %field dans un Multigroupe", + 'lid' => '405', + 'translation' => 'nombre', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '272', - 'translation' => "Cette modification n'est pas autorisée. Le champ %field possède déjà des données créées, et utilise un widget qui stocke les données différemment dans un groupe Standard que dans un Multigroupe. Effectuer ce changement pourrait entraîner la perte de données.", + 'lid' => '406', + 'translation' => 'Nombre', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '273', - 'translation' => "Vous êtes en train de retirer le champ %field d'un Multigroupe", + 'lid' => '407', + 'translation' => 'Définit des types de champs numériques.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '274', - 'translation' => 'Simple', + 'lid' => '408', + 'translation' => "Créez une liste d'options en tant que liste dans la Liste des valeurs autorisées, ou en tant que tableau en code PHP. Ces valeurs seront les mêmes pour le champ %field au sein de tous les types de contenu.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '275', - 'translation' => 'Groupe de champs', + 'lid' => '409', + 'translation' => "Pour un widget 'case à cocher oui/non', définissez la valeur 'non' en premier, puis la valeur 'oui', dans la section Valeurs autorisées. Notez que la case à cocher sera étiquetée avec l'étiquette de la valeur du 'oui'.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '276', - 'translation' => 'Ligne horizontale', + 'lid' => '410', + 'translation' => "Le widget 'cases à cocher/boutons radio' affichera des cases à cocher si l'option valeurs multiples est sélectionnées pour ce champ, autrement, des boutons radio seront affichés.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '277', - 'translation' => 'Tableau - Colonne unique', + 'lid' => '411', + 'translation' => "Vous devez préciser les 'valeurs autorisées' pour ce champ.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '278', - 'translation' => 'Tableau - Colonnes multiples', + 'lid' => '412', + 'translation' => 'Case à cocher on/off unique', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '279', - 'translation' => '[Format du sous-groupe]', + 'lid' => '413', + 'translation' => '%name : ce champ ne peut contenir plus de @count valeurs.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '280', - 'translation' => 'Paramètres multigroupe', + 'lid' => '414', + 'translation' => 'N/A', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '281', - 'translation' => 'Colonnes multiples', + 'lid' => '415', + 'translation' => '- Aucun -', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '282', - 'translation' => "Activez cette option pour rendre chaque champs dans une colonne disctincte sur le formulaire d'édition de noeud.", + 'lid' => '416', + 'translation' => 'optionwidgets', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '283', - 'translation' => "Activez cette option pour rendre obligatoire un minimum d'une collection de champs dans ce Multigroupe.", + 'lid' => '417', + 'translation' => 'Option Widgets', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '284', - 'translation' => "Nombre de fois où répéter l'ensemble Multigroupe de champs.", + 'lid' => '418', + 'translation' => 'Définit des widgets de liste déroulante, case à cocher et bouton radio pour des champs texte et numériques.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '285', - 'translation' => "'Illimité' fournira un bouton 'Ajouter plus' pour que les utilisateurs puissent ajouter des éléments autant de fois qu'ils le souhaitent.", + 'lid' => '419', + 'translation' => 'Enregistre le texte dans la base de données.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '286', - 'translation' => 'Tous les champs de ce groupe seront automatiquement paramétrés pour autoriser ce nombre de valeurs.', + 'lid' => '420', + 'translation' => 'Texte simple', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '287', - 'translation' => 'Nombre de répétitions', + 'lid' => '421', + 'translation' => "Texte filtré (l'utilisateur choisit le format d'entrée)", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '288', - 'translation' => 'Etiquettes', + 'lid' => '422', + 'translation' => 'Traitement du texte', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '289', - 'translation' => "Etiquettes pour chaque sous-groupe de champs. Les étiquettes peuvent être cachées ou affichées dans des contextes divers en utilisant l'écran 'Afficher les champs'.", + 'lid' => '423', + 'translation' => 'Taille maximale', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '290', - 'translation' => 'Etiquette du sous-groupe %number', + 'lid' => '424', + 'translation' => 'La taille maximale des champs, en caractères. Laisser vide pour ne pas limiter la taille.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '291', - 'translation' => 'Le champ %field dans ce groupe possède déjà %multiple valeurs dans la base de données. Pour éviter la perte de données, vous ne pouvez définir le nombre de valeurs du Multigroupe à une valeur inférieure.', + 'lid' => '425', + 'translation' => '%name : la valeur ne doit pas dépasser %max caractères.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '292', - 'translation' => 'Le champ !name est obligatoire dans le groupe @group.', + 'lid' => '426', + 'translation' => 'Par défaut', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '293', - 'translation' => 'Le groupe @name requiert au minimum un groupe de champs.', + 'lid' => '427', + 'translation' => 'Coupé', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '294', - 'translation' => 'Ajouter plus de valeurs', + 'lid' => '428', + 'translation' => 'Zone de texte (plusieurs lignes)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '295', - 'translation' => 'content_multigroup', + 'lid' => '429', + 'translation' => 'Taille du champ texte', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '296', - 'translation' => 'Contenu Multigroupe', + 'lid' => '430', + 'translation' => 'Rangées', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '297', - 'translation' => "Combinez de multiples champs CCK au sein de collections de champs qui fonctionnent à l'unisson.", + 'lid' => '431', + 'translation' => 'texte', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '298', - 'translation' => 'éditer', + 'lid' => '432', + 'translation' => 'Définit les types de champs en texte simple.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '299', - 'translation' => 'field_name', + 'lid' => '433', + 'translation' => 'Charge un utilisateur référencé', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '300', - 'translation' => 'voir', + 'lid' => '434', + 'translation' => 'Contenu contenant le champ userrefernece', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '301', - 'translation' => 'content_permissions', + 'lid' => '435', + 'translation' => 'Utilisateur référencé', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '302', - 'translation' => 'Veuillez configurer vos permissions sur les champs immédiatement. Tous les champs sont inaccessibles par défaut.', + 'lid' => '436', + 'translation' => 'Noter que si le champ possède des valeurs multiples, seul le premier utilisateur sera chargé', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '303', - 'translation' => 'Permissions sur les Contenus', + 'lid' => '437', + 'translation' => "Il n'y a aucun champ userreference défini", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '304', - 'translation' => 'Définit un niveau de permission par champ pour les champs CCK.', + 'lid' => '438', + 'translation' => 'User reference', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '305', - 'translation' => 'Contenu du groupe de champ', + 'lid' => '439', + 'translation' => "Stocke l'ID d'un utilisateur lié sous forme d'entier", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '306', - 'translation' => "Tous les champs d'un groupe de champs sur le node référencé.", + 'lid' => '440', + 'translation' => 'Rôles utilisateur pouvant être référencés', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '307', - 'translation' => '@group_label (@group_type_name)', + 'lid' => '441', + 'translation' => 'Statuts utilisateur pouvant être référencés', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '308', - 'translation' => 'Fieldgroup', + 'lid' => '442', + 'translation' => 'Actif', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '309', - 'translation' => "Texte à afficher si un groupe n'a pas de données. Notez que le titre ne s'affichera pas sauf s'il est surclassé.", + 'lid' => '443', + 'translation' => 'Bloqué', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '310', - 'translation' => '"@s" groupe de champs @name', + 'lid' => '444', + 'translation' => 'Avancé - Utilisateurs pouvant être référencés (Vue)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '311', - 'translation' => 'Paramètres du formulaire', + 'lid' => '445', + 'translation' => 'Vue utilisée pour sélectionner les utilisateurs', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '312', - 'translation' => "Ces paramètres s'appliquent au groupe dans le formulaire d'édition de nœud.", + 'lid' => '446', + 'translation' => '

                          Choisissez la vue du "module Views" qui sélectionne les utilisateurs pouvant être référencés.
                          Note :

                          ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '313', - 'translation' => 'Style', + 'lid' => '447', + 'translation' => "
                          • Seules les vues qui possèdent des champs fonctionneront à cet effet.
                          • Ceci annulera les paramètres \"Rôles Référençables\" et \"Statut Référençable\" ci-dessus. Utilisez la section \"filtres\" de la vue à la place.
                          • Utilisez la section \"champs\" de la vue pour afficher des informations supplémentaires à propos des utilisateurs candidats sur le formulaire de création/édition.
                          • Utilisez la section \"critère de tri\" de la vue pour déterminer l'ordre dans lequel les utilisateurs candidats seront affichés.
                          ", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '314', - 'translation' => 'toujours déplié', + 'lid' => '448', + 'translation' => "

                          La liste d'utilisateurs pouvant être référencés peut s'appueyr sur une vue du \"module Views\", mais aucune vue appropriée n'a été trouvée.
                          Note :

                          ", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '315', - 'translation' => 'repliable', + 'lid' => '449', + 'translation' => '%name : utilisateur invalide.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '316', - 'translation' => 'replié', + 'lid' => '450', + 'translation' => "Séléctionnez la méthode utilisée pour collecter les suggestions de l'autocomplétion. Notez que Contient peut engendrer des problèmes de performances sur des sites avec des milliers d'utilisateurs.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '317', - 'translation' => "Instructions à présenter à l'utilisateur dans le formulaire d'édition.", + 'lid' => '451', + 'translation' => 'Lien retour', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '318', - 'translation' => "Paramètres d'affichage", + 'lid' => '452', + 'translation' => "Si cette option est sélectionnée, un lien réciproque vers le nœud référençant sera affiché dans l'enregistrement utilisateur référencé.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '319', - 'translation' => "Ces paramètres s'appliquent au groupe à l'affichage du nœud.", + 'lid' => '453', + 'translation' => "%name : nous n'avons pas trouvé d'utilisateur valide pour ce nom.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '320', - 'translation' => 'Description du groupe.', + 'lid' => '454', + 'translation' => 'Contenu lié', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '321', - 'translation' => "Êtes-vous sûr(e) de vouloir supprimer le groupe '%label' ?", + 'lid' => '455', + 'translation' => 'Autocomplétion Userreference', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '322', - 'translation' => 'Cette action est irréversible.', + 'lid' => '456', + 'translation' => 'userreference', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '323', - 'translation' => "Le groupe '%group_name' a été supprimé.", + 'lid' => '457', + 'translation' => 'User Reference', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '324', - 'translation' => 'aucun', + 'lid' => '458', + 'translation' => 'Définit un type de champ pour référencer un utilisateur depuis un noeud.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '325', - 'translation' => 'Vous devez fournir une étiquette.', + 'lid' => '459', + 'translation' => 'Poids', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '326', - 'translation' => 'Vous devez fournir un nom de groupe', + 'lid' => '460', + 'translation' => 'Ajouter', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '327', - 'translation' => "Le nom de groupe %group_name n'est pas valide. Le nom ne doit contenir que des lettres sans accents, des nombres, et des underscores.", + 'lid' => '461', + 'translation' => 'Nouveau champ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '328', - 'translation' => "Le nom de groupe %group_name est trop long. Le nom est limité à 32 caractères, le préfixe 'group_' compris.", + 'lid' => '462', + 'translation' => 'Champ existant', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '329', - 'translation' => 'Le nom de groupe %group_name existe déjà.', + 'lid' => '463', + 'translation' => 'Nouveau groupe', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '330', - 'translation' => 'Ajouter un nouveau groupe :', + 'lid' => '464', + 'translation' => "Ajouter des champs et des groupes au type de contenu, et les paramétrer pour l'affichage du contenu et les formulaires de saisie.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '331', - 'translation' => 'Ajouter un nouveau groupe : vous devez fournir une étiquette.', + 'lid' => '465', + 'translation' => 'Vous pouvez ajouter un champ à un groupe en le faisant glisser ci-dessous et à la droite du groupe.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '332', - 'translation' => 'Ajouter un nouveau groupe : vous devez fournir un nom de groupe.', + 'lid' => '466', + 'translation' => "Note : l'installation du module Aide avancée (Advanced help) vous permettra d'accéder à plus d'aide, et de meilleure qualité.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '333', - 'translation' => 'Groupe standard', + 'lid' => '467', + 'translation' => "Utiliser la case à cocher 'Exclure' pour exclure un élément de la valeur de !content transmis au gabarit du node.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '334', - 'translation' => 'Éditer le groupe', + 'lid' => '468', + 'translation' => 'Supprimer cet élément', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '335', - 'translation' => 'fieldgroup', + 'lid' => '469', + 'translation' => 'Ajouter un champ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '336', - 'translation' => "Créée des groupes d'affichage pour les champs CCK.", + 'lid' => '470', + 'translation' => "Valeur illégale pour le champ '%name'.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '337', - 'translation' => 'Charge un noeud référencé', + 'lid' => '471', + 'translation' => "La longueur de '%label' dépasse %max caractères.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '338', - 'translation' => 'Contenu contenant le champ node reference', + 'lid' => '472', + 'translation' => "Le champ 'Rangées' doit être un entier positif.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '339', - 'translation' => 'Contenu référencé', + 'lid' => '473', + 'translation' => "Les valeurs que ce champ peut contenir. Saisissez une valeur par ligne, au format clé|libellé. La clé est la valeur qui sera enregistrée dans la base de données et elle doit correspondre au type définit pour le champ dans la base, '%type'. Le libellé est optionnel et, si aucune valeur n'est spécifiée, la clé sera utilisée comme libellé.
                          Balises HTML autorisées : @tags", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '340', - 'translation' => 'Notez que si le champs possède des valeurs multiples, seul le premier contenu sera chargé.', + 'lid' => '474', + 'translation' => 'ajouter un champ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '341', - 'translation' => "Il n'y a aucun champ nodereference défini.", + 'lid' => '475', + 'translation' => "Il n'y a aucun champ configuré pour ce type de contenu. Vous pouvez néanmoins dès maintenant !link.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '342', - 'translation' => 'Node référence', + 'lid' => '476', + 'translation' => 'ajouter un nouveau champ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '343', - 'translation' => "Stocker l'ID du noeud lié en tant que valeur entière.", + 'lid' => '477', + 'translation' => "Pour changer l'ordre d'un champ, utilisez la poignée qui figure sous la colonne Étiquette et faites glisser le champ jusqu'à l'emplacement désiré. Pour manipuler la poignée (l'icône en forme de croix), cliquez sur l'icône et maintenez enfoncé le bouton de la souris. N'oubliez pas que vos modifications ne seront prises en compte que lorsque vous aurez cliqué sur le bouton Enregistrer qui figure en base de page.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) -->values(array( - 'lid' => '344', - 'translation' => 'Types de contenu pouvant être référencés', +->values(array( + 'lid' => '478', + 'translation' => "Aucun module de champs n'est activé. Vous devez en activer un, par exemple le module Text, avant de pouvoir ajouter des champs.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '345', - 'translation' => 'Vues par défaut', + 'lid' => '479', + 'translation' => 'Ajouter un champ existant', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '346', - 'translation' => 'Vues éxistantes', + 'lid' => '480', + 'translation' => 'Créer un nouveau champ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '347', - 'translation' => 'Avancé - Nœuds pouvant être référencés (Vue)', + 'lid' => '481', + 'translation' => 'Nom du champ, lisible par une machine.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '348', - 'translation' => 'Vue utilisée pour sélectionner les noeuds', + 'lid' => '482', + 'translation' => ' Ce nom ne peut être modifié.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '349', - 'translation' => '

                          Choisissez la vue du "module Views" qui sélectionne les noeuds pouvant être référencés.
                          Note :

                          ', + 'lid' => '483', + 'translation' => " Ce nom ne pourra pas être modifié par la suite ! Le nom sera préfixé par 'field_' et peut inclure des lettres minuscules non accentués, des nombres et des caractères \"espace souligné\". La longueur du nom, y compris le préfixe, est limitée à un total de 32 caractères.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '350', - 'translation' => "
                          • Seules les vues qui possèdent des champs fonctionneront à cet effet.
                          • Ceci annulera les paramètres \"Types de contenu\" ci-dessus. Utilisez la section \"filtres\" de la vue à la place.
                          • Utilisez la section \"champs\" de la vue pour afficher des informations supplémentaires à propos des noeuds candidats sur le formulaire de création/édition.
                          • Utilisez la section \"critère de tri\" de la vue pour déterminer l'ordre dans lequel les noeuds candidats seront affichés.
                          ", + 'lid' => '484', + 'translation' => 'Type de données que vous souhaitez enregistrer, par le biais de ce champ, dans la base de données.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '351', - 'translation' => 'Arguments de la vue', + 'lid' => '485', + 'translation' => "Le nom de champ '%field_name' est invalide. Le nom ne doit comporter que des lettres minuscules non accentuées, des nombres ou des espaces soulignés.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '352', - 'translation' => "Fournit une liste d'arguments, séparés par des virgules, à transmettre à la vue.", + 'lid' => '486', + 'translation' => "Le nom de champ '%field_name' est trop long. La longueur du nom est limitée à 32 caractères, y compris le préfixe 'field_'.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '353', - 'translation' => "

                          La liste des noeuds pouvant être référencés peut s'appuyer sur une vue du \"module Views\" mais aucune vue appropriée n'a été trouvée.
                          Note :

                          ", + 'lid' => '487', + 'translation' => "Le nom de champ '%field_name' existe déjà.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '354', - 'translation' => '%name : saisie non valide.', + 'lid' => '488', + 'translation' => "Le nom 'field_instance' est un nom réservé.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '355', - 'translation' => '%name : ce contenu ne peut être référencé.', + 'lid' => '489', + 'translation' => "Le champ '%label' a été créé.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '356', - 'translation' => 'Titre (avec lien)', + 'lid' => '490', + 'translation' => "Mettre à jour le champ '%label'.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '357', - 'translation' => 'Titre (sans lien)', + 'lid' => '491', + 'translation' => "Un problème est survenu à la mise à jour du champ '%label'.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '358', - 'translation' => 'Liste de sélection', + 'lid' => '492', + 'translation' => "Réservé à une utilisation avancée : code PHP renvoyant la valeur par défaut du champ. Ne doit pas inclure les délimiteurs <?php ?>. Si ce champ est renseigné, la valeur renvoyée par le code supplante toute valeur indiquée ci-desus. Format attendu :
                          !sample
                          Vous pouvez utiliser l'onglet 'devel load' du module !link_devel sur un nœud de type '%type' pour mieux comprendre le format attendu.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '359', - 'translation' => 'Cases à cocher/boutons radio', + 'lid' => '493', + 'translation' => "Choisissez un nombre de valeurs pour ce champ, ou 'Illimité' pour proposer un bouton 'Ajouter' aux utilisateurs, de sorte qu'ils peuvent insérer autant de valeurs qu'ils le souhaitent.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '360', - 'translation' => 'Champ texte à auto-complètement', + 'lid' => '494', + 'translation' => 'Le code PHP de valeur par défaut a créé @value, qui est invalide.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '361', - 'translation' => "Correspondance de l'autocomplétion", + 'lid' => '495', + 'translation' => 'Lien HTML mis en forme vers le nœud', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '362', - 'translation' => 'Commence par', + 'lid' => '496', + 'translation' => "La valeur de '%name 'ne peut être plus petite que %min.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '363', - 'translation' => 'Contient', + 'lid' => '497', + 'translation' => "La valeur de '%name' ne peut pas être plus grande que %max.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '364', - 'translation' => "Séléctionnez la méthode utilisée pour collecter les suggestions de l'autocomplétion. Notez que Contient peut engendrer des problèmes de performances sur des sites avec des milliers de noeuds", + 'lid' => '498', + 'translation' => "Seuls des nombres et des décimaux sont autorisés dans '%field'. La valeur saisie, '%start', a été modifié en '%value'.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '365', - 'translation' => '%name : différence de titre. Veuillez vérifier votre sélection.', + 'lid' => '499', + 'translation' => "Seuls des nombres sont autorisés dans '%field'. La valeur saisie, '%start', a été modifié en '%value'.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '366', - 'translation' => "%name : aucun contenu valide n'a été trouvé pour ce titre.", + 'lid' => '500', + 'translation' => "Seuls des nombres et le marqueur décimal (%decimal) sont autorisés dans '%field'. La valeur saisie, '%start', a été modifié en '%value'.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '367', - 'translation' => 'Autocomplétion de nodereference', + 'lid' => '501', + 'translation' => "Créez une liste d'options en tant que liste dans les Valeurs permises ou en tant que tableau dans le code PHP. Ces valeurs seront identiques pour '%field' dans tous les types de contenus.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '368', - 'translation' => 'nodereference', + 'lid' => '504', + 'translation' => 'fr - Favorite color', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '369', - 'translation' => 'Node Reference', + 'lid' => '505', + 'translation' => 'Inscrivez votre couleur préférée', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '370', - 'translation' => 'Définit un type de champ pour référencer un noeud depuis un autre noeud.', + 'lid' => '506', + 'translation' => 'fr - Personal information', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '371', - 'translation' => 'Entier', + 'lid' => '507', + 'translation' => 'fr - Biography', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '372', - 'translation' => 'Stocke un nombre dans la base de données en format entier.', + 'lid' => '508', + 'translation' => 'fr - Tell people a little bit about yourself', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '373', - 'translation' => 'Décimal', + 'lid' => '509', + 'translation' => 'fr - Sell your email address?', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '374', - 'translation' => 'Stocke un nombre dans la base de données en format décimal fixe.', + 'lid' => '510', + 'translation' => "fr - If you check this box, we'll sell your address to spammers to help line the pockets of our shareholders. Thanks!", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '375', - 'translation' => 'Réel (Float)', + 'lid' => '511', + 'translation' => 'fr - Communication preferences', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '376', - 'translation' => 'Stocke un nombre dans la base de données en format réel.', + 'lid' => '512', + 'translation' => 'fr - Sales Category', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '377', - 'translation' => 'Minimum', + 'lid' => '513', + 'translation' => "fr - Select the sales categories to which this user's address was sold.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '378', - 'translation' => 'Maximum', + 'lid' => '514', + 'translation' => 'fr - Pill spammers Fitness spammers Back\slash Forward/slash Dot.in.the.middle', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '379', - 'translation' => 'Précision', + 'lid' => '515', + 'translation' => 'fr - Administrative data', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '380', - 'translation' => 'Le nombre total de chiffres à stocker dans la base de données, en incluant ceux à droite de la virgule.', + 'lid' => '516', + 'translation' => 'Mes groupes préférés', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '381', - 'translation' => 'Echelle', + 'lid' => '517', + 'translation' => "fr - Enter your favorite bands. When you've saved your profile, you'll be able to find other people with the same favorites.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '382', - 'translation' => 'Le nombre de chiffres à droite de la virgule', + 'lid' => '518', + 'translation' => 'fr - Birthdate', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '383', - 'translation' => 'Séparateur de décimales', + 'lid' => '519', + 'translation' => "fr - Enter your birth date and we'll send you a coupon.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '384', - 'translation' => 'Le caractère que les utilisateurs saisiront pour séparer les décimales dans les formulaires.', + 'lid' => '520', + 'translation' => "J'aime les migrations", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '385', - 'translation' => 'Préfixe', + 'lid' => '521', + 'translation' => 'Si vous cochez cette case, vous aimez les migrations.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '386', - 'translation' => 'Définissez une chaîne de caractères à utiliser pour préfixer la valeur, par exemple $ ou €. Laissez vide pour ne rien afficher de plus. Séparez les valeurs singulier et pluriel par une barre verticale (euro|euros).', + 'lid' => '522', + 'translation' => 'fr - Blog', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '387', - 'translation' => 'Suffixe', + 'lid' => '523', + 'translation' => 'fr - Paste the full URL, including http://, of your personal blog.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '388', - 'translation' => 'Définissez une chaîne qui sera ajoutée en suffixe à la valeur, comme m², m/s², kb/s. Laisser vide pour aucun suffixe. Séparez les singulier et pluriel avec un pipe (mètre|mètres).', + 'lid' => '524', + 'translation' => 'fr - Static Block', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '389', - 'translation' => 'Valeurs autorisées', + 'lid' => '525', + 'translation' => '

                          fr - My first custom block body

                          ', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '390', - 'translation' => 'Liste des valeurs autorisées', + 'lid' => '526', + 'translation' => 'Encore un bloc statique', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '391', - 'translation' => "Les valeurs possibles que ce champ peut contenir. Entrez une valeur par ligne, sous la forme clé|étiquette. La clé est une valeur qui sera stocker dans la base de données, elle doit correspondre au type de champ défini (%type). L'étiquette est optionnelle, si elle n'est pas précisée, la clé sera utilisée également comme étiquette.
                          Balises HTML autorisées : @tags", + 'lid' => '527', + 'translation' => 'Nom de vocabulaire beaucoup plus long que trente-deux caractères', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '392', - 'translation' => 'Pour usage avancé seulement : code PHP fournissant un tableau par clé des valeurs autorisées. Ne doit pas inclure les délimiteurs <?php ?>. Si ce champ est rempli, le tableau renvoyé par le code prendra le pas sur la liste des valeurs autorisées apparaissant ci-dessus.', + 'lid' => '528', + 'translation' => 'fr - Tags', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '393', - 'translation' => 'Ce code PHP a été saisi par un administrateur et supplantera la liste des valeurs permises ci-dessus.', + 'lid' => '529', + 'translation' => 'fr - vocabulary 1 (i=0)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '394', - 'translation' => '@label (!name) - Valeurs autorisées', + 'lid' => '530', + 'translation' => 'fr - vocabulary 2 (i=1)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '395', - 'translation' => '"Minimum" doit être un nombre.', + 'lid' => '531', + 'translation' => 'fr - vocabulary 3 (i=2)', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '396', - 'translation' => '"Maximum" doit être un nombre.', + 'lid' => '532', + 'translation' => 'Nom de vocabulaire beaucoup plus long que trente-deux caractères', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '397', - 'translation' => '%name : la valeur ne peut être inférieure à %min.', + 'lid' => '533', + 'translation' => 'fr - Article', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '398', - 'translation' => '%name : la valeur ne peut être supérieure à %max.', + 'lid' => '534', + 'translation' => 'fr - Title', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '399', - 'translation' => '%name : valeur illégale.', + 'lid' => '535', + 'translation' => 'fr - Body', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '400', - 'translation' => 'non mis en forme', + 'lid' => '536', + 'translation' => 'fr - An article, content type.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '401', - 'translation' => 'Champ texte', + 'lid' => '537', + 'translation' => 'fr - Company', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '402', - 'translation' => 'Seuls les nombres et les décimaux sont autorisés dans %field.', + 'lid' => '538', + 'translation' => 'fr - Name', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '403', - 'translation' => 'Seuls les nombres sont autorisés dans %field.', + 'lid' => '539', + 'translation' => 'fr - Description', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '404', - 'translation' => 'Seuls les nombres et le caractère décimal (%decimal) sont autorisés dans %field.', + 'lid' => '540', + 'translation' => 'fr - Company node type', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '405', - 'translation' => 'nombre', + 'lid' => '541', + 'translation' => 'fr - Employee', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '406', - 'translation' => 'Nombre', + 'lid' => '542', + 'translation' => 'fr - Name', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '407', - 'translation' => 'Définit des types de champs numériques.', + 'lid' => '543', + 'translation' => 'fr - Bio', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '408', - 'translation' => "Créez une liste d'options en tant que liste dans la Liste des valeurs autorisées, ou en tant que tableau en code PHP. Ces valeurs seront les mêmes pour le champ %field au sein de tous les types de contenu.", + 'lid' => '544', + 'translation' => 'fr - Employee node type', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '409', - 'translation' => "Pour un widget 'case à cocher oui/non', définissez la valeur 'non' en premier, puis la valeur 'oui', dans la section Valeurs autorisées. Notez que la case à cocher sera étiquetée avec l'étiquette de la valeur du 'oui'.", + 'lid' => '545', + 'translation' => 'fr - Sponsor', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '410', - 'translation' => "Le widget 'cases à cocher/boutons radio' affichera des cases à cocher si l'option valeurs multiples est sélectionnées pour ce champ, autrement, des boutons radio seront affichés.", + 'lid' => '546', + 'translation' => 'fr - Name', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '411', - 'translation' => "Vous devez préciser les 'valeurs autorisées' pour ce champ.", + 'lid' => '547', + 'translation' => 'fr - Body', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '412', - 'translation' => 'Case à cocher on/off unique', + 'lid' => '548', + 'translation' => 'fr - Sponsor node type', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '413', - 'translation' => '%name : ce champ ne peut contenir plus de @count valeurs.', + 'lid' => '549', + 'translation' => 'fr - Story', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '414', - 'translation' => 'N/A', + 'lid' => '550', + 'translation' => 'fr - Title', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '415', - 'translation' => '- Aucun -', + 'lid' => '551', + 'translation' => 'fr - Body', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '416', - 'translation' => 'optionwidgets', + 'lid' => '552', + 'translation' => "fr - A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '417', - 'translation' => 'Option Widgets', + 'lid' => '553', + 'translation' => 'fr - Migrate test event', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '418', - 'translation' => 'Définit des widgets de liste déroulante, case à cocher et bouton radio pour des champs texte et numériques.', + 'lid' => '554', + 'translation' => 'fr - Event Name', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '419', - 'translation' => 'Enregistre le texte dans la base de données.', + 'lid' => '555', + 'translation' => 'fr - Body', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '420', - 'translation' => 'Texte simple', + 'lid' => '556', + 'translation' => 'fr - test event description here', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '421', - 'translation' => "Texte filtré (l'utilisateur choisit le format d'entrée)", + 'lid' => '558', + 'translation' => 'fr - Migrate test page', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '422', - 'translation' => 'Traitement du texte', + 'lid' => '559', + 'translation' => 'fr - Title', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '423', - 'translation' => 'Taille maximale', + 'lid' => '560', + 'translation' => 'fr - This is the body field label', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '424', - 'translation' => 'La taille maximale des champs, en caractères. Laisser vide pour ne pas limiter la taille.', + 'lid' => '561', + 'translation' => "fr - A page, similar in form to a story, is a simple method for creating and displaying information that rarely changes, such as an \"About us\" section of a website. By default, a page entry does not allow visitor comments and is not featured on the site's initial home page.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '425', - 'translation' => '%name : la valeur ne doit pas dépasser %max caractères.', + 'lid' => '562', + 'translation' => 'fr - Migrate test planet', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '426', - 'translation' => 'Par défaut', + 'lid' => '563', + 'translation' => 'fr - Title', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '427', - 'translation' => 'Coupé', + 'lid' => '564', + 'translation' => 'fr - Body', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '428', - 'translation' => 'Zone de texte (plusieurs lignes)', + 'lid' => '565', + 'translation' => "fr - A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '429', - 'translation' => 'Taille du champ texte', + 'lid' => '566', + 'translation' => 'Migrer histoire de test', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '430', - 'translation' => 'Rangées', + 'lid' => '567', + 'translation' => 'Titre', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '431', - 'translation' => 'texte', + 'lid' => '568', + 'translation' => 'Le corps', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '432', - 'translation' => 'Définit les types de champs en texte simple.', + 'lid' => '569', + 'translation' => "A histoire, de forme semblable à un page, est idéal pour la création et l'affichage du contenu qui informe et pousse les visiteurs du site. Communiqués de presse, des annonces du site, et les entrées de blog comme informels peuvent être créés avec un histoire entrée. Par défaut, un histoire entrée est automatiquement présenté sur la page d'accueil initiale du site, et offre la possibilité de poster des commentaires.", 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '433', - 'translation' => 'Charge un utilisateur référencé', + 'lid' => '570', + 'translation' => 'fr - Text Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '434', - 'translation' => 'Contenu contenant le champ userrefernece', + 'lid' => '571', + 'translation' => 'fr - An example text field without exclude.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '435', - 'translation' => 'Utilisateur référencé', + 'lid' => '572', + 'translation' => 'fr - Integer Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '436', - 'translation' => 'Noter que si le champ possède des valeurs multiples, seul le premier utilisateur sera chargé', + 'lid' => '573', + 'translation' => 'fr - An example integer field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '437', - 'translation' => "Il n'y a aucun champ userreference défini", + 'lid' => '574', + 'translation' => 'fr - Text Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '438', - 'translation' => 'User reference', + 'lid' => '575', + 'translation' => 'fr - An example text field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '439', - 'translation' => "Stocke l'ID d'un utilisateur lié sous forme d'entier", + 'lid' => '576', + 'translation' => 'fr - Decimal Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '440', - 'translation' => 'Rôles utilisateur pouvant être référencés', + 'lid' => '577', + 'translation' => 'fr - An example decimal field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '441', - 'translation' => 'Statuts utilisateur pouvant être référencés', + 'lid' => '578', + 'translation' => 'fr - Float Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '442', - 'translation' => 'Actif', + 'lid' => '579', + 'translation' => 'fr - An example float field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '443', - 'translation' => 'Bloqué', + 'lid' => '580', + 'translation' => 'fr - Integer Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '444', - 'translation' => 'Avancé - Utilisateurs pouvant être référencés (Vue)', + 'lid' => '581', + 'translation' => 'fr - An example integer field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '445', - 'translation' => 'Vue utilisée pour sélectionner les utilisateurs', + 'lid' => '582', + 'translation' => 'fr - Integer Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '446', - 'translation' => '

                          Choisissez la vue du "module Views" qui sélectionne les utilisateurs pouvant être référencés.
                          Note :

                          ', + 'lid' => '583', + 'translation' => 'fr - An example integer field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) -->values(array( - 'lid' => '447', - 'translation' => "
                          • Seules les vues qui possèdent des champs fonctionneront à cet effet.
                          • Ceci annulera les paramètres \"Rôles Référençables\" et \"Statut Référençable\" ci-dessus. Utilisez la section \"filtres\" de la vue à la place.
                          • Utilisez la section \"champs\" de la vue pour afficher des informations supplémentaires à propos des utilisateurs candidats sur le formulaire de création/édition.
                          • Utilisez la section \"critère de tri\" de la vue pour déterminer l'ordre dans lequel les utilisateurs candidats seront affichés.
                          ", +->values(array( + 'lid' => '584', + 'translation' => 'fr - Email Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '448', - 'translation' => "

                          La liste d'utilisateurs pouvant être référencés peut s'appueyr sur une vue du \"module Views\", mais aucune vue appropriée n'a été trouvée.
                          Note :

                          ", + 'lid' => '585', + 'translation' => 'fr - An example email field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '449', - 'translation' => '%name : utilisateur invalide.', + 'lid' => '586', + 'translation' => 'fr - Link Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '450', - 'translation' => "Séléctionnez la méthode utilisée pour collecter les suggestions de l'autocomplétion. Notez que Contient peut engendrer des problèmes de performances sur des sites avec des milliers d'utilisateurs.", + 'lid' => '587', + 'translation' => 'fr - An example link field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '451', - 'translation' => 'Lien retour', + 'lid' => '588', + 'translation' => 'fr - File Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '452', - 'translation' => "Si cette option est sélectionnée, un lien réciproque vers le nœud référençant sera affiché dans l'enregistrement utilisateur référencé.", + 'lid' => '589', + 'translation' => 'fr - An example image field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '453', - 'translation' => "%name : nous n'avons pas trouvé d'utilisateur valide pour ce nom.", + 'lid' => '590', + 'translation' => 'fr - Image Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '454', - 'translation' => 'Contenu lié', + 'lid' => '591', + 'translation' => 'fr - An example image field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '455', - 'translation' => 'Autocomplétion Userreference', + 'lid' => '592', + 'translation' => 'fr - Date Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '456', - 'translation' => 'userreference', + 'lid' => '593', + 'translation' => 'fr - An example date field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '457', - 'translation' => 'User Reference', + 'lid' => '594', + 'translation' => 'fr - Date Stamp Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '458', - 'translation' => 'Définit un type de champ pour référencer un utilisateur depuis un noeud.', + 'lid' => '595', + 'translation' => 'fr - An example date stamp field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '459', - 'translation' => 'Poids', + 'lid' => '596', + 'translation' => 'fr - Datetime Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '460', - 'translation' => 'Ajouter', + 'lid' => '597', + 'translation' => 'fr - An example datetime field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '461', - 'translation' => 'Nouveau champ', + 'lid' => '598', + 'translation' => 'fr - Phone Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '462', - 'translation' => 'Champ existant', + 'lid' => '599', + 'translation' => 'fr - An example phone field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '463', - 'translation' => 'Nouveau groupe', + 'lid' => '600', + 'translation' => 'fr - Decimal Radio Buttons Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '464', - 'translation' => "Ajouter des champs et des groupes au type de contenu, et les paramétrer pour l'affichage du contenu et les formulaires de saisie.", + 'lid' => '601', + 'translation' => 'fr - An example decimal field using radio buttons.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '465', - 'translation' => 'Vous pouvez ajouter un champ à un groupe en le faisant glisser ci-dessous et à la droite du groupe.', + 'lid' => '604', + 'translation' => 'fr - Float Single Checkbox Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '466', - 'translation' => "Note : l'installation du module Aide avancée (Advanced help) vous permettra d'accéder à plus d'aide, et de meilleure qualité.", + 'lid' => '605', + 'translation' => 'fr - An example float field using a single on/off checkbox.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '467', - 'translation' => "Utiliser la case à cocher 'Exclure' pour exclure un élément de la valeur de !content transmis au gabarit du node.", + 'lid' => '608', + 'translation' => 'fr - Integer Select List Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '468', - 'translation' => 'Supprimer cet élément', + 'lid' => '609', + 'translation' => 'fr - An example integer field using a select list.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '469', - 'translation' => 'Ajouter un champ', + 'lid' => '614', + 'translation' => 'fr - Text Single Checkbox Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '470', - 'translation' => "Valeur illégale pour le champ '%name'.", + 'lid' => '615', + 'translation' => 'fr - An example text field using a single on/off checkbox.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '471', - 'translation' => "La longueur de '%label' dépasse %max caractères.", + 'lid' => '616', + 'translation' => 'fr - Hello', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '472', - 'translation' => "Le champ 'Rangées' doit être un entier positif.", + 'lid' => '617', + 'translation' => 'fr - Goodbye', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '473', - 'translation' => "Les valeurs que ce champ peut contenir. Saisissez une valeur par ligne, au format clé|libellé. La clé est la valeur qui sera enregistrée dans la base de données et elle doit correspondre au type définit pour le champ dans la base, '%type'. Le libellé est optionnel et, si aucune valeur n'est spécifiée, la clé sera utilisée comme libellé.
                          Balises HTML autorisées : @tags", + 'lid' => '618', + 'translation' => 'fr - Text Single Checkbox Field 2', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '474', - 'translation' => 'ajouter un champ', + 'lid' => '619', + 'translation' => 'fr - Checkbox that uses keys only and no label.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '475', - 'translation' => "Il n'y a aucun champ configuré pour ce type de contenu. Vous pouvez néanmoins dès maintenant !link.", + 'lid' => '620', + 'translation' => 'fr - Off', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '476', - 'translation' => 'ajouter un nouveau champ', + 'lid' => '621', + 'translation' => 'fr - Hello', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '477', - 'translation' => "Pour changer l'ordre d'un champ, utilisez la poignée qui figure sous la colonne Étiquette et faites glisser le champ jusqu'à l'emplacement désiré. Pour manipuler la poignée (l'icône en forme de croix), cliquez sur l'icône et maintenez enfoncé le bouton de la souris. N'oubliez pas que vos modifications ne seront prises en compte que lorsque vous aurez cliqué sur le bouton Enregistrer qui figure en base de page.", + 'lid' => '622', + 'translation' => 'Champ de texte', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '478', - 'translation' => "Aucun module de champs n'est activé. Vous devez en activer un, par exemple le module Text, avant de pouvoir ajouter des champs.", + 'lid' => '623', + 'translation' => 'fr - An example text field.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '479', - 'translation' => 'Ajouter un champ existant', + 'lid' => '624', + 'translation' => 'fr - Decimal Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '480', - 'translation' => 'Créer un nouveau champ', + 'lid' => '625', + 'translation' => 'Un exemple plusieurs valeurs champ décimal.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '481', - 'translation' => 'Nom du champ, lisible par une machine.', + 'lid' => '626', + 'translation' => 'fr - Text Single Checkbox Field', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '482', - 'translation' => ' Ce nom ne peut être modifié.', + 'lid' => '627', + 'translation' => 'fr - An example text field using a single on/off checkbox.', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '483', - 'translation' => " Ce nom ne pourra pas être modifié par la suite ! Le nom sera préfixé par 'field_' et peut inclure des lettres minuscules non accentués, des nombres et des caractères \"espace souligné\". La longueur du nom, y compris le préfixe, est limitée à un total de 32 caractères.", + 'lid' => '633', + 'translation' => 'fr - Drupal.org', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '484', - 'translation' => 'Type de données que vous souhaitez enregistrer, par le biais de ce champ, dans la base de données.', + 'lid' => '634', + 'translation' => 'fr - Test 2', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '485', - 'translation' => "Le nom de champ '%field_name' est invalide. Le nom ne doit comporter que des lettres minuscules non accentuées, des nombres ou des espaces soulignés.", + 'lid' => '635', + 'translation' => 'fr - Test menu link 2', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '486', - 'translation' => "Le nom de champ '%field_name' est trop long. La longueur du nom est limitée à 32 caractères, y compris le préfixe 'field_'.", + 'lid' => '663', + 'translation' => 'fr - Content management', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '487', - 'translation' => "Le nom de champ '%field_name' existe déjà.", + 'lid' => '1254', + 'translation' => 'fr - Modules', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '488', - 'translation' => "Le nom 'field_instance' est un nom réservé.", + 'lid' => '1264', + 'translation' => 'fr - By task', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '489', - 'translation' => "Le champ '%label' a été créé.", + 'lid' => '1265', + 'translation' => 'fr - By module', 'language' => 'fr', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '490', - 'translation' => "Mettre à jour le champ '%label'.", - 'language' => 'fr', + 'lid' => '66', + 'translation' => 'zu - CCK - Aucune Intégration aux Vues', + 'language' => 'zu', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '491', - 'translation' => "Un problème est survenu à la mise à jour du champ '%label'.", - 'language' => 'fr', + 'lid' => '506', + 'translation' => 'zu - Personal information', + 'language' => 'zu', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '492', - 'translation' => "Réservé à une utilisation avancée : code PHP renvoyant la valeur par défaut du champ. Ne doit pas inclure les délimiteurs <?php ?>. Si ce champ est renseigné, la valeur renvoyée par le code supplante toute valeur indiquée ci-desus. Format attendu :
                          !sample
                          Vous pouvez utiliser l'onglet 'devel load' du module !link_devel sur un nœud de type '%type' pour mieux comprendre le format attendu.", - 'language' => 'fr', + 'lid' => '512', + 'translation' => 'zu - Sales Category', + 'language' => 'zu', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '493', - 'translation' => "Choisissez un nombre de valeurs pour ce champ, ou 'Illimité' pour proposer un bouton 'Ajouter' aux utilisateurs, de sorte qu'ils peuvent insérer autant de valeurs qu'ils le souhaitent.", - 'language' => 'fr', + 'lid' => '513', + 'translation' => "zu - Select the sales categories to which this user's address was sold.", + 'language' => 'zu', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '494', - 'translation' => 'Le code PHP de valeur par défaut a créé @value, qui est invalide.', - 'language' => 'fr', + 'lid' => '514', + 'translation' => 'zu - Pill spammers Fitness spammers Back\slash Forward/slash Dot.in.the.middle', + 'language' => 'zu', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '495', - 'translation' => 'Lien HTML mis en forme vers le nœud', - 'language' => 'fr', + 'lid' => '525', + 'translation' => '

                          zu - My first custom block body

                          ', + 'language' => 'zu', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '496', - 'translation' => "La valeur de '%name 'ne peut être plus petite que %min.", - 'language' => 'fr', + 'lid' => '529', + 'translation' => 'zu - vocabulary 1 (i=0)', + 'language' => 'zu', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '497', - 'translation' => "La valeur de '%name' ne peut pas être plus grande que %max.", - 'language' => 'fr', + 'lid' => '535', + 'translation' => 'zu - Body', + 'language' => 'zu', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '498', - 'translation' => "Seuls des nombres et des décimaux sont autorisés dans '%field'. La valeur saisie, '%start', a été modifié en '%value'.", - 'language' => 'fr', + 'lid' => '590', + 'translation' => 'zu - Image Field', + 'language' => 'zu', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '499', - 'translation' => "Seuls des nombres sont autorisés dans '%field'. La valeur saisie, '%start', a été modifié en '%value'.", - 'language' => 'fr', + 'lid' => '591', + 'translation' => 'zu - An example image field.', + 'language' => 'zu', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '500', - 'translation' => "Seuls des nombres et le marqueur décimal (%decimal) sont autorisés dans '%field'. La valeur saisie, '%start', a été modifié en '%value'.", - 'language' => 'fr', + 'lid' => '621', + 'translation' => 'zu - Hello', + 'language' => 'zu', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->values(array( - 'lid' => '501', - 'translation' => "Créez une liste d'options en tant que liste dans les Valeurs permises ou en tant que tableau dans le code PHP. Ces valeurs seront identiques pour '%field' dans tous les types de contenus.", - 'language' => 'fr', + 'lid' => '635', + 'translation' => 'zu - Test menu link 2', + 'language' => 'zu', 'plid' => '0', 'plural' => '0', + 'i18n_status' => '0', )) ->execute(); @@ -20019,11 +31276,146 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '361', - 'plid' => '357', - 'link_path' => 'admin/content/aggregator/edit/feed/%', - 'router_path' => 'admin/content/aggregator/edit/feed/%', - 'link_title' => 'Edit feed', + 'mlid' => '361', + 'plid' => '357', + 'link_path' => 'admin/content/aggregator/edit/feed/%', + 'router_path' => 'admin/content/aggregator/edit/feed/%', + 'link_title' => 'Edit feed', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '4', + 'customized' => '0', + 'p1' => '144', + 'p2' => '157', + 'p3' => '357', + 'p4' => '361', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '363', + 'plid' => '0', + 'link_path' => 'book', + 'router_path' => 'book', + 'link_title' => 'Books', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '1', + 'customized' => '0', + 'p1' => '363', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '364', + 'plid' => '363', + 'link_path' => 'book/js/form', + 'router_path' => 'book/js/form', + 'link_title' => '', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '2', + 'customized' => '0', + 'p1' => '363', + 'p2' => '364', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '366', + 'plid' => '157', + 'link_path' => 'admin/content/book', + 'router_path' => 'admin/content/book', + 'link_title' => 'Books', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:33:\"Manage your site's book outlines.\";}}", + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '144', + 'p2' => '157', + 'p3' => '366', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '367', + 'plid' => '363', + 'link_path' => 'book/export/%/%', + 'router_path' => 'book/export/%/%', + 'link_title' => '', + 'options' => 'a:0:{}', + 'module' => 'system', + 'hidden' => '-1', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '2', + 'customized' => '0', + 'p1' => '363', + 'p2' => '367', + 'p3' => '0', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '369', + 'plid' => '366', + 'link_path' => 'admin/content/book/%', + 'router_path' => 'admin/content/book/%', + 'link_title' => 'Re-order book pages and change titles', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20035,8 +31427,8 @@ 'customized' => '0', 'p1' => '144', 'p2' => '157', - 'p3' => '357', - 'p4' => '361', + 'p3' => '366', + 'p4' => '369', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -20046,21 +31438,21 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '363', + 'mlid' => '370', 'plid' => '0', - 'link_path' => 'book', - 'router_path' => 'book', - 'link_title' => 'Books', + 'link_path' => 'node/%/outline/remove', + 'router_path' => 'node/%/outline/remove', + 'link_title' => 'Remove from outline', 'options' => 'a:0:{}', 'module' => 'system', - 'hidden' => '1', + 'hidden' => '-1', 'external' => '0', 'has_children' => '0', 'expanded' => '0', 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '363', + 'p1' => '370', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20072,23 +31464,23 @@ 'updated' => '0', )) ->values(array( - 'menu_name' => 'navigation', - 'mlid' => '364', - 'plid' => '363', - 'link_path' => 'book/js/form', - 'router_path' => 'book/js/form', - 'link_title' => '', + 'menu_name' => 'secondary-links', + 'mlid' => '393', + 'plid' => '0', + 'link_path' => 'user/login', + 'router_path' => 'user/login', + 'link_title' => 'Test 3', 'options' => 'a:0:{}', - 'module' => 'system', - 'hidden' => '-1', + 'module' => 'menu', + 'hidden' => '0', 'external' => '0', - 'has_children' => '0', + 'has_children' => '1', 'expanded' => '0', - 'weight' => '0', - 'depth' => '2', - 'customized' => '0', - 'p1' => '363', - 'p2' => '364', + 'weight' => '-47', + 'depth' => '1', + 'customized' => '1', + 'p1' => '393', + 'p2' => '0', 'p3' => '0', 'p4' => '0', 'p5' => '0', @@ -20100,12 +31492,12 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '366', - 'plid' => '157', - 'link_path' => 'admin/content/book', - 'router_path' => 'admin/content/book', - 'link_title' => 'Books', - 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:33:\"Manage your site's book outlines.\";}}", + 'mlid' => '394', + 'plid' => '166', + 'link_path' => 'admin/build/path', + 'router_path' => 'admin/build/path', + 'link_title' => 'URL aliases', + 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:46:\"Change your site's URL paths by aliasing them.\";}}", 'module' => 'system', 'hidden' => '0', 'external' => '0', @@ -20115,8 +31507,8 @@ 'depth' => '3', 'customized' => '0', 'p1' => '144', - 'p2' => '157', - 'p3' => '366', + 'p2' => '166', + 'p3' => '394', 'p4' => '0', 'p5' => '0', 'p6' => '0', @@ -20127,11 +31519,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '367', - 'plid' => '363', - 'link_path' => 'book/export/%/%', - 'router_path' => 'book/export/%/%', - 'link_title' => '', + 'mlid' => '395', + 'plid' => '394', + 'link_path' => 'admin/build/path/delete', + 'router_path' => 'admin/build/path/delete', + 'link_title' => 'Delete alias', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20139,12 +31531,12 @@ 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '2', + 'depth' => '4', 'customized' => '0', - 'p1' => '363', - 'p2' => '367', - 'p3' => '0', - 'p4' => '0', + 'p1' => '144', + 'p2' => '166', + 'p3' => '394', + 'p4' => '395', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -20154,11 +31546,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '369', - 'plid' => '366', - 'link_path' => 'admin/content/book/%', - 'router_path' => 'admin/content/book/%', - 'link_title' => 'Re-order book pages and change titles', + 'mlid' => '396', + 'plid' => '394', + 'link_path' => 'admin/build/path/edit', + 'router_path' => 'admin/build/path/edit', + 'link_title' => 'Edit alias', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20169,9 +31561,9 @@ 'depth' => '4', 'customized' => '0', 'p1' => '144', - 'p2' => '157', - 'p3' => '366', - 'p4' => '369', + 'p2' => '166', + 'p3' => '394', + 'p4' => '396', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -20181,11 +31573,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '370', + 'mlid' => '397', 'plid' => '0', - 'link_path' => 'node/%/outline/remove', - 'router_path' => 'node/%/outline/remove', - 'link_title' => 'Remove from outline', + 'link_path' => 'system/files/imagecache', + 'router_path' => 'system/files/imagecache', + 'link_title' => '', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20195,7 +31587,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '370', + 'p1' => '397', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20208,23 +31600,23 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '372', - 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test/remove', - 'link_title' => 'Remove field', - 'options' => 'a:0:{}', + 'mlid' => '398', + 'plid' => '167', + 'link_path' => 'admin/settings/imageapi', + 'router_path' => 'admin/settings/imageapi', + 'link_title' => 'ImageAPI', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:19:"Configure ImageAPI.";}}', 'module' => 'system', - 'hidden' => '-1', + 'hidden' => '0', 'external' => '0', 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '1', + 'depth' => '3', 'customized' => '0', - 'p1' => '372', - 'p2' => '0', - 'p3' => '0', + 'p1' => '144', + 'p2' => '167', + 'p3' => '398', 'p4' => '0', 'p5' => '0', 'p6' => '0', @@ -20235,23 +31627,23 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '373', - 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_date/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_date/remove', - 'link_title' => 'Remove field', - 'options' => 'a:0:{}', + 'mlid' => '399', + 'plid' => '167', + 'link_path' => 'admin/settings/language', + 'router_path' => 'admin/settings/language', + 'link_title' => 'Languages', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:55:"Configure languages for content and the user interface.";}}', 'module' => 'system', - 'hidden' => '-1', + 'hidden' => '0', 'external' => '0', 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '1', + 'depth' => '3', 'customized' => '0', - 'p1' => '373', - 'p2' => '0', - 'p3' => '0', + 'p1' => '144', + 'p2' => '167', + 'p3' => '399', 'p4' => '0', 'p5' => '0', 'p6' => '0', @@ -20262,11 +31654,38 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '374', - 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_datestamp/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_datestamp/remove', - 'link_title' => 'Remove field', + 'mlid' => '400', + 'plid' => '166', + 'link_path' => 'admin/build/translate', + 'router_path' => 'admin/build/translate', + 'link_title' => 'Translate interface', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:59:"Translate the built in interface and optionally other text.";}}', + 'module' => 'system', + 'hidden' => '0', + 'external' => '0', + 'has_children' => '0', + 'expanded' => '0', + 'weight' => '0', + 'depth' => '3', + 'customized' => '0', + 'p1' => '144', + 'p2' => '166', + 'p3' => '400', + 'p4' => '0', + 'p5' => '0', + 'p6' => '0', + 'p7' => '0', + 'p8' => '0', + 'p9' => '0', + 'updated' => '0', +)) +->values(array( + 'menu_name' => 'navigation', + 'mlid' => '401', + 'plid' => '399', + 'link_path' => 'admin/settings/language/delete/%', + 'router_path' => 'admin/settings/language/delete/%', + 'link_title' => 'Confirm', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20274,12 +31693,12 @@ 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '1', + 'depth' => '4', 'customized' => '0', - 'p1' => '374', - 'p2' => '0', - 'p3' => '0', - 'p4' => '0', + 'p1' => '144', + 'p2' => '167', + 'p3' => '399', + 'p4' => '401', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -20289,11 +31708,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '375', - 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_datetime/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_datetime/remove', - 'link_title' => 'Remove field', + 'mlid' => '403', + 'plid' => '400', + 'link_path' => 'admin/build/translate/delete/%', + 'router_path' => 'admin/build/translate/delete/%', + 'link_title' => 'Delete string', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20301,12 +31720,12 @@ 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '1', + 'depth' => '4', 'customized' => '0', - 'p1' => '375', - 'p2' => '0', - 'p3' => '0', - 'p4' => '0', + 'p1' => '144', + 'p2' => '166', + 'p3' => '400', + 'p4' => '403', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -20316,11 +31735,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '376', - 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_decimal_radio_buttons/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_decimal_radio_buttons/remove', - 'link_title' => 'Remove field', + 'mlid' => '404', + 'plid' => '399', + 'link_path' => 'admin/settings/language/edit/%', + 'router_path' => 'admin/settings/language/edit/%', + 'link_title' => 'Edit language', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20328,12 +31747,12 @@ 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '1', + 'depth' => '4', 'customized' => '0', - 'p1' => '376', - 'p2' => '0', - 'p3' => '0', - 'p4' => '0', + 'p1' => '144', + 'p2' => '167', + 'p3' => '399', + 'p4' => '404', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -20343,11 +31762,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '377', - 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_email/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_email/remove', - 'link_title' => 'Remove field', + 'mlid' => '405', + 'plid' => '400', + 'link_path' => 'admin/build/translate/edit/%', + 'router_path' => 'admin/build/translate/edit/%', + 'link_title' => 'Edit string', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20355,12 +31774,12 @@ 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '1', + 'depth' => '4', 'customized' => '0', - 'p1' => '377', - 'p2' => '0', - 'p3' => '0', - 'p4' => '0', + 'p1' => '144', + 'p2' => '166', + 'p3' => '400', + 'p4' => '405', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -20370,11 +31789,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '378', + 'mlid' => '407', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_exclude_unset/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_exclude_unset/remove', - 'link_title' => 'Remove field', + 'link_path' => 'i18nstrings/save', + 'router_path' => 'i18nstrings/save', + 'link_title' => 'Save string', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20384,7 +31803,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '378', + 'p1' => '407', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20397,11 +31816,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '379', + 'mlid' => '408', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_filefield/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_filefield/remove', - 'link_title' => 'Remove field', + 'link_path' => 'i18n/node/autocomplete', + 'router_path' => 'i18n/node/autocomplete', + 'link_title' => 'Node title autocomplete', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20411,7 +31830,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '379', + 'p1' => '408', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20424,11 +31843,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '380', + 'mlid' => '411', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_float_single_checkbox/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_float_single_checkbox/remove', - 'link_title' => 'Remove field', + 'link_path' => 'core/modules/simpletest/files/imagecache', + 'router_path' => 'core/modules/simpletest/files/imagecache', + 'link_title' => '', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20438,7 +31857,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '380', + 'p1' => '411', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20451,10 +31870,10 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '381', + 'mlid' => '412', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_four/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_four/remove', + 'link_path' => 'admin/content/node-type/story/fields/field_test/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test/remove', 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', @@ -20465,7 +31884,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '381', + 'p1' => '412', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20478,10 +31897,10 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '382', + 'mlid' => '413', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_identical1/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_identical1/remove', + 'link_path' => 'admin/content/node-type/story/fields/field_test_date/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_date/remove', 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', @@ -20492,7 +31911,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '382', + 'p1' => '413', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20505,10 +31924,10 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '383', + 'mlid' => '414', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_identical2/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_identical2/remove', + 'link_path' => 'admin/content/node-type/story/fields/field_test_datestamp/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_datestamp/remove', 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', @@ -20519,7 +31938,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '383', + 'p1' => '414', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20532,10 +31951,10 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '384', + 'mlid' => '415', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_imagefield/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_imagefield/remove', + 'link_path' => 'admin/content/node-type/story/fields/field_test_datetime/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_datetime/remove', 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', @@ -20546,7 +31965,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '384', + 'p1' => '415', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20559,10 +31978,10 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '385', + 'mlid' => '416', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_integer_selectlist/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_integer_selectlist/remove', + 'link_path' => 'admin/content/node-type/story/fields/field_test_decimal_radio_buttons/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_decimal_radio_buttons/remove', 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', @@ -20573,7 +31992,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '385', + 'p1' => '416', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20586,10 +32005,10 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '386', + 'mlid' => '417', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_link/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_link/remove', + 'link_path' => 'admin/content/node-type/story/fields/field_test_email/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_email/remove', 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', @@ -20600,7 +32019,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '386', + 'p1' => '417', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20613,10 +32032,10 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '387', + 'mlid' => '418', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_phone/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_phone/remove', + 'link_path' => 'admin/content/node-type/story/fields/field_test_exclude_unset/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_exclude_unset/remove', 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', @@ -20627,7 +32046,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '387', + 'p1' => '418', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20640,10 +32059,10 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '388', + 'mlid' => '419', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_text_single_checkbox/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_text_single_checkbox/remove', + 'link_path' => 'admin/content/node-type/story/fields/field_test_filefield/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_filefield/remove', 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', @@ -20654,7 +32073,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '388', + 'p1' => '419', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20667,10 +32086,10 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '389', + 'mlid' => '420', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_three/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_three/remove', + 'link_path' => 'admin/content/node-type/story/fields/field_test_float_single_checkbox/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_float_single_checkbox/remove', 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', @@ -20681,7 +32100,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '389', + 'p1' => '420', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20694,10 +32113,10 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '390', + 'mlid' => '421', 'plid' => '0', - 'link_path' => 'admin/content/node-type/story/fields/field_test_two/remove', - 'router_path' => 'admin/content/node-type/story/fields/field_test_two/remove', + 'link_path' => 'admin/content/node-type/story/fields/field_test_four/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_four/remove', 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', @@ -20708,7 +32127,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '390', + 'p1' => '421', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20721,10 +32140,10 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '391', + 'mlid' => '422', 'plid' => '0', - 'link_path' => 'admin/content/node-type/test-page/fields/field_test/remove', - 'router_path' => 'admin/content/node-type/test-page/fields/field_test/remove', + 'link_path' => 'admin/content/node-type/story/fields/field_test_identical1/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_identical1/remove', 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', @@ -20735,7 +32154,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '391', + 'p1' => '422', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20748,10 +32167,10 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '392', + 'mlid' => '423', 'plid' => '0', - 'link_path' => 'admin/content/node-type/test-planet/fields/field_multivalue/remove', - 'router_path' => 'admin/content/node-type/test-planet/fields/field_multivalue/remove', + 'link_path' => 'admin/content/node-type/story/fields/field_test_identical2/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_identical2/remove', 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', @@ -20762,7 +32181,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '392', + 'p1' => '423', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20774,22 +32193,22 @@ 'updated' => '0', )) ->values(array( - 'menu_name' => 'secondary-links', - 'mlid' => '393', + 'menu_name' => 'navigation', + 'mlid' => '424', 'plid' => '0', - 'link_path' => 'user/login', - 'router_path' => 'user/login', - 'link_title' => 'Test 3', + 'link_path' => 'admin/content/node-type/story/fields/field_test_imagefield/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_imagefield/remove', + 'link_title' => 'Remove field', 'options' => 'a:0:{}', - 'module' => 'menu', - 'hidden' => '0', + 'module' => 'system', + 'hidden' => '-1', 'external' => '0', - 'has_children' => '1', + 'has_children' => '0', 'expanded' => '0', - 'weight' => '-47', + 'weight' => '0', 'depth' => '1', - 'customized' => '1', - 'p1' => '393', + 'customized' => '0', + 'p1' => '424', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20802,23 +32221,23 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '394', - 'plid' => '166', - 'link_path' => 'admin/build/path', - 'router_path' => 'admin/build/path', - 'link_title' => 'URL aliases', - 'options' => "a:1:{s:10:\"attributes\";a:1:{s:5:\"title\";s:46:\"Change your site's URL paths by aliasing them.\";}}", + 'mlid' => '425', + 'plid' => '0', + 'link_path' => 'admin/content/node-type/story/fields/field_test_integer_selectlist/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_integer_selectlist/remove', + 'link_title' => 'Remove field', + 'options' => 'a:0:{}', 'module' => 'system', - 'hidden' => '0', + 'hidden' => '-1', 'external' => '0', 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '3', + 'depth' => '1', 'customized' => '0', - 'p1' => '144', - 'p2' => '166', - 'p3' => '394', + 'p1' => '425', + 'p2' => '0', + 'p3' => '0', 'p4' => '0', 'p5' => '0', 'p6' => '0', @@ -20829,11 +32248,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '395', - 'plid' => '394', - 'link_path' => 'admin/build/path/delete', - 'router_path' => 'admin/build/path/delete', - 'link_title' => 'Delete alias', + 'mlid' => '426', + 'plid' => '0', + 'link_path' => 'admin/content/node-type/story/fields/field_test_link/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_link/remove', + 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20841,12 +32260,12 @@ 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '4', + 'depth' => '1', 'customized' => '0', - 'p1' => '144', - 'p2' => '166', - 'p3' => '394', - 'p4' => '395', + 'p1' => '426', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -20856,11 +32275,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '396', - 'plid' => '394', - 'link_path' => 'admin/build/path/edit', - 'router_path' => 'admin/build/path/edit', - 'link_title' => 'Edit alias', + 'mlid' => '427', + 'plid' => '0', + 'link_path' => 'admin/content/node-type/story/fields/field_test_phone/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_phone/remove', + 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20868,12 +32287,12 @@ 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '4', + 'depth' => '1', 'customized' => '0', - 'p1' => '144', - 'p2' => '166', - 'p3' => '394', - 'p4' => '396', + 'p1' => '427', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -20883,11 +32302,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '397', + 'mlid' => '428', 'plid' => '0', - 'link_path' => 'system/files/imagecache', - 'router_path' => 'system/files/imagecache', - 'link_title' => '', + 'link_path' => 'admin/content/node-type/story/fields/field_test_text_single_checkbox/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_text_single_checkbox/remove', + 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -20897,7 +32316,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '397', + 'p1' => '428', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -20910,23 +32329,23 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '398', - 'plid' => '167', - 'link_path' => 'admin/settings/imageapi', - 'router_path' => 'admin/settings/imageapi', - 'link_title' => 'ImageAPI', - 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:19:"Configure ImageAPI.";}}', + 'mlid' => '429', + 'plid' => '0', + 'link_path' => 'admin/content/node-type/story/fields/field_test_text_single_checkbox2/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_text_single_checkbox2/remove', + 'link_title' => 'Remove field', + 'options' => 'a:0:{}', 'module' => 'system', - 'hidden' => '0', + 'hidden' => '-1', 'external' => '0', 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '3', + 'depth' => '1', 'customized' => '0', - 'p1' => '144', - 'p2' => '167', - 'p3' => '398', + 'p1' => '429', + 'p2' => '0', + 'p3' => '0', 'p4' => '0', 'p5' => '0', 'p6' => '0', @@ -20937,23 +32356,23 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '399', - 'plid' => '167', - 'link_path' => 'admin/settings/language', - 'router_path' => 'admin/settings/language', - 'link_title' => 'Languages', - 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:55:"Configure languages for content and the user interface.";}}', + 'mlid' => '430', + 'plid' => '0', + 'link_path' => 'admin/content/node-type/story/fields/field_test_three/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_three/remove', + 'link_title' => 'Remove field', + 'options' => 'a:0:{}', 'module' => 'system', - 'hidden' => '0', + 'hidden' => '-1', 'external' => '0', 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '3', + 'depth' => '1', 'customized' => '0', - 'p1' => '144', - 'p2' => '167', - 'p3' => '399', + 'p1' => '430', + 'p2' => '0', + 'p3' => '0', 'p4' => '0', 'p5' => '0', 'p6' => '0', @@ -20964,23 +32383,23 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '400', - 'plid' => '166', - 'link_path' => 'admin/build/translate', - 'router_path' => 'admin/build/translate', - 'link_title' => 'Translate interface', - 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:59:"Translate the built in interface and optionally other text.";}}', + 'mlid' => '431', + 'plid' => '0', + 'link_path' => 'admin/content/node-type/story/fields/field_test_two/remove', + 'router_path' => 'admin/content/node-type/story/fields/field_test_two/remove', + 'link_title' => 'Remove field', + 'options' => 'a:0:{}', 'module' => 'system', - 'hidden' => '0', + 'hidden' => '-1', 'external' => '0', 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '3', + 'depth' => '1', 'customized' => '0', - 'p1' => '144', - 'p2' => '166', - 'p3' => '400', + 'p1' => '431', + 'p2' => '0', + 'p3' => '0', 'p4' => '0', 'p5' => '0', 'p6' => '0', @@ -20991,11 +32410,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '401', - 'plid' => '399', - 'link_path' => 'admin/settings/language/delete/%', - 'router_path' => 'admin/settings/language/delete/%', - 'link_title' => 'Confirm', + 'mlid' => '432', + 'plid' => '0', + 'link_path' => 'admin/content/node-type/test-page/fields/field_test/remove', + 'router_path' => 'admin/content/node-type/test-page/fields/field_test/remove', + 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -21003,12 +32422,12 @@ 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '4', + 'depth' => '1', 'customized' => '0', - 'p1' => '144', - 'p2' => '167', - 'p3' => '399', - 'p4' => '401', + 'p1' => '432', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -21018,11 +32437,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '402', + 'mlid' => '433', 'plid' => '0', - 'link_path' => 'core/modules/simpletest/files/imagecache', - 'router_path' => 'core/modules/simpletest/files/imagecache', - 'link_title' => '', + 'link_path' => 'admin/content/node-type/test-planet/fields/field_multivalue/remove', + 'router_path' => 'admin/content/node-type/test-planet/fields/field_multivalue/remove', + 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -21032,7 +32451,7 @@ 'weight' => '0', 'depth' => '1', 'customized' => '0', - 'p1' => '402', + 'p1' => '433', 'p2' => '0', 'p3' => '0', 'p4' => '0', @@ -21045,11 +32464,11 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '403', - 'plid' => '400', - 'link_path' => 'admin/build/translate/delete/%', - 'router_path' => 'admin/build/translate/delete/%', - 'link_title' => 'Delete string', + 'mlid' => '434', + 'plid' => '0', + 'link_path' => 'admin/content/node-type/test-planet/fields/field_test_text_single_checkbox/remove', + 'router_path' => 'admin/content/node-type/test-planet/fields/field_test_text_single_checkbox/remove', + 'link_title' => 'Remove field', 'options' => 'a:0:{}', 'module' => 'system', 'hidden' => '-1', @@ -21057,12 +32476,12 @@ 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '4', + 'depth' => '1', 'customized' => '0', - 'p1' => '144', - 'p2' => '166', - 'p3' => '400', - 'p4' => '403', + 'p1' => '434', + 'p2' => '0', + 'p3' => '0', + 'p4' => '0', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -21072,24 +32491,24 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '404', - 'plid' => '399', - 'link_path' => 'admin/settings/language/edit/%', - 'router_path' => 'admin/settings/language/edit/%', - 'link_title' => 'Edit language', - 'options' => 'a:0:{}', + 'mlid' => '438', + 'plid' => '167', + 'link_path' => 'admin/settings/variable', + 'router_path' => 'admin/settings/variable', + 'link_title' => 'Variables', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:31:"Edit and delete site variables.";}}', 'module' => 'system', - 'hidden' => '-1', + 'hidden' => '0', 'external' => '0', - 'has_children' => '0', + 'has_children' => '1', 'expanded' => '0', 'weight' => '0', - 'depth' => '4', + 'depth' => '3', 'customized' => '0', 'p1' => '144', 'p2' => '167', - 'p3' => '399', - 'p4' => '404', + 'p3' => '438', + 'p4' => '0', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -21099,14 +32518,14 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '405', - 'plid' => '400', - 'link_path' => 'admin/build/translate/edit/%', - 'router_path' => 'admin/build/translate/edit/%', - 'link_title' => 'Edit string', + 'mlid' => '439', + 'plid' => '438', + 'link_path' => 'admin/settings/variable/edit/%', + 'router_path' => 'admin/settings/variable/edit/%', + 'link_title' => 'Edit variable', 'options' => 'a:0:{}', 'module' => 'system', - 'hidden' => '-1', + 'hidden' => '0', 'external' => '0', 'has_children' => '0', 'expanded' => '0', @@ -21114,9 +32533,9 @@ 'depth' => '4', 'customized' => '0', 'p1' => '144', - 'p2' => '166', - 'p3' => '400', - 'p4' => '405', + 'p2' => '167', + 'p3' => '438', + 'p4' => '439', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -21126,24 +32545,24 @@ )) ->values(array( 'menu_name' => 'navigation', - 'mlid' => '406', - 'plid' => '0', - 'link_path' => 'admin/content/node-type/test-planet/fields/field_test_text_single_checkbox/remove', - 'router_path' => 'admin/content/node-type/test-planet/fields/field_test_text_single_checkbox/remove', - 'link_title' => 'Remove field', - 'options' => 'a:0:{}', + 'mlid' => '440', + 'plid' => '438', + 'link_path' => 'admin/settings/variable/group/%', + 'router_path' => 'admin/settings/variable/group/%', + 'link_title' => 'Variables group', + 'options' => 'a:1:{s:10:"attributes";a:1:{s:5:"title";s:32:"Edit and delete group variables.";}}', 'module' => 'system', - 'hidden' => '-1', + 'hidden' => '0', 'external' => '0', 'has_children' => '0', 'expanded' => '0', 'weight' => '0', - 'depth' => '1', + 'depth' => '4', 'customized' => '0', - 'p1' => '406', - 'p2' => '0', - 'p3' => '0', - 'p4' => '0', + 'p1' => '144', + 'p2' => '167', + 'p3' => '438', + 'p4' => '440', 'p5' => '0', 'p6' => '0', 'p7' => '0', @@ -21458,7 +32877,7 @@ 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => '_block_themes_access', - 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:33:"themes/bluemarine/bluemarine.info";s:4:"name";s:10:"bluemarine";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:13:{s:4:"name";s:10:"Bluemarine";s:11:"description";s:66:"Table-based multi-column theme with a marine and ash color scheme.";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/bluemarine/script.js";}s:10:"screenshot";s:32:"themes/bluemarine/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:6:"engine";s:11:"phptemplate";}}', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:33:"themes/bluemarine/bluemarine.info";s:4:"name";s:10:"bluemarine";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:10:"Bluemarine";s:11:"description";s:66:"Table-based multi-column theme with a marine and ash color scheme.";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/bluemarine/script.js";}s:10:"screenshot";s:32:"themes/bluemarine/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:6:"engine";s:11:"phptemplate";}}', 'page_callback' => 'block_admin_display', 'page_arguments' => 'a:1:{i:0;s:10:"bluemarine";}', 'fit' => '31', @@ -21480,7 +32899,7 @@ 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => '_block_themes_access', - 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":11:{s:8:"filename";s:31:"themes/chameleon/chameleon.info";s:4:"name";s:9:"chameleon";s:4:"type";s:5:"theme";s:5:"owner";s:32:"themes/chameleon/chameleon.theme";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:12:{s:4:"name";s:9:"Chameleon";s:11:"description";s:42:"Minimalist tabled theme with light colors.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:8:"features";a:4:{i:0;s:4:"logo";i:1;s:7:"favicon";i:2;s:4:"name";i:3;s:6:"slogan";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"scripts";a:1:{s:9:"script.js";s:26:"themes/chameleon/script.js";}s:10:"screenshot";s:31:"themes/chameleon/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}}}', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":11:{s:8:"filename";s:31:"themes/chameleon/chameleon.info";s:4:"name";s:9:"chameleon";s:4:"type";s:5:"theme";s:5:"owner";s:32:"themes/chameleon/chameleon.theme";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:10:{s:4:"name";s:9:"Chameleon";s:11:"description";s:42:"Minimalist tabled theme with light colors.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:8:"features";a:4:{i:0;s:4:"logo";i:1;s:7:"favicon";i:2;s:4:"name";i:3;s:6:"slogan";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:7:"scripts";a:1:{s:9:"script.js";s:26:"themes/chameleon/script.js";}s:10:"screenshot";s:31:"themes/chameleon/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}}}', 'page_callback' => 'block_admin_display', 'page_arguments' => 'a:1:{i:0;s:9:"chameleon";}', 'fit' => '31', @@ -21502,7 +32921,7 @@ 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => '_block_themes_access', - 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:27:"themes/garland/garland.info";s:4:"name";s:7:"garland";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"1";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:13:{s:4:"name";s:7:"Garland";s:11:"description";s:66:"Tableless, recolorable, multi-column, fluid width theme (default).";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:24:"themes/garland/script.js";}s:10:"screenshot";s:29:"themes/garland/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:6:"engine";s:11:"phptemplate";}}', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:27:"themes/garland/garland.info";s:4:"name";s:7:"garland";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"1";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:7:"Garland";s:11:"description";s:66:"Tableless, recolorable, multi-column, fluid width theme (default).";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:24:"themes/garland/script.js";}s:10:"screenshot";s:29:"themes/garland/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:6:"engine";s:11:"phptemplate";}}', 'page_callback' => 'block_admin_display', 'page_arguments' => 'a:1:{i:0;s:7:"garland";}', 'fit' => '31', @@ -21546,7 +32965,7 @@ 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => '_block_themes_access', - 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:35:"themes/chameleon/marvin/marvin.info";s:4:"name";s:6:"marvin";s:4:"type";s:5:"theme";s:5:"owner";s:0:"";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:13:{s:4:"name";s:6:"Marvin";s:11:"description";s:31:"Boxy tabled theme in all grays.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:10:"base theme";s:9:"chameleon";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/chameleon/marvin/script.js";}s:10:"screenshot";s:38:"themes/chameleon/marvin/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:10:"base_theme";s:9:"chameleon";}}', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:35:"themes/chameleon/marvin/marvin.info";s:4:"name";s:6:"marvin";s:4:"type";s:5:"theme";s:5:"owner";s:0:"";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:6:"Marvin";s:11:"description";s:31:"Boxy tabled theme in all grays.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:10:"base theme";s:9:"chameleon";s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/chameleon/marvin/script.js";}s:10:"screenshot";s:38:"themes/chameleon/marvin/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:10:"base_theme";s:9:"chameleon";}}', 'page_callback' => 'block_admin_display', 'page_arguments' => 'a:1:{i:0;s:6:"marvin";}', 'fit' => '31', @@ -21568,7 +32987,7 @@ 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => '_block_themes_access', - 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":13:{s:8:"filename";s:37:"themes/garland/minnelli/minnelli.info";s:4:"name";s:8:"minnelli";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:14:{s:4:"name";s:8:"Minnelli";s:11:"description";s:56:"Tableless, recolorable, multi-column, fixed width theme.";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:10:"base theme";s:7:"garland";s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/garland/minnelli/script.js";}s:10:"screenshot";s:38:"themes/garland/minnelli/screenshot.png";s:3:"php";s:5:"4.3.5";s:6:"engine";s:11:"phptemplate";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:6:"engine";s:11:"phptemplate";s:10:"base_theme";s:7:"garland";}}', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":13:{s:8:"filename";s:37:"themes/garland/minnelli/minnelli.info";s:4:"name";s:8:"minnelli";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:12:{s:4:"name";s:8:"Minnelli";s:11:"description";s:56:"Tableless, recolorable, multi-column, fixed width theme.";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:10:"base theme";s:7:"garland";s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/garland/minnelli/script.js";}s:10:"screenshot";s:38:"themes/garland/minnelli/screenshot.png";s:3:"php";s:5:"4.3.5";s:6:"engine";s:11:"phptemplate";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:6:"engine";s:11:"phptemplate";s:10:"base_theme";s:7:"garland";}}', 'page_callback' => 'block_admin_display', 'page_arguments' => 'a:1:{i:0;s:8:"minnelli";}', 'fit' => '31', @@ -21590,7 +33009,7 @@ 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => '_block_themes_access', - 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:33:"themes/pushbutton/pushbutton.info";s:4:"name";s:10:"pushbutton";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:13:{s:4:"name";s:10:"Pushbutton";s:11:"description";s:52:"Tabled, multi-column theme in blue and orange tones.";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/pushbutton/script.js";}s:10:"screenshot";s:32:"themes/pushbutton/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:6:"engine";s:11:"phptemplate";}}', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:33:"themes/pushbutton/pushbutton.info";s:4:"name";s:10:"pushbutton";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:10:"Pushbutton";s:11:"description";s:52:"Tabled, multi-column theme in blue and orange tones.";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/pushbutton/script.js";}s:10:"screenshot";s:32:"themes/pushbutton/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:6:"engine";s:11:"phptemplate";}}', 'page_callback' => 'block_admin_display', 'page_arguments' => 'a:1:{i:0;s:10:"pushbutton";}', 'fit' => '31', @@ -22294,7 +33713,7 @@ 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => '_system_themes_access', - 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:33:"themes/bluemarine/bluemarine.info";s:4:"name";s:10:"bluemarine";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:13:{s:4:"name";s:10:"Bluemarine";s:11:"description";s:66:"Table-based multi-column theme with a marine and ash color scheme.";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/bluemarine/script.js";}s:10:"screenshot";s:32:"themes/bluemarine/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:6:"engine";s:11:"phptemplate";}}', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:33:"themes/bluemarine/bluemarine.info";s:4:"name";s:10:"bluemarine";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:10:"Bluemarine";s:11:"description";s:66:"Table-based multi-column theme with a marine and ash color scheme.";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/bluemarine/script.js";}s:10:"screenshot";s:32:"themes/bluemarine/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:6:"engine";s:11:"phptemplate";}}', 'page_callback' => 'drupal_get_form', 'page_arguments' => 'a:2:{i:0;s:21:"system_theme_settings";i:1;s:10:"bluemarine";}', 'fit' => '31', @@ -22316,7 +33735,7 @@ 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => '_system_themes_access', - 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":11:{s:8:"filename";s:31:"themes/chameleon/chameleon.info";s:4:"name";s:9:"chameleon";s:4:"type";s:5:"theme";s:5:"owner";s:32:"themes/chameleon/chameleon.theme";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:12:{s:4:"name";s:9:"Chameleon";s:11:"description";s:42:"Minimalist tabled theme with light colors.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:8:"features";a:4:{i:0;s:4:"logo";i:1;s:7:"favicon";i:2;s:4:"name";i:3;s:6:"slogan";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"scripts";a:1:{s:9:"script.js";s:26:"themes/chameleon/script.js";}s:10:"screenshot";s:31:"themes/chameleon/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}}}', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":11:{s:8:"filename";s:31:"themes/chameleon/chameleon.info";s:4:"name";s:9:"chameleon";s:4:"type";s:5:"theme";s:5:"owner";s:32:"themes/chameleon/chameleon.theme";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:10:{s:4:"name";s:9:"Chameleon";s:11:"description";s:42:"Minimalist tabled theme with light colors.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:8:"features";a:4:{i:0;s:4:"logo";i:1;s:7:"favicon";i:2;s:4:"name";i:3;s:6:"slogan";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:7:"scripts";a:1:{s:9:"script.js";s:26:"themes/chameleon/script.js";}s:10:"screenshot";s:31:"themes/chameleon/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}}}', 'page_callback' => 'drupal_get_form', 'page_arguments' => 'a:2:{i:0;s:21:"system_theme_settings";i:1;s:9:"chameleon";}', 'fit' => '31', @@ -22338,7 +33757,7 @@ 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => '_system_themes_access', - 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:27:"themes/garland/garland.info";s:4:"name";s:7:"garland";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"1";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:13:{s:4:"name";s:7:"Garland";s:11:"description";s:66:"Tableless, recolorable, multi-column, fluid width theme (default).";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:24:"themes/garland/script.js";}s:10:"screenshot";s:29:"themes/garland/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:6:"engine";s:11:"phptemplate";}}', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:27:"themes/garland/garland.info";s:4:"name";s:7:"garland";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"1";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:7:"Garland";s:11:"description";s:66:"Tableless, recolorable, multi-column, fluid width theme (default).";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:24:"themes/garland/script.js";}s:10:"screenshot";s:29:"themes/garland/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:6:"engine";s:11:"phptemplate";}}', 'page_callback' => 'drupal_get_form', 'page_arguments' => 'a:2:{i:0;s:21:"system_theme_settings";i:1;s:7:"garland";}', 'fit' => '31', @@ -22382,7 +33801,7 @@ 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => '_system_themes_access', - 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:35:"themes/chameleon/marvin/marvin.info";s:4:"name";s:6:"marvin";s:4:"type";s:5:"theme";s:5:"owner";s:0:"";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:13:{s:4:"name";s:6:"Marvin";s:11:"description";s:31:"Boxy tabled theme in all grays.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:10:"base theme";s:9:"chameleon";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/chameleon/marvin/script.js";}s:10:"screenshot";s:38:"themes/chameleon/marvin/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:10:"base_theme";s:9:"chameleon";}}', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:35:"themes/chameleon/marvin/marvin.info";s:4:"name";s:6:"marvin";s:4:"type";s:5:"theme";s:5:"owner";s:0:"";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:6:"Marvin";s:11:"description";s:31:"Boxy tabled theme in all grays.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:10:"base theme";s:9:"chameleon";s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/chameleon/marvin/script.js";}s:10:"screenshot";s:38:"themes/chameleon/marvin/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:10:"base_theme";s:9:"chameleon";}}', 'page_callback' => 'drupal_get_form', 'page_arguments' => 'a:2:{i:0;s:21:"system_theme_settings";i:1;s:6:"marvin";}', 'fit' => '31', @@ -22404,7 +33823,7 @@ 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => '_system_themes_access', - 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":13:{s:8:"filename";s:37:"themes/garland/minnelli/minnelli.info";s:4:"name";s:8:"minnelli";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:14:{s:4:"name";s:8:"Minnelli";s:11:"description";s:56:"Tableless, recolorable, multi-column, fixed width theme.";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:10:"base theme";s:7:"garland";s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/garland/minnelli/script.js";}s:10:"screenshot";s:38:"themes/garland/minnelli/screenshot.png";s:3:"php";s:5:"4.3.5";s:6:"engine";s:11:"phptemplate";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:6:"engine";s:11:"phptemplate";s:10:"base_theme";s:7:"garland";}}', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":13:{s:8:"filename";s:37:"themes/garland/minnelli/minnelli.info";s:4:"name";s:8:"minnelli";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:12:{s:4:"name";s:8:"Minnelli";s:11:"description";s:56:"Tableless, recolorable, multi-column, fixed width theme.";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:10:"base theme";s:7:"garland";s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/garland/minnelli/script.js";}s:10:"screenshot";s:38:"themes/garland/minnelli/screenshot.png";s:3:"php";s:5:"4.3.5";s:6:"engine";s:11:"phptemplate";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:6:"engine";s:11:"phptemplate";s:10:"base_theme";s:7:"garland";}}', 'page_callback' => 'drupal_get_form', 'page_arguments' => 'a:2:{i:0;s:21:"system_theme_settings";i:1;s:8:"minnelli";}', 'fit' => '31', @@ -22426,7 +33845,7 @@ 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => '_system_themes_access', - 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:33:"themes/pushbutton/pushbutton.info";s:4:"name";s:10:"pushbutton";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:13:{s:4:"name";s:10:"Pushbutton";s:11:"description";s:52:"Tabled, multi-column theme in blue and orange tones.";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/pushbutton/script.js";}s:10:"screenshot";s:32:"themes/pushbutton/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:6:"engine";s:11:"phptemplate";}}', + 'access_arguments' => 'a:1:{i:0;O:8:"stdClass":12:{s:8:"filename";s:33:"themes/pushbutton/pushbutton.info";s:4:"name";s:10:"pushbutton";s:4:"type";s:5:"theme";s:5:"owner";s:45:"themes/engines/phptemplate/phptemplate.engine";s:6:"status";s:1:"0";s:8:"throttle";s:1:"0";s:9:"bootstrap";s:1:"0";s:14:"schema_version";s:2:"-1";s:6:"weight";s:1:"0";s:4:"info";a:11:{s:4:"name";s:10:"Pushbutton";s:11:"description";s:52:"Tabled, multi-column theme in blue and orange tones.";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/pushbutton/script.js";}s:10:"screenshot";s:32:"themes/pushbutton/screenshot.png";s:3:"php";s:5:"4.3.5";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:6:"engine";s:11:"phptemplate";}}', 'page_callback' => 'drupal_get_form', 'page_arguments' => 'a:2:{i:0;s:21:"system_theme_settings";i:1;s:10:"pushbutton";}', 'fit' => '31', @@ -22576,6 +33995,28 @@ 'file' => '', )) ->values(array( + 'path' => 'admin/build/translate/refresh', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:19:"translate interface";}', + 'page_callback' => 'i18nstrings_admin_refresh_page', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/build/translate', + 'tab_root' => 'admin/build/translate', + 'title' => 'Refresh', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '20', + 'file' => 'sites/all/modules/i18n/i18nstrings/i18nstrings.admin.inc', +)) +->values(array( 'path' => 'admin/build/translate/search', 'load_functions' => '', 'to_arg_functions' => '', @@ -24870,7 +36311,7 @@ 'access_callback' => 'user_access', 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', 'page_callback' => 'drupal_get_form', - 'page_arguments' => 'a:2:{i:0;s:14:"node_type_form";i:1;O:8:"stdClass":14:{s:4:"type";s:10:"test_event";s:4:"name";s:18:"Migrate test event";s:6:"module";s:4:"node";s:11:"description";s:27:"test event description here";s:4:"help";s:14:"help text here";s:9:"has_title";s:1:"1";s:11:"title_label";s:10:"Event Name";s:8:"has_body";s:1:"1";s:10:"body_label";s:4:"Body";s:14:"min_word_count";s:1:"0";s:6:"custom";s:1:"1";s:8:"modified";s:1:"1";s:6:"locked";s:1:"0";s:9:"orig_type";s:5:"event";}}', + 'page_arguments' => 'a:2:{i:0;s:14:"node_type_form";i:1;O:8:"stdClass":14:{s:4:"type";s:10:"test_event";s:4:"name";s:18:"Migrate test event";s:6:"module";s:4:"node";s:11:"description";s:27:"test event description here";s:4:"help";s:0:"";s:9:"has_title";s:1:"1";s:11:"title_label";s:10:"Event Name";s:8:"has_body";s:1:"1";s:10:"body_label";s:4:"Body";s:14:"min_word_count";s:1:"0";s:6:"custom";s:1:"1";s:8:"modified";s:1:"1";s:6:"locked";s:1:"0";s:9:"orig_type";s:5:"event";}}', 'fit' => '15', 'number_parts' => '4', 'tab_parent' => '', @@ -24892,7 +36333,7 @@ 'access_callback' => 'user_access', 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', 'page_callback' => 'drupal_get_form', - 'page_arguments' => 'a:2:{i:0;s:24:"node_type_delete_confirm";i:1;O:8:"stdClass":14:{s:4:"type";s:10:"test_event";s:4:"name";s:18:"Migrate test event";s:6:"module";s:4:"node";s:11:"description";s:27:"test event description here";s:4:"help";s:14:"help text here";s:9:"has_title";s:1:"1";s:11:"title_label";s:10:"Event Name";s:8:"has_body";s:1:"1";s:10:"body_label";s:4:"Body";s:14:"min_word_count";s:1:"0";s:6:"custom";s:1:"1";s:8:"modified";s:1:"1";s:6:"locked";s:1:"0";s:9:"orig_type";s:5:"event";}}', + 'page_arguments' => 'a:2:{i:0;s:24:"node_type_delete_confirm";i:1;O:8:"stdClass":14:{s:4:"type";s:10:"test_event";s:4:"name";s:18:"Migrate test event";s:6:"module";s:4:"node";s:11:"description";s:27:"test event description here";s:4:"help";s:0:"";s:9:"has_title";s:1:"1";s:11:"title_label";s:10:"Event Name";s:8:"has_body";s:1:"1";s:10:"body_label";s:4:"Body";s:14:"min_word_count";s:1:"0";s:6:"custom";s:1:"1";s:8:"modified";s:1:"1";s:6:"locked";s:1:"0";s:9:"orig_type";s:5:"event";}}', 'fit' => '31', 'number_parts' => '5', 'tab_parent' => '', @@ -25002,7 +36443,7 @@ 'access_callback' => 'user_access', 'access_arguments' => 'a:1:{i:0;s:24:"administer content types";}', 'page_callback' => 'drupal_get_form', - 'page_arguments' => 'a:2:{i:0;s:14:"node_type_form";i:1;O:8:"stdClass":14:{s:4:"type";s:10:"test_event";s:4:"name";s:18:"Migrate test event";s:6:"module";s:4:"node";s:11:"description";s:27:"test event description here";s:4:"help";s:14:"help text here";s:9:"has_title";s:1:"1";s:11:"title_label";s:10:"Event Name";s:8:"has_body";s:1:"1";s:10:"body_label";s:4:"Body";s:14:"min_word_count";s:1:"0";s:6:"custom";s:1:"1";s:8:"modified";s:1:"1";s:6:"locked";s:1:"0";s:9:"orig_type";s:5:"event";}}', + 'page_arguments' => 'a:2:{i:0;s:14:"node_type_form";i:1;O:8:"stdClass":14:{s:4:"type";s:10:"test_event";s:4:"name";s:18:"Migrate test event";s:6:"module";s:4:"node";s:11:"description";s:27:"test event description here";s:4:"help";s:0:"";s:9:"has_title";s:1:"1";s:11:"title_label";s:10:"Event Name";s:8:"has_body";s:1:"1";s:10:"body_label";s:4:"Body";s:14:"min_word_count";s:1:"0";s:6:"custom";s:1:"1";s:8:"modified";s:1:"1";s:6:"locked";s:1:"0";s:9:"orig_type";s:5:"event";}}', 'fit' => '31', 'number_parts' => '5', 'tab_parent' => 'admin/content/node-type/test-event', @@ -25832,6 +37273,28 @@ 'file' => 'modules/taxonomy/taxonomy.admin.inc', )) ->values(array( + 'path' => 'admin/content/taxonomy/%/translation', + 'load_functions' => 'a:1:{i:3;s:24:"taxonomy_vocabulary_load";}', + 'to_arg_functions' => '', + 'access_callback' => '_i18ntaxonomy_translation_tab', + 'access_arguments' => 'a:1:{i:0;i:3;}', + 'page_callback' => 'i18ntaxonomy_page_vocabulary', + 'page_arguments' => 'a:3:{i:0;i:3;i:1;i:5;i:2;i:6;}', + 'fit' => '29', + 'number_parts' => '5', + 'tab_parent' => 'admin/content/taxonomy/%', + 'tab_root' => 'admin/content/taxonomy/%', + 'title' => 'Translation', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.admin.inc', +)) +->values(array( 'path' => 'admin/content/taxonomy/add/vocabulary', 'load_functions' => '', 'to_arg_functions' => '', @@ -26954,6 +38417,50 @@ 'file' => '', )) ->values(array( + 'path' => 'admin/settings/language/configure/language', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:20:"administer languages";}', + 'page_callback' => 'locale_inc_callback', + 'page_arguments' => 'a:2:{i:0;s:15:"drupal_get_form";i:1;s:31:"locale_languages_configure_form";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/settings/language/configure', + 'tab_root' => 'admin/settings/language', + 'title' => 'Language negotiation', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '-10', + 'file' => '', +)) +->values(array( + 'path' => 'admin/settings/language/configure/strings', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:18:"administer filters";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:26:"i18nstrings_admin_settings";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/settings/language/configure', + 'tab_root' => 'admin/settings/language', + 'title' => 'String translation', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '20', + 'file' => 'sites/all/modules/i18n/i18nstrings/i18nstrings.admin.inc', +)) +->values(array( 'path' => 'admin/settings/language/delete/%', 'load_functions' => 'a:1:{i:4;N;}', 'to_arg_functions' => '', @@ -26998,6 +38505,72 @@ 'file' => '', )) ->values(array( + 'path' => 'admin/settings/language/i18n', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:19:"i18n_admin_settings";}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/settings/language', + 'tab_root' => 'admin/settings/language', + 'title' => 'Multilingual system', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => 'Configure extended options for multilingual content and translations.', + 'position' => '', + 'weight' => '10', + 'file' => 'sites/all/modules/i18n/i18n.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/language/i18n/configure', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:19:"i18n_admin_settings";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/settings/language/i18n', + 'tab_root' => 'admin/settings/language', + 'title' => 'Options', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', + 'block_callback' => '', + 'description' => 'Configure extended options for multilingual content and translations.', + 'position' => '', + 'weight' => '0', + 'file' => 'sites/all/modules/i18n/i18n.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/language/i18n/variables', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:25:"i18n_admin_variables_form";}', + 'fit' => '31', + 'number_parts' => '5', + 'tab_parent' => 'admin/settings/language/i18n', + 'tab_root' => 'admin/settings/language', + 'title' => 'Variables', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '128', + 'block_callback' => '', + 'description' => 'Multilingual variables.', + 'position' => '', + 'weight' => '0', + 'file' => 'sites/all/modules/i18n/i18n.admin.inc', +)) +->values(array( 'path' => 'admin/settings/language/overview', 'load_functions' => '', 'to_arg_functions' => '', @@ -27056,78 +38629,210 @@ 'title' => 'Performance', 'title_callback' => 't', 'title_arguments' => '', - 'type' => '6', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/site-information', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:32:"system_site_information_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/site-information', + 'title' => 'Site information', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Change basic site information, such as the site name, slogan, e-mail address, mission, front page and more.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/site-maintenance', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:32:"system_site_maintenance_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/site-maintenance', + 'title' => 'Site maintenance', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Take the site off-line for maintenance or bring it back online.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/system/system.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/uploads', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'drupal_get_form', + 'page_arguments' => 'a:1:{i:0;s:21:"upload_admin_settings";}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/uploads', + 'title' => 'File uploads', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Control how files may be attached to content.', + 'position' => '', + 'weight' => '0', + 'file' => 'modules/upload/upload.admin.inc', +)) +->values(array( + 'path' => 'admin/settings/variable', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'variable_admin_page_overview', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/variable', + 'title' => 'Variables', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => 'Edit and delete site variables.', + 'position' => '', + 'weight' => '0', + 'file' => 'sites/all/modules/variable/variable_admin/variable_admin.pages.inc', +)) +->values(array( + 'path' => 'admin/settings/variable/edit/%', + 'load_functions' => 'a:1:{i:4;N;}', + 'to_arg_functions' => '', + 'access_callback' => 'variable_access', + 'access_arguments' => 'a:1:{i:0;i:4;}', + 'page_callback' => 'variable_admin_page_edit', + 'page_arguments' => 'a:1:{i:0;i:4;}', + 'fit' => '30', + 'number_parts' => '5', + 'tab_parent' => '', + 'tab_root' => 'admin/settings/variable/edit/%', + 'title' => 'Edit variable', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '6', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'sites/all/modules/variable/variable_admin/variable_admin.pages.inc', +)) +->values(array( + 'path' => 'admin/settings/variable/group', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', + 'page_callback' => 'variable_admin_page_overview', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/settings/variable', + 'tab_root' => 'admin/settings/variable', + 'title' => 'By group', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '136', 'block_callback' => '', - 'description' => 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.', + 'description' => 'Variables by group.', 'position' => '', 'weight' => '0', - 'file' => 'modules/system/system.admin.inc', + 'file' => 'sites/all/modules/variable/variable_admin/variable_admin.pages.inc', )) ->values(array( - 'path' => 'admin/settings/site-information', - 'load_functions' => '', + 'path' => 'admin/settings/variable/group/%', + 'load_functions' => 'a:1:{i:4;N;}', 'to_arg_functions' => '', 'access_callback' => 'user_access', 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', - 'page_callback' => 'drupal_get_form', - 'page_arguments' => 'a:1:{i:0;s:32:"system_site_information_settings";}', - 'fit' => '7', - 'number_parts' => '3', + 'page_callback' => 'variable_admin_page_group', + 'page_arguments' => 'a:1:{i:0;i:4;}', + 'fit' => '30', + 'number_parts' => '5', 'tab_parent' => '', - 'tab_root' => 'admin/settings/site-information', - 'title' => 'Site information', + 'tab_root' => 'admin/settings/variable/group/%', + 'title' => 'Variables group', 'title_callback' => 't', 'title_arguments' => '', 'type' => '6', 'block_callback' => '', - 'description' => 'Change basic site information, such as the site name, slogan, e-mail address, mission, front page and more.', + 'description' => 'Edit and delete group variables.', 'position' => '', 'weight' => '0', - 'file' => 'modules/system/system.admin.inc', + 'file' => 'sites/all/modules/variable/variable_admin/variable_admin.pages.inc', )) ->values(array( - 'path' => 'admin/settings/site-maintenance', + 'path' => 'admin/settings/variable/modules', 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => 'user_access', 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', - 'page_callback' => 'drupal_get_form', - 'page_arguments' => 'a:1:{i:0;s:32:"system_site_maintenance_settings";}', - 'fit' => '7', - 'number_parts' => '3', - 'tab_parent' => '', - 'tab_root' => 'admin/settings/site-maintenance', - 'title' => 'Site maintenance', + 'page_callback' => 'variable_admin_page_modules', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/settings/variable', + 'tab_root' => 'admin/settings/variable', + 'title' => 'By module', 'title_callback' => 't', 'title_arguments' => '', - 'type' => '6', + 'type' => '128', 'block_callback' => '', - 'description' => 'Take the site off-line for maintenance or bring it back online.', + 'description' => 'Variables by module.', 'position' => '', 'weight' => '0', - 'file' => 'modules/system/system.admin.inc', + 'file' => 'sites/all/modules/variable/variable_admin/variable_admin.pages.inc', )) ->values(array( - 'path' => 'admin/settings/uploads', + 'path' => 'admin/settings/variable/undefined', 'load_functions' => '', 'to_arg_functions' => '', 'access_callback' => 'user_access', 'access_arguments' => 'a:1:{i:0;s:29:"administer site configuration";}', - 'page_callback' => 'drupal_get_form', - 'page_arguments' => 'a:1:{i:0;s:21:"upload_admin_settings";}', - 'fit' => '7', - 'number_parts' => '3', - 'tab_parent' => '', - 'tab_root' => 'admin/settings/uploads', - 'title' => 'File uploads', + 'page_callback' => 'variable_admin_page_undefined', + 'page_arguments' => 'a:0:{}', + 'fit' => '15', + 'number_parts' => '4', + 'tab_parent' => 'admin/settings/variable', + 'tab_root' => 'admin/settings/variable', + 'title' => 'Undefined', 'title_callback' => 't', 'title_arguments' => '', - 'type' => '6', + 'type' => '128', 'block_callback' => '', - 'description' => 'Control how files may be attached to content.', + 'description' => 'Unknown variables.', 'position' => '', 'weight' => '0', - 'file' => 'modules/upload/upload.admin.inc', + 'file' => 'sites/all/modules/variable/variable_admin/variable_admin.pages.inc', )) ->values(array( 'path' => 'admin/user', @@ -28274,6 +39979,50 @@ 'file' => 'modules/filter/filter.pages.inc', )) ->values(array( + 'path' => 'i18n/node/autocomplete', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:14:"access content";}', + 'page_callback' => 'i18n_node_autocomplete', + 'page_arguments' => 'a:0:{}', + 'fit' => '7', + 'number_parts' => '3', + 'tab_parent' => '', + 'tab_root' => 'i18n/node/autocomplete', + 'title' => 'Node title autocomplete', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => 'sites/all/modules/i18n/i18n.pages.inc', +)) +->values(array( + 'path' => 'i18nstrings/save', + 'load_functions' => '', + 'to_arg_functions' => '', + 'access_callback' => 'user_access', + 'access_arguments' => 'a:1:{i:0;s:23:"use on-page translation";}', + 'page_callback' => 'i18nstrings_save_string', + 'page_arguments' => 'a:0:{}', + 'fit' => '3', + 'number_parts' => '2', + 'tab_parent' => '', + 'tab_root' => 'i18nstrings/save', + 'title' => 'Save string', + 'title_callback' => 't', + 'title_arguments' => '', + 'type' => '4', + 'block_callback' => '', + 'description' => '', + 'position' => '', + 'weight' => '0', + 'file' => '', +)) +->values(array( 'path' => 'logout', 'load_functions' => '', 'to_arg_functions' => '', @@ -28543,7 +40292,7 @@ 'to_arg_functions' => '', 'access_callback' => '_translation_tab_access', 'access_arguments' => 'a:1:{i:0;i:1;}', - 'page_callback' => 'translation_node_overview', + 'page_callback' => 'i18n_translation_node_overview', 'page_arguments' => 'a:1:{i:0;i:1;}', 'fit' => '5', 'number_parts' => '3', @@ -28557,7 +40306,7 @@ 'description' => '', 'position' => '', 'weight' => '2', - 'file' => 'modules/translation/translation.pages.inc', + 'file' => 'sites/all/modules/i18n/i18n.pages.inc', )) ->values(array( 'path' => 'node/%/view', @@ -28587,7 +40336,7 @@ 'to_arg_functions' => '', 'access_callback' => '_node_add_access', 'access_arguments' => 'a:0:{}', - 'page_callback' => 'node_add_page', + 'page_callback' => 'i18ncontent_node_add_page', 'page_arguments' => 'a:0:{}', 'fit' => '3', 'number_parts' => '2', @@ -28609,15 +40358,15 @@ 'to_arg_functions' => '', 'access_callback' => 'node_access', 'access_arguments' => 'a:2:{i:0;s:6:"create";i:1;s:7:"article";}', - 'page_callback' => 'node_add', + 'page_callback' => 'i18ncontent_node_add', 'page_arguments' => 'a:1:{i:0;i:2;}', 'fit' => '7', 'number_parts' => '3', 'tab_parent' => '', 'tab_root' => 'node/add/article', 'title' => 'Article', - 'title_callback' => 'check_plain', - 'title_arguments' => '', + 'title_callback' => 'i18nstrings_title_callback', + 'title_arguments' => 'a:2:{i:0;s:26:"nodetype:type:article:name";i:1;s:7:"Article";}', 'type' => '6', 'block_callback' => '', 'description' => 'An article, content type.', @@ -28631,15 +40380,15 @@ 'to_arg_functions' => '', 'access_callback' => 'node_access', 'access_arguments' => 'a:2:{i:0;s:6:"create";i:1;s:7:"company";}', - 'page_callback' => 'node_add', + 'page_callback' => 'i18ncontent_node_add', 'page_arguments' => 'a:1:{i:0;i:2;}', 'fit' => '7', 'number_parts' => '3', 'tab_parent' => '', 'tab_root' => 'node/add/company', 'title' => 'Company', - 'title_callback' => 'check_plain', - 'title_arguments' => '', + 'title_callback' => 'i18nstrings_title_callback', + 'title_arguments' => 'a:2:{i:0;s:26:"nodetype:type:company:name";i:1;s:7:"Company";}', 'type' => '6', 'block_callback' => '', 'description' => 'Company node type', @@ -28653,15 +40402,15 @@ 'to_arg_functions' => '', 'access_callback' => 'node_access', 'access_arguments' => 'a:2:{i:0;s:6:"create";i:1;s:8:"employee";}', - 'page_callback' => 'node_add', + 'page_callback' => 'i18ncontent_node_add', 'page_arguments' => 'a:1:{i:0;i:2;}', 'fit' => '7', 'number_parts' => '3', 'tab_parent' => '', 'tab_root' => 'node/add/employee', 'title' => 'Employee', - 'title_callback' => 'check_plain', - 'title_arguments' => '', + 'title_callback' => 'i18nstrings_title_callback', + 'title_arguments' => 'a:2:{i:0;s:27:"nodetype:type:employee:name";i:1;s:8:"Employee";}', 'type' => '6', 'block_callback' => '', 'description' => 'Employee node type', @@ -28675,15 +40424,15 @@ 'to_arg_functions' => '', 'access_callback' => 'node_access', 'access_arguments' => 'a:2:{i:0;s:6:"create";i:1;s:7:"sponsor";}', - 'page_callback' => 'node_add', + 'page_callback' => 'i18ncontent_node_add', 'page_arguments' => 'a:1:{i:0;i:2;}', 'fit' => '7', 'number_parts' => '3', 'tab_parent' => '', 'tab_root' => 'node/add/sponsor', 'title' => 'Sponsor', - 'title_callback' => 'check_plain', - 'title_arguments' => '', + 'title_callback' => 'i18nstrings_title_callback', + 'title_arguments' => 'a:2:{i:0;s:26:"nodetype:type:sponsor:name";i:1;s:7:"Sponsor";}', 'type' => '6', 'block_callback' => '', 'description' => 'Sponsor node type', @@ -28697,15 +40446,15 @@ 'to_arg_functions' => '', 'access_callback' => 'node_access', 'access_arguments' => 'a:2:{i:0;s:6:"create";i:1;s:5:"story";}', - 'page_callback' => 'node_add', + 'page_callback' => 'i18ncontent_node_add', 'page_arguments' => 'a:1:{i:0;i:2;}', 'fit' => '7', 'number_parts' => '3', 'tab_parent' => '', 'tab_root' => 'node/add/story', 'title' => 'Story', - 'title_callback' => 'check_plain', - 'title_arguments' => '', + 'title_callback' => 'i18nstrings_title_callback', + 'title_arguments' => 'a:2:{i:0;s:24:"nodetype:type:story:name";i:1;s:5:"Story";}', 'type' => '6', 'block_callback' => '', 'description' => "A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments.", @@ -28719,15 +40468,15 @@ 'to_arg_functions' => '', 'access_callback' => 'node_access', 'access_arguments' => 'a:2:{i:0;s:6:"create";i:1;s:10:"test_event";}', - 'page_callback' => 'node_add', + 'page_callback' => 'i18ncontent_node_add', 'page_arguments' => 'a:1:{i:0;i:2;}', 'fit' => '7', 'number_parts' => '3', 'tab_parent' => '', 'tab_root' => 'node/add/test-event', 'title' => 'Migrate test event', - 'title_callback' => 'check_plain', - 'title_arguments' => '', + 'title_callback' => 'i18nstrings_title_callback', + 'title_arguments' => 'a:2:{i:0;s:29:"nodetype:type:test_event:name";i:1;s:18:"Migrate test event";}', 'type' => '6', 'block_callback' => '', 'description' => 'test event description here', @@ -28741,15 +40490,15 @@ 'to_arg_functions' => '', 'access_callback' => 'node_access', 'access_arguments' => 'a:2:{i:0;s:6:"create";i:1;s:9:"test_page";}', - 'page_callback' => 'node_add', + 'page_callback' => 'i18ncontent_node_add', 'page_arguments' => 'a:1:{i:0;i:2;}', 'fit' => '7', 'number_parts' => '3', 'tab_parent' => '', 'tab_root' => 'node/add/test-page', 'title' => 'Migrate test page', - 'title_callback' => 'check_plain', - 'title_arguments' => '', + 'title_callback' => 'i18nstrings_title_callback', + 'title_arguments' => 'a:2:{i:0;s:28:"nodetype:type:test_page:name";i:1;s:17:"Migrate test page";}', 'type' => '6', 'block_callback' => '', 'description' => "A page, similar in form to a story, is a simple method for creating and displaying information that rarely changes, such as an \"About us\" section of a website. By default, a page entry does not allow visitor comments and is not featured on the site's initial home page.", @@ -28763,15 +40512,15 @@ 'to_arg_functions' => '', 'access_callback' => 'node_access', 'access_arguments' => 'a:2:{i:0;s:6:"create";i:1;s:11:"test_planet";}', - 'page_callback' => 'node_add', + 'page_callback' => 'i18ncontent_node_add', 'page_arguments' => 'a:1:{i:0;i:2;}', 'fit' => '7', 'number_parts' => '3', 'tab_parent' => '', 'tab_root' => 'node/add/test-planet', 'title' => 'Migrate test planet', - 'title_callback' => 'check_plain', - 'title_arguments' => '', + 'title_callback' => 'i18nstrings_title_callback', + 'title_arguments' => 'a:2:{i:0;s:30:"nodetype:type:test_planet:name";i:1;s:19:"Migrate test planet";}', 'type' => '6', 'block_callback' => '', 'description' => "A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments.", @@ -28785,15 +40534,15 @@ 'to_arg_functions' => '', 'access_callback' => 'node_access', 'access_arguments' => 'a:2:{i:0;s:6:"create";i:1;s:10:"test_story";}', - 'page_callback' => 'node_add', + 'page_callback' => 'i18ncontent_node_add', 'page_arguments' => 'a:1:{i:0;i:2;}', 'fit' => '7', 'number_parts' => '3', 'tab_parent' => '', 'tab_root' => 'node/add/test-story', 'title' => 'Migrate test story', - 'title_callback' => 'check_plain', - 'title_arguments' => '', + 'title_callback' => 'i18nstrings_title_callback', + 'title_arguments' => 'a:2:{i:0;s:29:"nodetype:type:test_story:name";i:1;s:18:"Migrate test story";}', 'type' => '6', 'block_callback' => '', 'description' => "A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments.", @@ -28917,7 +40666,7 @@ 'to_arg_functions' => '', 'access_callback' => 'user_access', 'access_arguments' => 'a:1:{i:0;s:14:"access content";}', - 'page_callback' => 'taxonomy_autocomplete', + 'page_callback' => 'i18ntaxonomy_autocomplete', 'page_arguments' => 'a:0:{}', 'fit' => '3', 'number_parts' => '2', @@ -28931,7 +40680,7 @@ 'description' => '', 'position' => '', 'weight' => '0', - 'file' => 'modules/taxonomy/taxonomy.pages.inc', + 'file' => 'sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.pages.inc', )) ->values(array( 'path' => 'taxonomy/term/%', @@ -28939,7 +40688,7 @@ 'to_arg_functions' => '', 'access_callback' => 'user_access', 'access_arguments' => 'a:1:{i:0;s:14:"access content";}', - 'page_callback' => 'taxonomy_term_page', + 'page_callback' => 'i18ntaxonomy_term_page', 'page_arguments' => 'a:1:{i:0;i:2;}', 'fit' => '6', 'number_parts' => '3', @@ -28953,7 +40702,7 @@ 'description' => '', 'position' => '', 'weight' => '0', - 'file' => 'modules/taxonomy/taxonomy.pages.inc', + 'file' => 'sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.pages.inc', )) ->values(array( 'path' => 'upload/js', @@ -29122,7 +40871,7 @@ 'tab_parent' => 'user/%/edit', 'tab_root' => 'user/%', 'title' => '', - 'title_callback' => 'check_plain', + 'title_callback' => 'i18nprofile_translate_category', 'title_arguments' => 'a:1:{i:0;s:19:"Administrative data";}', 'type' => '128', 'block_callback' => '', @@ -29144,7 +40893,7 @@ 'tab_parent' => 'user/%/edit', 'tab_root' => 'user/%', 'title' => '', - 'title_callback' => 'check_plain', + 'title_callback' => 'i18nprofile_translate_category', 'title_arguments' => 'a:1:{i:0;s:25:"Communication preferences";}', 'type' => '128', 'block_callback' => '', @@ -29166,7 +40915,7 @@ 'tab_parent' => 'user/%/edit', 'tab_root' => 'user/%', 'title' => '', - 'title_callback' => 'check_plain', + 'title_callback' => 'i18nprofile_translate_category', 'title_arguments' => 'a:1:{i:0;s:20:"Personal information";}', 'type' => '128', 'block_callback' => '', @@ -30221,7 +41970,7 @@ 'name' => 'Migrate test event', 'module' => 'node', 'description' => 'test event description here', - 'help' => 'help text here', + 'help' => '', 'has_title' => '1', 'title_label' => 'Event Name', 'has_body' => '1', @@ -31000,7 +42749,7 @@ 'bootstrap' => '0', 'schema_version' => '6001', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:10:"Aggregator";s:11:"description";s:57:"Aggregates syndicated content (RSS, RDF, and Atom feeds).";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:10:"Aggregator";s:11:"description";s:57:"Aggregates syndicated content (RSS, RDF, and Atom feeds).";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/block/block.module', @@ -31012,7 +42761,7 @@ 'bootstrap' => '0', 'schema_version' => '0', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:5:"Block";s:11:"description";s:62:"Controls the boxes that are displayed around the main content.";s:7:"package";s:15:"Core - required";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:5:"Block";s:11:"description";s:62:"Controls the boxes that are displayed around the main content.";s:7:"package";s:15:"Core - required";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/blog/blog.module', @@ -31024,7 +42773,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:4:"Blog";s:11:"description";s:69:"Enables keeping easily and regularly updated user web pages or blogs.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:4:"Blog";s:11:"description";s:69:"Enables keeping easily and regularly updated user web pages or blogs.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/blogapi/blogapi.module', @@ -31036,7 +42785,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:8:"Blog API";s:11:"description";s:79:"Allows users to post content using applications that support XML-RPC blog APIs.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:8:"Blog API";s:11:"description";s:79:"Allows users to post content using applications that support XML-RPC blog APIs.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/book/book.module', @@ -31048,7 +42797,7 @@ 'bootstrap' => '0', 'schema_version' => '6000', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:4:"Book";s:11:"description";s:63:"Allows users to structure site pages in a hierarchy or outline.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:4:"Book";s:11:"description";s:63:"Allows users to structure site pages in a hierarchy or outline.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/color/color.module', @@ -31060,7 +42809,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:5:"Color";s:11:"description";s:61:"Allows the user to change the color scheme of certain themes.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:5:"Color";s:11:"description";s:61:"Allows the user to change the color scheme of certain themes.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/comment/comment.module', @@ -31072,7 +42821,7 @@ 'bootstrap' => '0', 'schema_version' => '6005', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:7:"Comment";s:11:"description";s:57:"Allows users to comment on and discuss published content.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:7:"Comment";s:11:"description";s:57:"Allows users to comment on and discuss published content.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/contact/contact.module', @@ -31084,7 +42833,7 @@ 'bootstrap' => '0', 'schema_version' => '6001', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:7:"Contact";s:11:"description";s:61:"Enables the use of both personal and site-wide contact forms.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:7:"Contact";s:11:"description";s:61:"Enables the use of both personal and site-wide contact forms.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/dblog/dblog.module', @@ -31096,7 +42845,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:16:"Database logging";s:11:"description";s:47:"Logs and records system events to the database.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:16:"Database logging";s:11:"description";s:47:"Logs and records system events to the database.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/filter/filter.module', @@ -31108,7 +42857,7 @@ 'bootstrap' => '0', 'schema_version' => '0', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:6:"Filter";s:11:"description";s:60:"Handles the filtering of content in preparation for display.";s:7:"package";s:15:"Core - required";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:6:"Filter";s:11:"description";s:60:"Handles the filtering of content in preparation for display.";s:7:"package";s:15:"Core - required";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/forum/forum.module', @@ -31120,7 +42869,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:5:"Forum";s:11:"description";s:50:"Enables threaded discussions about general topics.";s:12:"dependencies";a:2:{i:0;s:8:"taxonomy";i:1;s:7:"comment";}s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:5:"Forum";s:11:"description";s:50:"Enables threaded discussions about general topics.";s:12:"dependencies";a:2:{i:0;s:8:"taxonomy";i:1;s:7:"comment";}s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/help/help.module', @@ -31132,7 +42881,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:4:"Help";s:11:"description";s:35:"Manages the display of online help.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:4:"Help";s:11:"description";s:35:"Manages the display of online help.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/locale/locale.module', @@ -31144,7 +42893,7 @@ 'bootstrap' => '0', 'schema_version' => '6007', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:6:"Locale";s:11:"description";s:119:"Adds language handling functionality and enables the translation of the user interface to languages other than English.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:6:"Locale";s:11:"description";s:119:"Adds language handling functionality and enables the translation of the user interface to languages other than English.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/menu/menu.module', @@ -31156,7 +42905,7 @@ 'bootstrap' => '0', 'schema_version' => '6000', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:4:"Menu";s:11:"description";s:60:"Allows administrators to customize the site navigation menu.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:4:"Menu";s:11:"description";s:60:"Allows administrators to customize the site navigation menu.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/node/node.module', @@ -31168,7 +42917,7 @@ 'bootstrap' => '0', 'schema_version' => '0', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:4:"Node";s:11:"description";s:66:"Allows content to be submitted to the site and displayed on pages.";s:7:"package";s:15:"Core - required";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:4:"Node";s:11:"description";s:66:"Allows content to be submitted to the site and displayed on pages.";s:7:"package";s:15:"Core - required";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/openid/openid.module', @@ -31180,7 +42929,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:6:"OpenID";s:11:"description";s:48:"Allows users to log into your site using OpenID.";s:7:"version";s:4:"6.37";s:7:"package";s:15:"Core - optional";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:6:"OpenID";s:11:"description";s:48:"Allows users to log into your site using OpenID.";s:7:"version";s:8:"6.38-dev";s:7:"package";s:15:"Core - optional";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/path/path.module', @@ -31192,7 +42941,7 @@ 'bootstrap' => '0', 'schema_version' => '0', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:4:"Path";s:11:"description";s:28:"Allows users to rename URLs.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:4:"Path";s:11:"description";s:28:"Allows users to rename URLs.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/php/php.module', @@ -31204,7 +42953,7 @@ 'bootstrap' => '0', 'schema_version' => '0', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:10:"PHP filter";s:11:"description";s:50:"Allows embedded PHP code/snippets to be evaluated.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:10:"PHP filter";s:11:"description";s:50:"Allows embedded PHP code/snippets to be evaluated.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/ping/ping.module', @@ -31216,7 +42965,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:4:"Ping";s:11:"description";s:51:"Alerts other sites when your site has been updated.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:4:"Ping";s:11:"description";s:51:"Alerts other sites when your site has been updated.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/poll/poll.module', @@ -31228,7 +42977,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:4:"Poll";s:11:"description";s:95:"Allows your site to capture votes on different topics in the form of multiple choice questions.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:4:"Poll";s:11:"description";s:95:"Allows your site to capture votes on different topics in the form of multiple choice questions.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/profile/profile.module', @@ -31240,7 +42989,7 @@ 'bootstrap' => '0', 'schema_version' => '6001', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:7:"Profile";s:11:"description";s:36:"Supports configurable user profiles.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:7:"Profile";s:11:"description";s:36:"Supports configurable user profiles.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/search/search.module', @@ -31252,7 +43001,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:6:"Search";s:11:"description";s:36:"Enables site-wide keyword searching.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:6:"Search";s:11:"description";s:36:"Enables site-wide keyword searching.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/statistics/statistics.module', @@ -31264,7 +43013,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:10:"Statistics";s:11:"description";s:37:"Logs access statistics for your site.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:10:"Statistics";s:11:"description";s:37:"Logs access statistics for your site.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/syslog/syslog.module', @@ -31276,7 +43025,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:6:"Syslog";s:11:"description";s:41:"Logs and records system events to syslog.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:6:"Syslog";s:11:"description";s:41:"Logs and records system events to syslog.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/system/system.module', @@ -31288,7 +43037,7 @@ 'bootstrap' => '0', 'schema_version' => '6055', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:6:"System";s:11:"description";s:54:"Handles general site configuration for administrators.";s:7:"package";s:15:"Core - required";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:6:"System";s:11:"description";s:54:"Handles general site configuration for administrators.";s:7:"package";s:15:"Core - required";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/taxonomy/taxonomy.module', @@ -31300,7 +43049,7 @@ 'bootstrap' => '0', 'schema_version' => '6001', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:8:"Taxonomy";s:11:"description";s:38:"Enables the categorization of content.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:8:"Taxonomy";s:11:"description";s:38:"Enables the categorization of content.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/throttle/throttle.module', @@ -31312,7 +43061,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:8:"Throttle";s:11:"description";s:66:"Handles the auto-throttling mechanism, to control site congestion.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:8:"Throttle";s:11:"description";s:66:"Handles the auto-throttling mechanism, to control site congestion.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/tracker/tracker.module', @@ -31324,7 +43073,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:7:"Tracker";s:11:"description";s:43:"Enables tracking of recent posts for users.";s:12:"dependencies";a:1:{i:0;s:7:"comment";}s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:7:"Tracker";s:11:"description";s:43:"Enables tracking of recent posts for users.";s:12:"dependencies";a:1:{i:0;s:7:"comment";}s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/translation/translation.module', @@ -31336,7 +43085,7 @@ 'bootstrap' => '0', 'schema_version' => '0', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:19:"Content translation";s:11:"description";s:57:"Allows content to be translated into different languages.";s:12:"dependencies";a:1:{i:0;s:6:"locale";}s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:19:"Content translation";s:11:"description";s:57:"Allows content to be translated into different languages.";s:12:"dependencies";a:1:{i:0;s:6:"locale";}s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/trigger/trigger.module', @@ -31348,7 +43097,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:7:"Trigger";s:11:"description";s:90:"Enables actions to be fired on certain system events, such as when new content is created.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:7:"Trigger";s:11:"description";s:90:"Enables actions to be fired on certain system events, such as when new content is created.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/update/update.module', @@ -31360,7 +43109,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:13:"Update status";s:11:"description";s:88:"Checks the status of available updates for Drupal and your installed modules and themes.";s:7:"version";s:4:"6.37";s:7:"package";s:15:"Core - optional";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:13:"Update status";s:11:"description";s:88:"Checks the status of available updates for Drupal and your installed modules and themes.";s:7:"version";s:8:"6.38-dev";s:7:"package";s:15:"Core - optional";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/upload/upload.module', @@ -31372,7 +43121,7 @@ 'bootstrap' => '0', 'schema_version' => '6000', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:6:"Upload";s:11:"description";s:51:"Allows users to upload and attach files to content.";s:7:"package";s:15:"Core - optional";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:6:"Upload";s:11:"description";s:51:"Allows users to upload and attach files to content.";s:7:"package";s:15:"Core - optional";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'modules/user/user.module', @@ -31384,7 +43133,7 @@ 'bootstrap' => '0', 'schema_version' => '0', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:4:"User";s:11:"description";s:47:"Manages the user registration and login system.";s:7:"package";s:15:"Core - required";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:8:{s:4:"name";s:4:"User";s:11:"description";s:47:"Manages the user registration and login system.";s:7:"package";s:15:"Core - required";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'sites/all/modules/cck/content.module', @@ -31396,7 +43145,7 @@ 'bootstrap' => '0', 'schema_version' => '6010', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:7:"Content";s:11:"description";s:50:"Allows administrators to define new content types.";s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-2.10";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1434568159";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:7:"Content";s:11:"description";s:50:"Allows administrators to define new content types.";s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:7:"6.x-2.9";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1294407979";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'sites/all/modules/cck/modules/content_copy/content_copy.module', @@ -31408,7 +43157,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:12:"Content Copy";s:11:"description";s:51:"Enables ability to import/export field definitions.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-2.10";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1434568159";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:12:"Content Copy";s:11:"description";s:51:"Enables ability to import/export field definitions.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:7:"6.x-2.9";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1294407979";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'sites/all/modules/cck/modules/content_permissions/content_permissions.module', @@ -31420,7 +43169,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:19:"Content Permissions";s:11:"description";s:43:"Set field-level permissions for CCK fields.";s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"version";s:8:"6.x-2.10";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1434568159";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:19:"Content Permissions";s:11:"description";s:43:"Set field-level permissions for CCK fields.";s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"version";s:7:"6.x-2.9";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1294407979";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'sites/all/modules/cck/modules/fieldgroup/fieldgroup.module', @@ -31432,7 +43181,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:10:"Fieldgroup";s:11:"description";s:37:"Create display groups for CCK fields.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-2.10";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1434568159";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:10:"Fieldgroup";s:11:"description";s:37:"Create display groups for CCK fields.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:7:"6.x-2.9";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1294407979";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'sites/all/modules/cck/modules/nodereference/nodereference.module', @@ -31444,7 +43193,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:14:"Node Reference";s:11:"description";s:59:"Defines a field type for referencing one node from another.";s:12:"dependencies";a:3:{i:0;s:7:"content";i:1;s:4:"text";i:2;s:13:"optionwidgets";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-2.10";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1434568159";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:14:"Node Reference";s:11:"description";s:59:"Defines a field type for referencing one node from another.";s:12:"dependencies";a:3:{i:0;s:7:"content";i:1;s:4:"text";i:2;s:13:"optionwidgets";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:7:"6.x-2.9";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1294407979";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'sites/all/modules/cck/modules/number/number.module', @@ -31456,7 +43205,7 @@ 'bootstrap' => '0', 'schema_version' => '6000', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:6:"Number";s:11:"description";s:28:"Defines numeric field types.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-2.10";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1434568159";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:6:"Number";s:11:"description";s:28:"Defines numeric field types.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:7:"6.x-2.9";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1294407979";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'sites/all/modules/cck/modules/optionwidgets/optionwidgets.module', @@ -31468,7 +43217,7 @@ 'bootstrap' => '0', 'schema_version' => '6001', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:14:"Option Widgets";s:11:"description";s:82:"Defines selection, check box and radio button widgets for text and numeric fields.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-2.10";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1434568159";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:14:"Option Widgets";s:11:"description";s:82:"Defines selection, check box and radio button widgets for text and numeric fields.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:7:"6.x-2.9";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1294407979";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'sites/all/modules/cck/modules/text/text.module', @@ -31480,7 +43229,7 @@ 'bootstrap' => '0', 'schema_version' => '6003', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:4:"Text";s:11:"description";s:32:"Defines simple text field types.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-2.10";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1434568159";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:4:"Text";s:11:"description";s:32:"Defines simple text field types.";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:7:"6.x-2.9";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1294407979";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'sites/all/modules/cck/modules/userreference/userreference.module', @@ -31492,7 +43241,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:14:"User Reference";s:11:"description";s:56:"Defines a field type for referencing a user from a node.";s:12:"dependencies";a:3:{i:0;s:7:"content";i:1;s:4:"text";i:2;s:13:"optionwidgets";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-2.10";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1434568159";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:14:"User Reference";s:11:"description";s:56:"Defines a field type for referencing a user from a node.";s:12:"dependencies";a:3:{i:0;s:7:"content";i:1;s:4:"text";i:2;s:13:"optionwidgets";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:7:"version";s:7:"6.x-2.9";s:7:"project";s:3:"cck";s:9:"datestamp";s:10:"1294407979";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'sites/all/modules/date/date/date.module', @@ -31711,6 +43460,138 @@ 'info' => 'a:10:{s:4:"name";s:14:"FileField Meta";s:11:"description";s:48:"Add metadata gathering and storage to FileField.";s:12:"dependencies";a:2:{i:0;s:9:"filefield";i:1;s:6:"getid3";}s:7:"package";s:3:"CCK";s:4:"core";s:3:"6.x";s:3:"php";s:3:"5.0";s:7:"version";s:8:"6.x-3.13";s:7:"project";s:9:"filefield";s:9:"datestamp";s:10:"1405541029";s:10:"dependents";a:0:{}}', )) ->values(array( + 'filename' => 'sites/all/modules/i18n/i18n.module', + 'name' => 'i18n', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '1', + 'schema_version' => '9', + 'weight' => '10', + 'info' => 'a:10:{s:4:"name";s:20:"Internationalization";s:11:"description";s:49:"Extends Drupal support for multilingual features.";s:12:"dependencies";a:2:{i:0;s:6:"locale";i:1;s:11:"translation";}s:7:"package";s:13:"Multilanguage";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-1.10";s:7:"project";s:4:"i18n";s:9:"datestamp";s:10:"1318336004";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/i18n/i18nblocks/i18nblocks.module', + 'name' => 'i18nblocks', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '6001', + 'weight' => '0', + 'info' => 'a:10:{s:4:"name";s:17:"Block translation";s:11:"description";s:50:"Enables multilingual blocks and block translation.";s:12:"dependencies";a:2:{i:0;s:4:"i18n";i:1;s:11:"i18nstrings";}s:7:"package";s:13:"Multilanguage";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-1.10";s:7:"project";s:4:"i18n";s:9:"datestamp";s:10:"1318336004";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/i18n/i18ncck/i18ncck.module', + 'name' => 'i18ncck', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:10:{s:4:"name";s:15:"CCK translation";s:11:"description";s:56:"Supports translatable custom CCK fields and fieldgroups.";s:12:"dependencies";a:3:{i:0;s:4:"i18n";i:1;s:7:"content";i:2;s:11:"i18nstrings";}s:7:"package";s:13:"Multilanguage";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-1.10";s:7:"project";s:4:"i18n";s:9:"datestamp";s:10:"1318336004";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/i18n/i18ncontent/i18ncontent.module', + 'name' => 'i18ncontent', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '6002', + 'weight' => '0', + 'info' => 'a:10:{s:4:"name";s:24:"Content type translation";s:11:"description";s:99:"Add multilingual options for content and translate related strings: name, description, help text...";s:12:"dependencies";a:1:{i:0;s:11:"i18nstrings";}s:7:"package";s:13:"Multilanguage";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-1.10";s:7:"project";s:4:"i18n";s:9:"datestamp";s:10:"1318336004";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/i18n/i18nmenu/i18nmenu.module', + 'name' => 'i18nmenu', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:10:{s:4:"name";s:16:"Menu translation";s:11:"description";s:40:"Supports translatable custom menu items.";s:12:"dependencies";a:4:{i:0;s:4:"i18n";i:1;s:4:"menu";i:2;s:10:"i18nblocks";i:3;s:11:"i18nstrings";}s:7:"package";s:13:"Multilanguage";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-1.10";s:7:"project";s:4:"i18n";s:9:"datestamp";s:10:"1318336004";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/i18n/i18npoll/i18npoll.module', + 'name' => 'i18npoll', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:10:{s:4:"name";s:14:"Poll aggregate";s:11:"description";s:45:"Aggregates poll results for all translations.";s:12:"dependencies";a:2:{i:0;s:11:"translation";i:1;s:4:"poll";}s:7:"package";s:13:"Multilanguage";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-1.10";s:7:"project";s:4:"i18n";s:9:"datestamp";s:10:"1318336004";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/i18n/i18nprofile/i18nprofile.module', + 'name' => 'i18nprofile', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '2', + 'weight' => '0', + 'info' => 'a:10:{s:4:"name";s:19:"Profile translation";s:11:"description";s:36:"Enables multilingual profile fields.";s:12:"dependencies";a:2:{i:0;s:7:"profile";i:1;s:11:"i18nstrings";}s:7:"package";s:13:"Multilanguage";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-1.10";s:7:"project";s:4:"i18n";s:9:"datestamp";s:10:"1318336004";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/i18n/i18nstrings/i18nstrings.module', + 'name' => 'i18nstrings', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '6006', + 'weight' => '10', + 'info' => 'a:10:{s:4:"name";s:18:"String translation";s:11:"description";s:57:"Provides support for translation of user defined strings.";s:12:"dependencies";a:1:{i:0;s:6:"locale";}s:7:"package";s:13:"Multilanguage";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-1.10";s:7:"project";s:4:"i18n";s:9:"datestamp";s:10:"1318336004";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/i18n/i18nsync/i18nsync.module', + 'name' => 'i18nsync', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:10:{s:4:"name";s:24:"Synchronize translations";s:11:"description";s:74:"Synchronizes taxonomy and fields accross translations of the same content.";s:12:"dependencies";a:1:{i:0;s:4:"i18n";}s:7:"package";s:13:"Multilanguage";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-1.10";s:7:"project";s:4:"i18n";s:9:"datestamp";s:10:"1318336004";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/i18n/i18ntaxonomy/i18ntaxonomy.module', + 'name' => 'i18ntaxonomy', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '6002', + 'weight' => '5', + 'info' => 'a:10:{s:4:"name";s:20:"Taxonomy translation";s:11:"description";s:30:"Enables multilingual taxonomy.";s:12:"dependencies";a:3:{i:0;s:4:"i18n";i:1;s:8:"taxonomy";i:2;s:11:"i18nstrings";}s:7:"package";s:13:"Multilanguage";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-1.10";s:7:"project";s:4:"i18n";s:9:"datestamp";s:10:"1318336004";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/i18n/tests/i18n_test.module', + 'name' => 'i18n_test', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:10:{s:4:"name";s:26:"Internationalization tests";s:11:"description";s:55:"Helper module for testing i18n (do not enable manually)";s:12:"dependencies";a:3:{i:0;s:6:"locale";i:1;s:11:"translation";i:2;s:4:"i18n";}s:7:"package";s:7:"Testing";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-1.10";s:7:"project";s:4:"i18n";s:9:"datestamp";s:10:"1318336004";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', +)) +->values(array( 'filename' => 'sites/all/modules/imageapi/imageapi.module', 'name' => 'imageapi', 'type' => 'module', @@ -31819,6 +43700,30 @@ 'info' => 'a:10:{s:4:"name";s:11:"Phone - CCK";s:11:"description";s:84:"The phone module allows administrators to define a CCK field type for phone numbers.";s:7:"package";s:3:"CCK";s:12:"dependencies";a:1:{i:0;s:7:"content";}s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-2.18";s:7:"project";s:5:"phone";s:9:"datestamp";s:10:"1294067495";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( + 'filename' => 'sites/all/modules/variable/variable.module', + 'name' => 'variable', + 'type' => 'module', + 'owner' => '', + 'status' => '0', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '-1', + 'weight' => '0', + 'info' => 'a:7:{s:4:"name";s:12:"Variable API";s:11:"description";s:12:"Variable API";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( + 'filename' => 'sites/all/modules/variable/variable_admin/variable_admin.module', + 'name' => 'variable_admin', + 'type' => 'module', + 'owner' => '', + 'status' => '1', + 'throttle' => '0', + 'bootstrap' => '0', + 'schema_version' => '0', + 'weight' => '0', + 'info' => 'a:7:{s:4:"name";s:14:"Variable admin";s:11:"description";s:23:"Variable API - Admin UI";s:4:"core";s:3:"6.x";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:7:"version";N;s:3:"php";s:5:"4.3.5";}', +)) +->values(array( 'filename' => 'sites/all/modules/views/tests/views_test.module', 'name' => 'views_test', 'type' => 'module', @@ -31840,7 +43745,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:5:"Views";s:11:"description";s:55:"Create customized lists and queries from your database.";s:7:"package";s:5:"Views";s:4:"core";s:3:"6.x";s:7:"version";s:7:"6.x-3.0";s:7:"project";s:5:"views";s:9:"datestamp";s:10:"1325638545";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:5:"Views";s:11:"description";s:55:"Create customized lists and queries from your database.";s:7:"package";s:5:"Views";s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-2.18";s:7:"project";s:5:"views";s:9:"datestamp";s:10:"1423647793";s:12:"dependencies";a:0:{}s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'sites/all/modules/views/views_export/views_export.module', @@ -31852,7 +43757,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:14:"Views exporter";s:11:"description";s:40:"Allows exporting multiple views at once.";s:7:"package";s:5:"Views";s:12:"dependencies";a:1:{i:0;s:5:"views";}s:4:"core";s:3:"6.x";s:7:"version";s:7:"6.x-3.0";s:7:"project";s:5:"views";s:9:"datestamp";s:10:"1325638545";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:14:"Views exporter";s:11:"description";s:40:"Allows exporting multiple views at once.";s:7:"package";s:5:"Views";s:12:"dependencies";a:1:{i:0;s:5:"views";}s:4:"core";s:3:"6.x";s:7:"version";s:8:"6.x-2.18";s:7:"project";s:5:"views";s:9:"datestamp";s:10:"1423647793";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'sites/all/modules/views/views_ui.module', @@ -31864,7 +43769,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:10:{s:4:"name";s:8:"Views UI";s:11:"description";s:93:"Administrative interface to views. Without this module, you cannot create or edit your views.";s:7:"package";s:5:"Views";s:4:"core";s:3:"6.x";s:12:"dependencies";a:1:{i:0;s:5:"views";}s:7:"version";s:7:"6.x-3.0";s:7:"project";s:5:"views";s:9:"datestamp";s:10:"1325638545";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:8:"Views UI";s:11:"description";s:93:"Administrative interface to views. Without this module, you cannot create or edit your views.";s:7:"package";s:5:"Views";s:4:"core";s:3:"6.x";s:12:"dependencies";a:1:{i:0;s:5:"views";}s:7:"version";s:8:"6.x-2.18";s:7:"project";s:5:"views";s:9:"datestamp";s:10:"1423647793";s:10:"dependents";a:0:{}s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'themes/bluemarine/bluemarine.info', @@ -31876,7 +43781,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:13:{s:4:"name";s:10:"Bluemarine";s:11:"description";s:66:"Table-based multi-column theme with a marine and ash color scheme.";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/bluemarine/script.js";}s:10:"screenshot";s:32:"themes/bluemarine/screenshot.png";s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:11:{s:4:"name";s:10:"Bluemarine";s:11:"description";s:66:"Table-based multi-column theme with a marine and ash color scheme.";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/bluemarine/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/bluemarine/script.js";}s:10:"screenshot";s:32:"themes/bluemarine/screenshot.png";s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'themes/chameleon/chameleon.info', @@ -31888,7 +43793,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:12:{s:4:"name";s:9:"Chameleon";s:11:"description";s:42:"Minimalist tabled theme with light colors.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:8:"features";a:4:{i:0;s:4:"logo";i:1;s:7:"favicon";i:2;s:4:"name";i:3;s:6:"slogan";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"scripts";a:1:{s:9:"script.js";s:26:"themes/chameleon/script.js";}s:10:"screenshot";s:31:"themes/chameleon/screenshot.png";s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:10:{s:4:"name";s:9:"Chameleon";s:11:"description";s:42:"Minimalist tabled theme with light colors.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:8:"features";a:4:{i:0;s:4:"logo";i:1;s:7:"favicon";i:2;s:4:"name";i:3;s:6:"slogan";}s:11:"stylesheets";a:1:{s:3:"all";a:2:{s:9:"style.css";s:26:"themes/chameleon/style.css";s:10:"common.css";s:27:"themes/chameleon/common.css";}}s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:7:"scripts";a:1:{s:9:"script.js";s:26:"themes/chameleon/script.js";}s:10:"screenshot";s:31:"themes/chameleon/screenshot.png";s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'themes/chameleon/marvin/marvin.info', @@ -31900,7 +43805,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:13:{s:4:"name";s:6:"Marvin";s:11:"description";s:31:"Boxy tabled theme in all grays.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:10:"base theme";s:9:"chameleon";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/chameleon/marvin/script.js";}s:10:"screenshot";s:38:"themes/chameleon/marvin/screenshot.png";s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:11:{s:4:"name";s:6:"Marvin";s:11:"description";s:31:"Boxy tabled theme in all grays.";s:7:"regions";a:2:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";}s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:10:"base theme";s:9:"chameleon";s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:33:"themes/chameleon/marvin/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/chameleon/marvin/script.js";}s:10:"screenshot";s:38:"themes/chameleon/marvin/screenshot.png";s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'themes/garland/garland.info', @@ -31912,7 +43817,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:13:{s:4:"name";s:7:"Garland";s:11:"description";s:66:"Tableless, recolorable, multi-column, fluid width theme (default).";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:24:"themes/garland/script.js";}s:10:"screenshot";s:29:"themes/garland/screenshot.png";s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:11:{s:4:"name";s:7:"Garland";s:11:"description";s:66:"Tableless, recolorable, multi-column, fluid width theme (default).";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:11:"stylesheets";a:2:{s:3:"all";a:1:{s:9:"style.css";s:24:"themes/garland/style.css";}s:5:"print";a:1:{s:9:"print.css";s:24:"themes/garland/print.css";}}s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:24:"themes/garland/script.js";}s:10:"screenshot";s:29:"themes/garland/screenshot.png";s:3:"php";s:5:"4.3.5";}', )) ->values(array( 'filename' => 'themes/garland/minnelli/minnelli.info', @@ -31924,7 +43829,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:14:{s:4:"name";s:8:"Minnelli";s:11:"description";s:56:"Tableless, recolorable, multi-column, fixed width theme.";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:10:"base theme";s:7:"garland";s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/garland/minnelli/script.js";}s:10:"screenshot";s:38:"themes/garland/minnelli/screenshot.png";s:3:"php";s:5:"4.3.5";s:6:"engine";s:11:"phptemplate";}', + 'info' => 'a:12:{s:4:"name";s:8:"Minnelli";s:11:"description";s:56:"Tableless, recolorable, multi-column, fixed width theme.";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:10:"base theme";s:7:"garland";s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:12:"minnelli.css";s:36:"themes/garland/minnelli/minnelli.css";}}s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:7:"scripts";a:1:{s:9:"script.js";s:33:"themes/garland/minnelli/script.js";}s:10:"screenshot";s:38:"themes/garland/minnelli/screenshot.png";s:3:"php";s:5:"4.3.5";s:6:"engine";s:11:"phptemplate";}', )) ->values(array( 'filename' => 'themes/pushbutton/pushbutton.info', @@ -31936,7 +43841,7 @@ 'bootstrap' => '0', 'schema_version' => '-1', 'weight' => '0', - 'info' => 'a:13:{s:4:"name";s:10:"Pushbutton";s:11:"description";s:52:"Tabled, multi-column theme in blue and orange tones.";s:7:"version";s:4:"6.37";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"project";s:6:"drupal";s:9:"datestamp";s:10:"1440020160";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/pushbutton/script.js";}s:10:"screenshot";s:32:"themes/pushbutton/screenshot.png";s:3:"php";s:5:"4.3.5";}', + 'info' => 'a:11:{s:4:"name";s:10:"Pushbutton";s:11:"description";s:52:"Tabled, multi-column theme in blue and orange tones.";s:7:"version";s:8:"6.38-dev";s:4:"core";s:3:"6.x";s:6:"engine";s:11:"phptemplate";s:7:"regions";a:5:{s:4:"left";s:12:"Left sidebar";s:5:"right";s:13:"Right sidebar";s:7:"content";s:7:"Content";s:6:"header";s:6:"Header";s:6:"footer";s:6:"Footer";}s:8:"features";a:10:{i:0;s:20:"comment_user_picture";i:1;s:7:"favicon";i:2;s:7:"mission";i:3;s:4:"logo";i:4;s:4:"name";i:5;s:17:"node_user_picture";i:6;s:6:"search";i:7;s:6:"slogan";i:8;s:13:"primary_links";i:9;s:15:"secondary_links";}s:11:"stylesheets";a:1:{s:3:"all";a:1:{s:9:"style.css";s:27:"themes/pushbutton/style.css";}}s:7:"scripts";a:1:{s:9:"script.js";s:27:"themes/pushbutton/script.js";}s:10:"screenshot";s:32:"themes/pushbutton/screenshot.png";s:3:"php";s:5:"4.3.5";}', )) ->execute(); @@ -31972,6 +43877,18 @@ 'size' => 'normal', 'default' => '0', ), + 'language' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '12', + 'default' => '', + ), + 'trid' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'default' => '0', + ), ), 'primary key' => array( 'tid', @@ -31986,6 +43903,8 @@ 'name', 'description', 'weight', + 'language', + 'trid', )) ->values(array( 'tid' => '1', @@ -31993,6 +43912,8 @@ 'name' => 'term 1 of vocabulary 1', 'description' => 'description of term 1 of vocabulary 1', 'weight' => '0', + 'language' => '', + 'trid' => '0', )) ->values(array( 'tid' => '2', @@ -32000,6 +43921,8 @@ 'name' => 'term 2 of vocabulary 2', 'description' => 'description of term 2 of vocabulary 2', 'weight' => '3', + 'language' => '', + 'trid' => '0', )) ->values(array( 'tid' => '3', @@ -32007,6 +43930,8 @@ 'name' => 'term 3 of vocabulary 2', 'description' => 'description of term 3 of vocabulary 2', 'weight' => '4', + 'language' => '', + 'trid' => '0', )) ->values(array( 'tid' => '4', @@ -32014,6 +43939,8 @@ 'name' => 'term 4 of vocabulary 3', 'description' => 'description of term 4 of vocabulary 3', 'weight' => '6', + 'language' => '', + 'trid' => '0', )) ->values(array( 'tid' => '5', @@ -32021,6 +43948,8 @@ 'name' => 'term 5 of vocabulary 3', 'description' => 'description of term 5 of vocabulary 3', 'weight' => '7', + 'language' => '', + 'trid' => '0', )) ->values(array( 'tid' => '6', @@ -32028,6 +43957,8 @@ 'name' => 'term 6 of vocabulary 3', 'description' => 'description of term 6 of vocabulary 3', 'weight' => '8', + 'language' => '', + 'trid' => '0', )) ->execute(); @@ -32552,8 +44483,8 @@ 'signature' => '', 'signature_format' => '0', 'created' => '0', - 'access' => '1444943005', - 'login' => '1444943005', + 'access' => '1458198052', + 'login' => '1458193160', 'status' => '1', 'timezone' => NULL, 'language' => '', @@ -32833,6 +44764,10 @@ 'value' => 's:5:"Guest";', )) ->values(array( + 'name' => 'array_filter', + 'value' => 'b:1;', +)) +->values(array( 'name' => 'book_allowed_types', 'value' => 'a:1:{i:0;s:4:"book";}', )) @@ -33214,7 +45149,7 @@ )) ->values(array( 'name' => 'css_js_query_string', - 'value' => 's:20:"SAkMTxRZndiw70000000";', + 'value' => 's:20:"8SAkMTxRZndiw7000000";', )) ->values(array( 'name' => 'date:story:4:field_test_datestamp_fromto', @@ -33513,6 +45448,10 @@ 'value' => 's:3:"all";', )) ->values(array( + 'name' => 'event_timezone_display', + 'value' => 's:5:"event";', +)) +->values(array( 'name' => 'feed_default_items', 'value' => 'i:10;', )) @@ -33593,6 +45532,10 @@ 'value' => 's:2:"25";', )) ->values(array( + 'name' => 'i18nstrings_allowed_formats', + 'value' => 'a:2:{i:0;i:1;i:1;i:2;}', +)) +->values(array( 'name' => 'image_jpeg_quality', 'value' => 'i:75;', )) @@ -33602,7 +45545,7 @@ )) ->values(array( 'name' => 'javascript_parsed', - 'value' => 'a:0:{}', + 'value' => 'a:21:{i:0;s:14:"misc/jquery.js";i:1;s:14:"misc/drupal.js";i:2;s:19:"misc/tableheader.js";i:3;s:16:"misc/collapse.js";i:4;s:16:"misc/textarea.js";i:5;s:20:"modules/user/user.js";i:6;s:17:"misc/tabledrag.js";i:7;s:26:"modules/profile/profile.js";i:8;s:12:"misc/form.js";i:9;s:19:"misc/tableselect.js";i:10;s:20:"misc/autocomplete.js";s:10:"refresh:ga";s:7:"waiting";s:10:"refresh:ab";s:7:"waiting";s:10:"refresh:ca";s:7:"waiting";s:10:"refresh:fi";s:7:"waiting";s:10:"refresh:es";s:7:"waiting";i:11;s:16:"misc/progress.js";i:12;s:13:"misc/batch.js";s:10:"refresh:nl";s:7:"waiting";s:10:"refresh:de";s:7:"waiting";s:10:"refresh:pl";s:7:"waiting";}', )) ->values(array( 'name' => 'language_content_type_article', @@ -33610,13 +45553,17 @@ )) ->values(array( 'name' => 'language_count', - 'value' => 'i:2;', + 'value' => 'i:11;', )) ->values(array( 'name' => 'language_default', 'value' => 'O:8:"stdClass":11:{s:8:"language";s:2:"en";s:4:"name";s:7:"English";s:6:"native";s:7:"English";s:9:"direction";s:1:"0";s:7:"enabled";i:1;s:7:"plurals";s:1:"0";s:7:"formula";s:0:"";s:6:"domain";s:0:"";s:6:"prefix";s:0:"";s:6:"weight";s:1:"0";s:10:"javascript";s:0:"";}', )) ->values(array( + 'name' => 'language_negotiation', + 'value' => 's:1:"1";', +)) +->values(array( 'name' => 'locale_cache_strings', 'value' => 'i:1;', )) @@ -34018,6 +45965,12 @@ 'size' => 'normal', 'default' => '0', ), + 'language' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '12', + 'default' => '', + ), ), 'primary key' => array( 'vid', @@ -34038,6 +45991,7 @@ 'tags', 'module', 'weight', + 'language', )) ->values(array( 'vid' => '1', @@ -34051,6 +46005,7 @@ 'tags' => '0', 'module' => 'taxonomy', 'weight' => '4', + 'language' => '', )) ->values(array( 'vid' => '2', @@ -34064,6 +46019,7 @@ 'tags' => '1', 'module' => 'taxonomy', 'weight' => '5', + 'language' => '', )) ->values(array( 'vid' => '3', @@ -34077,6 +46033,7 @@ 'tags' => '0', 'module' => 'taxonomy', 'weight' => '6', + 'language' => '', )) ->values(array( 'vid' => '4', @@ -34090,6 +46047,7 @@ 'tags' => '0', 'module' => 'taxonomy', 'weight' => '0', + 'language' => '', )) ->values(array( 'vid' => '5', @@ -34103,6 +46061,7 @@ 'tags' => '0', 'module' => 'taxonomy', 'weight' => '7', + 'language' => '', )) ->execute(); diff --git a/core/modules/migrate_drupal/tests/src/Kernel/MigrateDrupalTestBase.php b/core/modules/migrate_drupal/tests/src/Kernel/MigrateDrupalTestBase.php new file mode 100644 index 0000000..68c4211 --- /dev/null +++ b/core/modules/migrate_drupal/tests/src/Kernel/MigrateDrupalTestBase.php @@ -0,0 +1,52 @@ +installEntitySchema('user'); + $this->installConfig(['migrate_drupal', 'system']); + } + + /** + * Loads a database fixture into the source database connection. + * + * @param string $path + * Path to the dump file. + */ + protected function loadFixture($path) { + $default_db = Database::getConnection()->getKey(); + Database::setActiveConnection($this->sourceDatabase->getKey()); + + if (substr($path, -3) == '.gz') { + $path = 'compress.zlib://' . $path; + } + require $path; + + Database::setActiveConnection($default_db); + } + +} diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d6/EntityContentBaseTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d6/EntityContentBaseTest.php new file mode 100644 index 0000000..fc271a5 --- /dev/null +++ b/core/modules/migrate_drupal/tests/src/Kernel/d6/EntityContentBaseTest.php @@ -0,0 +1,94 @@ + 'signature', + 'entity_type' => 'user', + 'type' => 'text_long', + ])->save(); + + FieldConfig::create([ + 'field_name' => 'signature', + 'entity_type' => 'user', + 'bundle' => 'user', + ])->save(); + + User::create([ + 'uid' => 2, + 'name' => 'Ford Prefect', + 'mail' => 'ford.prefect@localhost', + 'signature' => array( + array( + 'value' => 'Bring a towel.', + 'format' => 'filtered_html', + ), + ), + 'init' => 'proto@zo.an', + ])->save(); + + $this->executeMigrations(['d6_filter_format', 'd6_user_role']); + } + + /** + * Tests overwriting all mapped properties in the destination entity (default + * behavior). + */ + public function testOverwriteAllMappedProperties() { + $this->executeMigration('d6_user'); + /** @var \Drupal\user\UserInterface $account */ + $account = User::load(2); + $this->assertIdentical('john.doe', $account->label()); + $this->assertIdentical('john.doe@example.com', $account->getEmail()); + $this->assertIdentical('doe@example.com', $account->getInitialEmail()); + } + + /** + * Tests overwriting selected properties in the destination entity, specified + * in the destination configuration. + */ + public function testOverwriteProperties() { + // Execute the migration in migrate_overwrite_test, which documents how + // property overwrites work. + $this->executeMigration('users'); + + /** @var \Drupal\user\UserInterface $account */ + $account = User::load(2); + $this->assertIdentical('john.doe', $account->label()); + $this->assertIdentical('john.doe@example.com', $account->getEmail()); + $this->assertIdentical('The answer is 42.', $account->signature->value); + // This value is not overwritten because it's not listed in + // overwrite_properties. + $this->assertIdentical('proto@zo.an', $account->getInitialEmail()); + } + +} diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6TestBase.php b/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6TestBase.php new file mode 100644 index 0000000..c650f57 --- /dev/null +++ b/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6TestBase.php @@ -0,0 +1,121 @@ +loadFixture( __DIR__ . '/../../../fixtures/drupal6.php'); + } + + /** + * Executes all user migrations. + * + * @param bool $include_pictures + * If TRUE, migrates user pictures. + */ + protected function migrateUsers($include_pictures = TRUE) { + $this->executeMigrations(['d6_filter_format', 'd6_user_role']); + + if ($include_pictures) { + $this->installEntitySchema('file'); + $this->executeMigrations([ + 'd6_file', + 'd6_user_picture_file', + 'user_picture_field', + 'user_picture_field_instance', + 'user_picture_entity_display', + 'user_picture_entity_form_display', + ]); + } + + $this->executeMigration('d6_user'); + } + + /** + * Migrates node types. + */ + protected function migrateContentTypes() { + $this->installConfig(['node']); + $this->executeMigration('d6_node_type'); + } + + /** + * Executes all field migrations. + */ + protected function migrateFields() { + $this->migrateContentTypes(); + $this->executeMigrations([ + 'd6_field', + 'd6_field_instance', + 'd6_field_instance_widget_settings', + 'd6_view_modes', + 'd6_field_formatter_settings', + 'd6_upload_field', + 'd6_upload_field_instance', + ]); + } + + /** + * Executes all content migrations. + * + * @param bool $include_revisions + * If TRUE, migrates node revisions. + */ + protected function migrateContent($include_revisions = FALSE) { + $this->migrateUsers(FALSE); + $this->migrateFields(); + + $this->installEntitySchema('node'); + $this->executeMigrations(['d6_node_settings', 'd6_node']); + + if ($include_revisions) { + $this->executeMigrations(['d6_node_revision']); + } + } + + /** + * Executes all taxonomy migrations. + */ + protected function migrateTaxonomy() { + $this->migrateContentTypes(); + $this->installEntitySchema('taxonomy_term'); + $this->executeMigrations([ + 'd6_taxonomy_vocabulary', + 'd6_vocabulary_field', + 'd6_vocabulary_field_instance', + 'd6_vocabulary_entity_display', + 'd6_vocabulary_entity_form_display', + 'd6_taxonomy_term', + ]); + } + +} diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7TestBase.php b/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7TestBase.php new file mode 100644 index 0000000..3a15ff6 --- /dev/null +++ b/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7TestBase.php @@ -0,0 +1,25 @@ +loadFixture(__DIR__ . '/../../../fixtures/drupal7.php'); + } + +} diff --git a/core/modules/migrate_drupal/tests/src/Kernel/dependencies/MigrateDependenciesTest.php b/core/modules/migrate_drupal/tests/src/Kernel/dependencies/MigrateDependenciesTest.php new file mode 100644 index 0000000..fb63811 --- /dev/null +++ b/core/modules/migrate_drupal/tests/src/Kernel/dependencies/MigrateDependenciesTest.php @@ -0,0 +1,78 @@ +container->get('plugin.manager.migration')->createInstances($migration_items); + $expected_order = array('d6_filter_format', 'd6_node:page', 'd6_comment'); + $this->assertIdentical(array_keys($migrations), $expected_order); + $expected_requirements = array( + // d6_comment depends on d6_node:*, which the deriver expands into every + // variant of d6_node. + 'd6_node:article', + 'd6_node:company', + 'd6_node:employee', + 'd6_node:event', + 'd6_node:page', + 'd6_node:sponsor', + 'd6_node:story', + 'd6_node:test_event', + 'd6_node:test_page', + 'd6_node:test_planet', + 'd6_node:test_story', + 'd6_node_type', + 'd6_node_settings', + 'd6_filter_format', + 'd6_user', + 'd6_comment_type', + 'd6_comment_entity_display', + 'd6_comment_entity_form_display', + ); + // Migration dependencies for comment include dependencies for node + // migration as well. + $actual_requirements = $migrations['d6_comment']->get('requirements'); + $this->assertIdentical(count($actual_requirements), count($expected_requirements)); + foreach ($expected_requirements as $requirement) { + $this->assertIdentical($actual_requirements[$requirement], $requirement); + } + } + + /** + * Tests dependencies on the migration of aggregator feeds & items. + */ + public function testAggregatorMigrateDependencies() { + /** @var \Drupal\migrate\Plugin\Migration $migration */ + $migration = $this->getMigration('d6_aggregator_item'); + $executable = new MigrateExecutable($migration, $this); + $this->startCollectingMessages(); + $executable->import(); + $this->assertEqual($this->migrateMessages['error'], array(SafeMarkup::format('Migration @id did not meet the requirements. Missing migrations d6_aggregator_feed. requirements: d6_aggregator_feed.', array('@id' => $migration->id())))); + $this->collectMessages = FALSE; + } + +} diff --git a/core/modules/migrate_drupal_ui/migrate_drupal_ui.info.yml b/core/modules/migrate_drupal_ui/migrate_drupal_ui.info.yml new file mode 100644 index 0000000..4a9c448 --- /dev/null +++ b/core/modules/migrate_drupal_ui/migrate_drupal_ui.info.yml @@ -0,0 +1,11 @@ +name: 'Drupal Upgrade UI' +type: module +description: 'UI for direct upgrades from older Drupal versions.' +package: 'Core (Experimental)' +version: VERSION +core: 8.x +configure: migrate_drupal_ui.upgrade +dependencies: + - migrate + - migrate_drupal + - dblog diff --git a/core/modules/migrate_drupal_ui/migrate_drupal_ui.install b/core/modules/migrate_drupal_ui/migrate_drupal_ui.install new file mode 100644 index 0000000..7df3f17 --- /dev/null +++ b/core/modules/migrate_drupal_ui/migrate_drupal_ui.install @@ -0,0 +1,16 @@ +toString(); + drupal_set_message(t('The Drupal Upgrade UI module has been enabled. Proceed to the upgrade form.', [':url' => $url])); +} diff --git a/core/modules/migrate_drupal_ui/migrate_drupal_ui.module b/core/modules/migrate_drupal_ui/migrate_drupal_ui.module new file mode 100644 index 0000000..6202081 --- /dev/null +++ b/core/modules/migrate_drupal_ui/migrate_drupal_ui.module @@ -0,0 +1,19 @@ +' . t('The Drupal Upgrade UI module provides a one-click upgrade from an earlier version of Drupal. For details, see the online documentation for the Drupal Upgrade UI module in the handbook on upgrading from previous versions.', [':migrate' => 'https://www.drupal.org/upgrade/migrate']) . '

                          '; + return $output; + } +} diff --git a/core/modules/migrate_drupal_ui/migrate_drupal_ui.routing.yml b/core/modules/migrate_drupal_ui/migrate_drupal_ui.routing.yml new file mode 100644 index 0000000..2d99abd --- /dev/null +++ b/core/modules/migrate_drupal_ui/migrate_drupal_ui.routing.yml @@ -0,0 +1,18 @@ +migrate_drupal_ui.upgrade: + path: '/upgrade' + defaults: + _form: '\Drupal\migrate_drupal_ui\Form\MigrateUpgradeForm' + _title: 'Upgrade' + requirements: + _custom_access: '\Drupal\migrate_drupal_ui\MigrateAccessCheck::checkAccess' + options: + _admin_route: TRUE + +migrate_drupal_ui.log: + path: '/upgrade/log' + defaults: + _controller: '\Drupal\migrate_drupal_ui\Controller\MigrateController::showLog' + requirements: + _custom_access: '\Drupal\migrate_drupal_ui\MigrateAccessCheck::checkAccess' + options: + _admin_route: TRUE diff --git a/core/modules/migrate_drupal_ui/src/Controller/MigrateController.php b/core/modules/migrate_drupal_ui/src/Controller/MigrateController.php new file mode 100644 index 0000000..5d4a885 --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Controller/MigrateController.php @@ -0,0 +1,29 @@ + 'migrate_drupal_ui']; + return $this->redirect('dblog.overview'); + } + +} diff --git a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php new file mode 100644 index 0000000..bf50d5b --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php @@ -0,0 +1,1123 @@ + [ + 'source_module' => 'system', + 'destination_module' => 'action', + ], + 'd6_aggregator_feed' => [ + 'source_module' => 'aggregator', + 'destination_module' => 'aggregator', + ], + 'd6_aggregator_item' => [ + 'source_module' => 'aggregator', + 'destination_module' => 'aggregator', + ], + 'd6_aggregator_settings' => [ + 'source_module' => 'aggregator', + 'destination_module' => 'aggregator', + ], + 'd7_aggregator_feed' => [ + 'source_module' => 'aggregator', + 'destination_module' => 'aggregator', + ], + 'd7_aggregator_item' => [ + 'source_module' => 'aggregator', + 'destination_module' => 'aggregator', + ], + 'd7_aggregator_settings' => [ + 'source_module' => 'aggregator', + 'destination_module' => 'aggregator', + ], + 'd7_blocked_ips' => [ + 'source_module' => 'system', + 'destination_module' => 'ban', + ], + 'd6_block' => [ + 'source_module' => 'block', + 'destination_module' => 'block', + ], + 'd7_block' => [ + 'source_module' => 'block', + 'destination_module' => 'block', + ], + 'block_content_body_field' => [ + 'source_module' => 'block', + 'destination_module' => 'block_content', + ], + 'block_content_type' => [ + 'source_module' => 'block', + 'destination_module' => 'block_content', + ], + 'd6_custom_block' => [ + 'source_module' => 'block', + 'destination_module' => 'block_content', + ], + 'd7_custom_block' => [ + 'source_module' => 'block', + 'destination_module' => 'block_content', + ], + 'd6_book' => [ + 'source_module' => 'book', + 'destination_module' => 'book', + ], + 'd6_book_settings' => [ + 'source_module' => 'book', + 'destination_module' => 'book', + ], + 'd6_comment' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_comment_entity_display' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_comment_entity_form_display' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_comment_entity_form_display_subject' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_comment_field' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_comment_field_instance' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd6_comment_type' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment_entity_display' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment_entity_form_display' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment_entity_form_display_subject' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment_field' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment_field_instance' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'd7_comment_type' => [ + 'source_module' => 'comment', + 'destination_module' => 'comment', + ], + 'contact_category' => [ + 'source_module' => 'contact', + 'destination_module' => 'contact', + ], + 'd6_contact_settings' => [ + 'source_module' => 'contact', + 'destination_module' => 'contact', + ], + 'd7_contact_settings' => [ + 'source_module' => 'contact', + 'destination_module' => 'contact', + ], + 'd6_dblog_settings' => [ + 'source_module' => 'dblog', + 'destination_module' => 'dblog', + ], + 'd7_dblog_settings' => [ + 'source_module' => 'dblog', + 'destination_module' => 'dblog', + ], + 'd6_field' => [ + 'source_module' => 'content', + 'destination_module' => 'field', + ], + 'd6_field_formatter_settings' => [ + 'source_module' => 'content', + 'destination_module' => 'field', + ], + 'd6_field_instance' => [ + 'source_module' => 'content', + 'destination_module' => 'field', + ], + 'd6_field_instance_widget_settings' => [ + 'source_module' => 'content', + 'destination_module' => 'field', + ], + 'd7_field' => [ + 'source_module' => 'field', + 'destination_module' => 'field', + ], + 'd7_field_formatter_settings' => [ + 'source_module' => 'field', + 'destination_module' => 'field', + ], + 'd7_field_instance' => [ + 'source_module' => 'field', + 'destination_module' => 'field', + ], + 'd7_field_instance_widget_settings' => [ + 'source_module' => 'field', + 'destination_module' => 'field', + ], + 'd7_view_modes' => [ + 'source_module' => 'field', + 'destination_module' => 'field', + ], + 'd6_file' => [ + 'source_module' => 'system', + 'destination_module' => 'file', + ], + 'd6_file_settings' => [ + 'source_module' => 'system', + 'destination_module' => 'file', + ], + 'd6_upload' => [ + 'source_module' => 'upload', + 'destination_module' => 'file', + ], + 'd6_upload_entity_display' => [ + 'source_module' => 'upload', + 'destination_module' => 'file', + ], + 'd6_upload_entity_form_display' => [ + 'source_module' => 'upload', + 'destination_module' => 'file', + ], + 'd6_upload_field' => [ + 'source_module' => 'upload', + 'destination_module' => 'file', + ], + 'd6_upload_field_instance' => [ + 'source_module' => 'upload', + 'destination_module' => 'file', + ], + 'd7_file' => [ + 'source_module' => 'file', + 'destination_module' => 'file', + ], + 'd6_filter_format' => [ + 'source_module' => 'filter', + 'destination_module' => 'filter', + ], + 'd7_filter_format' => [ + 'source_module' => 'filter', + 'destination_module' => 'filter', + ], + 'd6_forum_settings' => [ + 'source_module' => 'forum', + 'destination_module' => 'forum', + ], + 'd7_forum_settings' => [ + 'source_module' => 'forum', + 'destination_module' => 'forum', + ], + 'd6_imagecache_presets' => [ + 'source_module' => 'imagecache', + 'destination_module' => 'image', + ], + 'd7_image_settings' => [ + 'source_module' => 'image', + 'destination_module' => 'image', + ], + 'd7_image_styles' => [ + 'source_module' => 'image', + 'destination_module' => 'image', + ], + 'd7_language_negotiation_settings' => [ + 'source_module' => 'locale', + 'destination_module' => 'language', + ], + 'language' => [ + 'source_module' => 'locale', + 'destination_module' => 'language', + ], + 'locale_settings' => [ + 'source_module' => 'locale', + 'destination_module' => 'locale', + ], + 'menu_links' => [ + 'source_module' => 'menu', + 'destination_module' => 'menu_link_content', + ], + 'menu_settings' => [ + 'source_module' => 'menu', + 'destination_module' => 'menu_ui', + ], + 'd6_node' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_node_revision' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_node_setting_promote' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_node_setting_status' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_node_setting_sticky' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_node_settings' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_node_type' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_view_modes' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd7_node' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd7_node_revision' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd7_node_settings' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd7_node_title_label' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd7_node_type' => [ + 'source_module' => 'node', + 'destination_module' => 'node', + ], + 'd6_url_alias' => [ + 'source_module' => 'path', + 'destination_module' => 'path', + ], + 'd7_url_alias' => [ + 'source_module' => 'path', + 'destination_module' => 'path', + ], + 'search_page' => [ + 'source_module' => 'search', + 'destination_module' => 'search', + ], + 'd6_search_settings' => [ + 'source_module' => 'search', + 'destination_module' => 'search', + ], + 'd7_search_settings' => [ + 'source_module' => 'search', + 'destination_module' => 'search', + ], + 'd7_shortcut' => [ + 'source_module' => 'shortcut', + 'destination_module' => 'shortcut', + ], + 'd7_shortcut_set' => [ + 'source_module' => 'shortcut', + 'destination_module' => 'shortcut', + ], + 'd7_shortcut_set_users' => [ + 'source_module' => 'shortcut', + 'destination_module' => 'shortcut', + ], + 'd6_simpletest_settings' => [ + 'source_module' => 'simpletest', + 'destination_module' => 'simpletest', + ], + 'd7_simpletest_settings' => [ + 'source_module' => 'simpletest', + 'destination_module' => 'simpletest', + ], + 'd6_statistics_settings' => [ + 'source_module' => 'statistics', + 'destination_module' => 'statistics', + ], + 'd6_syslog_settings' => [ + 'source_module' => 'syslog', + 'destination_module' => 'syslog', + ], + 'd7_syslog_settings' => [ + 'source_module' => 'syslog', + 'destination_module' => 'syslog', + ], + 'd6_date_formats' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_cron' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_date' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_file' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_image' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_image_gd' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_logging' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_maintenance' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_performance' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_rss' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'd6_system_site' => [ + 'source_module' => 'system', + 'destination_module' => 'system', + ], + 'menu' => [ + 'source_module' => 'menu', + 'destination_module' => 'system', + ], + 'taxonomy_settings' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_taxonomy_term' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_taxonomy_vocabulary' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_term_node' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_term_node_revision' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_vocabulary_entity_display' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_vocabulary_entity_form_display' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_vocabulary_field' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd6_vocabulary_field_instance' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd7_taxonomy_term' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'd7_taxonomy_vocabulary' => [ + 'source_module' => 'taxonomy', + 'destination_module' => 'taxonomy', + ], + 'text_settings' => [ + 'source_module' => 'text', + 'destination_module' => 'text', + ], + 'd7_tracker_node' => [ + 'source_module' => 'tracker', + 'destination_module' => 'tracker', + ], + 'd7_tracker_settings' => [ + 'source_module' => 'tracker', + 'destination_module' => 'tracker', + ], + 'd7_tracker_user' => [ + 'source_module' => 'tracker', + 'destination_module' => 'tracker', + ], + 'update_settings' => [ + 'source_module' => 'update', + 'destination_module' => 'update', + ], + 'd6_profile_values' => [ + 'source_module' => 'profile', + 'destination_module' => 'user', + ], + 'd6_user' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd6_user_contact_settings' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd6_user_mail' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd6_user_picture_file' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd6_user_role' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd6_user_settings' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd7_user' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd7_user_flood' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd7_user_mail' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'd7_user_role' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'user_picture_entity_display' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'user_picture_entity_form_display' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'user_picture_field' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'user_picture_field_instance' => [ + 'source_module' => 'user', + 'destination_module' => 'user', + ], + 'user_profile_entity_display' => [ + 'source_module' => 'profile', + 'destination_module' => 'user', + ], + 'user_profile_entity_form_display' => [ + 'source_module' => 'profile', + 'destination_module' => 'user', + ], + 'user_profile_field' => [ + 'source_module' => 'profile', + 'destination_module' => 'user', + ], + 'user_profile_field_instance' => [ + 'source_module' => 'profile', + 'destination_module' => 'user', + ], + ]; + + /** + * The state service. + * + * @var \Drupal\Core\State\StateInterface + */ + protected $state; + + /** + * The date formatter service. + * + * @var \Drupal\Core\Datetime\DateFormatterInterface + */ + protected $dateFormatter; + + /** + * The renderer service. + * + * @var \Drupal\Core\Render\RendererInterface + */ + protected $renderer; + + /** + * The migration plugin manager. + * + * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface + */ + protected $pluginManager; + + /** + * Constructs the MigrateUpgradeForm. + * + * @param \Drupal\Core\State\StateInterface $state + * The state service. + * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter + * The date formatter service. + * @param \Drupal\Core\Render\RendererInterface $renderer + * The renderer service. + * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $plugin_manager + * The migration plugin manager. + */ + public function __construct(StateInterface $state, DateFormatterInterface $date_formatter, RendererInterface $renderer, MigrationPluginManagerInterface $plugin_manager) { + $this->state = $state; + $this->dateFormatter = $date_formatter; + $this->renderer = $renderer; + $this->pluginManager = $plugin_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('state'), + $container->get('date.formatter'), + $container->get('renderer'), + $container->get('plugin.manager.migration') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'migrate_drupal_ui_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $step = $form_state->getValue('step', 'overview'); + switch ($step) { + case 'overview': + return $this->buildOverviewForm($form, $form_state); + + case 'credentials': + return $this->buildCredentialForm($form, $form_state); + + case 'confirm': + return $this->buildConfirmForm($form, $form_state); + + default: + drupal_set_message($this->t('Unrecognized form step @step', ['@step' => $step]), 'error'); + return []; + } + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + // This method is intentionally empty, see the specific submit methods for + // each form step. + } + + /** + * Builds the form presenting an overview of the migration process. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + * The form structure. + */ + public function buildOverviewForm(array $form, FormStateInterface $form_state) { + $form['#title'] = $this->t('Drupal Upgrade'); + + if ($date_performed = $this->state->get('migrate_drupal_ui.performed')) { + // @todo Add back support for rollbacks and incremental migrations. + // https://www.drupal.org/node/2687843 + // https://www.drupal.org/node/2687849 + $form['upgrade_option_item'] = [ + '#type' => 'item', + '#prefix' => $this->t('An upgrade has already been performed on this site. To perform a new migration, create a clean and empty new install of Drupal 8. Rollbacks and incremental migrations are not yet supported through the user interface. For more information, see the upgrading handbook.', [':url' => 'https://www.drupal.org/upgrade/migrate']), + '#description' => $this->t('

                          Last upgrade: @date

                          ', ['@date' => $this->dateFormatter->format($date_performed)]), + ]; + return $form; + } + else { + $form['info_header'] = [ + '#markup' => '

                          ' . $this->t('Upgrade a Drupal site by importing it into a clean and empty new install of Drupal 8. You will lose any existing configuration once you import your site into it. See the upgrading handbook for more detailed information.', [ + ':url' => 'https://www.drupal.org/upgrade/migrate', + ]), + ]; + + $info[] = $this->t('Back up the database for this site. Upgrade will change the database for this site.'); + $info[] = $this->t('Make sure that the host this site is on has access to the database for your previous site.'); + $info[] = $this->t('If your previous site has private files to be migrated, a copy of your files directory must be accessible on the host this site is on.'); + $info[] = $this->t('In general, enable all modules on this site that are enabled on the previous site. For example, if you have used the book module on the previous site then you must enable the book module on this site for that data to be available on this site.'); + $info[] = $this->t('Put this site into maintenance mode.', [ + ':url' => Url::fromRoute('system.site_maintenance_mode')->toString(TRUE)->getGeneratedUrl(), + ]); + + $form['info'] = [ + '#theme' => 'item_list', + '#list_type' => 'ol', + '#items' => $info, + ]; + + $form['info_footer'] = [ + '#markup' => '

                          ' . $this->t('This upgrade can take a long time. It is better to import a local copy of your site instead of directly importing from your live site.'), + ]; + + $validate = []; + } + + $form['actions'] = ['#type' => 'actions']; + $form['actions']['save'] = [ + '#type' => 'submit', + '#value' => $this->t('Continue'), + '#button_type' => 'primary', + '#validate' => $validate, + '#submit' => ['::submitOverviewForm'], + ]; + return $form; + } + + /** + * Form submission handler for the overview form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function submitOverviewForm(array &$form, FormStateInterface $form_state) { + $form_state->setValue('step', 'credentials'); + $form_state->setRebuild(); + } + + /** + * Builds the database credential form and adds file location information. + * + * This is largely borrowed from \Drupal\Core\Installer\Form\SiteSettingsForm. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + * The form structure. + * + * @todo Private files directory not yet implemented, depends on + * https://www.drupal.org/node/2547125. + */ + public function buildCredentialForm(array $form, FormStateInterface $form_state) { + $form['#title'] = $this->t('Drupal Upgrade'); + + $drivers = $this->getDatabaseTypes(); + $drivers_keys = array_keys($drivers); + // @todo https://www.drupal.org/node/2678510 Because this is a multi-step + // form, the form is not rebuilt during submission. Ideally we would get + // the chosen driver from form input, if available, in order to use + // #limit_validation_errors in the same way + // \Drupal\Core\Installer\Form\SiteSettingsForm does. + $default_driver = current($drivers_keys); + + $default_options = []; + + $form['database'] = [ + '#type' => 'details', + '#title' => $this->t('Source database'), + '#description' => $this->t('Provide credentials for the database of the Drupal site you want to upgrade.'), + '#open' => TRUE, + ]; + + $form['database']['driver'] = [ + '#type' => 'radios', + '#title' => $this->t('Database type'), + '#required' => TRUE, + '#default_value' => $default_driver, + ]; + if (count($drivers) == 1) { + $form['database']['driver']['#disabled'] = TRUE; + } + + // Add driver-specific configuration options. + foreach ($drivers as $key => $driver) { + $form['database']['driver']['#options'][$key] = $driver->name(); + + $form['database']['settings'][$key] = $driver->getFormOptions($default_options); + // @todo https://www.drupal.org/node/2678510 Using + // #limit_validation_errors in the submit does not work so it is not + // possible to require the database and username for mysql and pgsql. + // This is because this is a multi-step form. + $form['database']['settings'][$key]['database']['#required'] = FALSE; + $form['database']['settings'][$key]['username']['#required'] = FALSE; + $form['database']['settings'][$key]['#prefix'] = '

                          ' . $this->t('@driver_name settings', ['@driver_name' => $driver->name()]) . '

                          '; + $form['database']['settings'][$key]['#type'] = 'container'; + $form['database']['settings'][$key]['#tree'] = TRUE; + $form['database']['settings'][$key]['advanced_options']['#parents'] = [$key]; + $form['database']['settings'][$key]['#states'] = [ + 'visible' => [ + ':input[name=driver]' => ['value' => $key], + ], + ]; + + // Move the host fields out of advanced settings. + if (isset($form['database']['settings'][$key]['advanced_options']['host'])) { + $form['database']['settings'][$key]['host'] = $form['database']['settings'][$key]['advanced_options']['host']; + $form['database']['settings'][$key]['host']['#title'] = 'Database host'; + $form['database']['settings'][$key]['host']['#weight'] = -1; + unset($form['database']['settings'][$key]['database']['#default_value']); + unset($form['database']['settings'][$key]['advanced_options']['host']); + } + } + + $form['source'] = [ + '#type' => 'details', + '#title' => $this->t('Source files'), + '#open' => TRUE, + ]; + $form['source']['source_base_path'] = [ + '#type' => 'textfield', + '#title' => $this->t('Files directory'), + '#description' => $this->t('To import files from your current Drupal site, enter a local file directory containing your site (e.g. /var/www/docroot), or your site address (e.g. http://example.com).'), + ]; + + $form['actions'] = ['#type' => 'actions']; + $form['actions']['save'] = [ + '#type' => 'submit', + '#value' => $this->t('Review upgrade'), + '#button_type' => 'primary', + '#validate' => ['::validateCredentialForm'], + '#submit' => ['::submitCredentialForm'], + ]; + return $form; + } + + /** + * Validation handler for the credentials form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function validateCredentialForm(array &$form, FormStateInterface $form_state) { + + // Retrieve the database driver from the form, use reflection to get the + // namespace, and then construct a valid database array the same as in + // settings.php. + $driver = $form_state->getValue('driver'); + $drivers = $this->getDatabaseTypes(); + $reflection = new \ReflectionClass($drivers[$driver]); + $install_namespace = $reflection->getNamespaceName(); + + $database = $form_state->getValue($driver); + // Cut the trailing \Install from namespace. + $database['namespace'] = substr($install_namespace, 0, strrpos($install_namespace, '\\')); + $database['driver'] = $driver; + + // Validate the driver settings and just end here if we have any issues. + if ($errors = $drivers[$driver]->validateDatabaseSettings($database)) { + foreach ($errors as $name => $message) { + $form_state->setErrorByName($name, $message); + } + return; + } + + try { + $connection = $this->getConnection($database); + $version = $this->getLegacyDrupalVersion($connection); + if (!$version) { + $form_state->setErrorByName($database['driver'] . '][0', $this->t('Source database does not contain a recognizable Drupal version.')); + } + else { + $this->createDatabaseStateSettings($database, $version); + $migrations = $this->getMigrations('migrate_drupal_' . $version, $version); + + // Get the system data from source database. + $system_data = $this->getSystemData($connection); + + // Convert the migration object into array + // so that it can be stored in form storage. + $migration_array = []; + foreach ($migrations as $migration) { + $migration_array[$migration->id()] = $migration->label(); + } + + // Store the retrieved migration IDs in form storage. + $form_state->set('migrations', $migration_array); + $form_state->set('source_base_path', $form_state->getValue('source_base_path')); + + // Store the retrived system data in form storage. + $form_state->set('system_data', $system_data); + } + } + catch (\Exception $e) { + $error_message = [ + '#type' => 'inline_template', + '#template' => '{% trans %}Resolve the issue below to continue the upgrade.{% endtrans%}{{ errors }}', + '#context' => [ + 'errors' => [ + '#theme' => 'item_list', + '#items' => [$e->getMessage()], + ], + ], + ]; + + $form_state->setErrorByName($database['driver'] . '][0', $this->renderer->renderPlain($error_message)); + } + } + + /** + * Submission handler for the credentials form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function submitCredentialForm(array &$form, FormStateInterface $form_state) { + // Indicate the next step is confirmation. + $form_state->setValue('step', 'confirm'); + $form_state->setRebuild(); + } + + /** + * Confirmation form for missing migrations, etc. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return array + * The form structure. + */ + public function buildConfirmForm(array $form, FormStateInterface $form_state) { + $form = parent::buildForm($form, $form_state); + $form['actions']['submit']['#submit'] = ['::submitConfirmForm']; + + $form['actions']['submit']['#value'] = $this->t('Perform upgrade'); + + $table_data = []; + $system_data = []; + foreach ($form_state->get('migrations') as $migration_id => $migration_label) { + // Fetch the system data at the first opportunity. + if (empty($system_data)) { + $system_data = $form_state->get('system_data'); + } + + // Handle derivatives. + list($migration_id,) = explode(':', $migration_id, 2); + $source_module = $this->moduleUpgradePaths[$migration_id]['source_module']; + $destination_module = $this->moduleUpgradePaths[$migration_id]['destination_module']; + $table_data[$source_module][$destination_module][$migration_id] = $migration_label; + } + // Sort the table by source module names and within that destination + // module names. + ksort($table_data); + foreach ($table_data as $source_module => $destination_module_info) { + ksort($table_data[$source_module]); + } + $unmigrated_source_modules = array_diff_key($system_data['module'], $table_data); + + // Missing migrations. + $form['missing_module_list_title'] = [ + '#type' => 'item', + '#title' => $this->t('Missing upgrade paths'), + '#description' => $this->t('The following items will not be upgraded. For more information see Upgrading from Drupal 6 or 7 to Drupal 8.', array(':migrate' => 'https://www.drupal.org/upgrade/migrate')), + ]; + $form['missing_module_list'] = [ + '#type' => 'table', + '#header' => [ + $this->t('Source'), + $this->t('Destination'), + ], + ]; + $missing_count = 0; + ksort($unmigrated_source_modules); + foreach ($unmigrated_source_modules as $source_module => $module_data) { + if ($module_data['status']) { + $missing_count++; + $form['missing_module_list'][$source_module] = [ + 'source_module' => ['#plain_text' => $source_module], + 'destination_module' => ['#plain_text' => 'Missing'], + ]; + } + } + // Available migrations. + $form['available_module_list'] = [ + '#tree' => TRUE, + '#type' => 'details', + '#title' => $this->t('Available upgrade paths'), + ]; + + $form['available_module_list']['module_list'] = [ + '#type' => 'table', + '#header' => [ + $this->t('Source'), + $this->t('Destination'), + ], + ]; + + $available_count = 0; + foreach ($table_data as $source_module => $destination_module_info) { + $available_count++; + $destination_details = []; + foreach ($destination_module_info as $destination_module => $migration_ids) { + $destination_details[$destination_module] = [ + '#type' => 'item', + '#plain_text' => $destination_module, + ]; + } + $form['available_module_list']['module_list'][$source_module] = [ + 'source_module' => ['#plain_text' => $source_module], + 'destination_module' => $destination_details, + ]; + } + $form['counts'] = [ + '#type' => 'item', + '#title' => '
                          • ' . $this->t('@count available upgrade paths', ['@count' => $available_count]) . '
                          • ' . $this->t('@count missing upgrade paths', ['@count' => $missing_count]) . '
                          ', + '#weight' => -15, + ]; + + return $form; + } + + /** + * Submission handler for the confirmation form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function submitConfirmForm(array &$form, FormStateInterface $form_state) { + $storage = $form_state->getStorage(); + + $migrations = $storage['migrations']; + $config['source_base_path'] = $storage['source_base_path']; + $batch = [ + 'title' => $this->t('Running upgrade'), + 'progress_message' => '', + 'operations' => [ + [ + [MigrateUpgradeRunBatch::class, 'run'], + [array_keys($migrations), 'import', $config], + ], + ], + 'finished' => [ + MigrateUpgradeRunBatch::class, + 'finished', + ], + ]; + batch_set($batch); + $form_state->setRedirect(''); + $this->state->set('migrate_drupal_ui.performed', REQUEST_TIME); + } + + /** + * Returns all supported database driver installer objects. + * + * @return \Drupal\Core\Database\Install\Tasks[] + * An array of available database driver installer objects. + */ + protected function getDatabaseTypes() { + // Make sure the install API is available. + include_once DRUPAL_ROOT . '/core/includes/install.inc'; + return drupal_get_database_types(); + } + + /** + * {@inheritdoc} + */ + public function getQuestion() { + return $this->t('Are you sure?'); + } + + /** + * {@inheritdoc} + */ + public function getCancelUrl() { + return new Url('migrate_drupal_ui.upgrade'); + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return $this->t('

                          Upgrade analysis report

                          '); + } + + /** + * {@inheritdoc} + */ + public function getConfirmText() { + return $this->t('Perform upgrade'); + } + +} diff --git a/core/modules/migrate_drupal_ui/src/MigrateAccessCheck.php b/core/modules/migrate_drupal_ui/src/MigrateAccessCheck.php new file mode 100644 index 0000000..e66fad7 --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/MigrateAccessCheck.php @@ -0,0 +1,40 @@ +id() === 1)->mergeCacheMaxAge(0); + } + +} diff --git a/core/modules/migrate_drupal_ui/src/MigrateMessageCapture.php b/core/modules/migrate_drupal_ui/src/MigrateMessageCapture.php new file mode 100644 index 0000000..9e220f1 --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/MigrateMessageCapture.php @@ -0,0 +1,48 @@ +messages[] = $message; + } + + /** + * Clears out any captured messages. + */ + public function clear() { + $this->messages = []; + } + + /** + * Returns any captured messages. + * + * @return array + * The captured messages. + */ + public function getMessages() { + return $this->messages; + } + +} diff --git a/core/modules/migrate_drupal_ui/src/MigrateUpgradeRunBatch.php b/core/modules/migrate_drupal_ui/src/MigrateUpgradeRunBatch.php new file mode 100644 index 0000000..2ce971b --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/MigrateUpgradeRunBatch.php @@ -0,0 +1,355 @@ +addListener(MigrateEvents::POST_ROW_SAVE, [static::class, 'onPostRowSave']); + $event_dispatcher->addListener(MigrateEvents::MAP_SAVE, [static::class, 'onMapSave']); + $event_dispatcher->addListener(MigrateEvents::IDMAP_MESSAGE, [static::class, 'onIdMapMessage']); + } + static::$maxExecTime = ini_get('max_execution_time'); + if (static::$maxExecTime <= 0) { + static::$maxExecTime = 60; + } + // Set an arbitrary threshold of 3 seconds (e.g., if max_execution_time is + // 45 seconds, we will quit at 42 seconds so a slow item or cleanup + // overhead don't put us over 45). + static::$maxExecTime -= 3; + static::$listenersAdded = TRUE; + } + if (!isset($context['sandbox']['migration_ids'])) { + $context['sandbox']['max'] = count($initial_ids); + $context['sandbox']['current'] = 1; + // Total number processed for this migration. + $context['sandbox']['num_processed'] = 0; + // migration_ids will be the list of IDs remaining to run. + $context['sandbox']['migration_ids'] = $initial_ids; + $context['sandbox']['messages'] = []; + $context['results']['failures'] = 0; + $context['results']['successes'] = 0; + $context['results']['operation'] = $operation; + } + + // Number processed in this batch. + static::$numProcessed = 0; + + $migration_id = reset($context['sandbox']['migration_ids']); + /** @var \Drupal\migrate\Plugin\Migration $migration */ + $migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_id); + + // @TODO, remove this in https://www.drupal.org/node/2681869. + $destination = $migration->getDestinationConfiguration(); + if ($destination['plugin'] === 'entity:file') { + // Make sure we have a single trailing slash. + $source_base_path = rtrim($config['source_base_path'], '/') . '/'; + $destination['source_base_path'] = $source_base_path; + $migration->set('destination', $destination); + } + + if ($migration) { + static::$messages = new MigrateMessageCapture(); + $executable = new MigrateExecutable($migration, static::$messages); + + $migration_name = $migration->label() ? $migration->label() : $migration_id; + + try { + if ($operation == 'import') { + $migration_status = $executable->import(); + } + } + catch (\Exception $e) { + static::logger()->error($e->getMessage()); + $migration_status = MigrationInterface::RESULT_FAILED; + } + + switch ($migration_status) { + case MigrationInterface::RESULT_COMPLETED: + // Store the number processed in the sandbox. + $context['sandbox']['num_processed'] += static::$numProcessed; + if ($operation == 'import') { + $message = static::getTranslation()->formatPlural( + $context['sandbox']['num_processed'], 'Upgraded @migration (processed 1 item total)', 'Upgraded @migration (processed @num_processed items total)', + ['@migration' => $migration_name, '@num_processed' => $context['sandbox']['num_processed']]); + } + $context['sandbox']['messages'][] = $message; + static::logger()->notice($message); + $context['sandbox']['num_processed'] = 0; + $context['results']['successes']++; + break; + + case MigrationInterface::RESULT_INCOMPLETE: + $context['sandbox']['messages'][] = static::getTranslation()->formatPlural( + static::$numProcessed, 'Continuing with @migration (processed 1 item)', 'Continuing with @migration (processed @num_processed items)', + ['@migration' => $migration_name, '@num_processed' => static::$numProcessed]); + $context['sandbox']['num_processed'] += static::$numProcessed; + break; + + case MigrationInterface::RESULT_STOPPED: + $context['sandbox']['messages'][] = t('Operation stopped by request'); + break; + + case MigrationInterface::RESULT_FAILED: + $context['sandbox']['messages'][] = t('Operation on @migration failed', ['@migration' => $migration_name]); + $context['results']['failures']++; + static::logger()->error('Operation on @migration failed', ['@migration' => $migration_name]); + break; + + case MigrationInterface::RESULT_SKIPPED: + $context['sandbox']['messages'][] = t('Operation on @migration skipped due to unfulfilled dependencies', ['@migration' => $migration_name]); + static::logger()->error('Operation on @migration skipped due to unfulfilled dependencies', ['@migration' => $migration_name]); + break; + + case MigrationInterface::RESULT_DISABLED: + // Skip silently if disabled. + break; + } + + // Unless we're continuing on with this migration, take it off the list. + if ($migration_status != MigrationInterface::RESULT_INCOMPLETE) { + array_shift($context['sandbox']['migration_ids']); + $context['sandbox']['current']++; + } + + // Add and log any captured messages. + foreach (static::$messages->getMessages() as $message) { + $context['sandbox']['messages'][] = $message; + static::logger()->error($message); + } + + // Only display the last MESSAGE_LENGTH messages, in reverse order. + $message_count = count($context['sandbox']['messages']); + $context['message'] = ''; + for ($index = max(0, $message_count - self::MESSAGE_LENGTH); $index < $message_count; $index++) { + $context['message'] = $context['sandbox']['messages'][$index] . "
                          \n" . $context['message']; + } + if ($message_count > self::MESSAGE_LENGTH) { + // Indicate there are earlier messages not displayed. + $context['message'] .= '…'; + } + // At the top of the list, display the next one (which will be the one + // that is running while this message is visible). + if (!empty($context['sandbox']['migration_ids'])) { + $migration_id = reset($context['sandbox']['migration_ids']); + $migration = \Drupal::service('plugin.manager.migration')->createInstance($migration_id); + $migration_name = $migration->label() ? $migration->label() : $migration_id; + if ($operation == 'import') { + $context['message'] = t('Currently upgrading @migration (@current of @max total tasks)', [ + '@migration' => $migration_name, + '@current' => $context['sandbox']['current'], + '@max' => $context['sandbox']['max'], + ]) . "
                          \n" . $context['message']; + } + } + } + else { + array_shift($context['sandbox']['migration_ids']); + $context['sandbox']['current']++; + } + + $context['finished'] = 1 - count($context['sandbox']['migration_ids']) / $context['sandbox']['max']; + } + + /** + * Returns the logger using the migrate_drupal_ui channel. + * + * @return \Psr\Log\LoggerInterface + * The logger instance. + */ + protected static function logger() { + return \Drupal::logger('migrate_drupal_ui'); + } + + /** + * Wraps the translation manager. + * + * @return \Drupal\Core\StringTranslation\TranslationManager + * The string translation manager. + */ + protected static function getTranslation() { + return \Drupal::translation(); + } + + /** + * Implements the Batch API finished method. + */ + public static function finished($success, $results, $operations, $elapsed) { + static::displayResults($results); + } + + /** + * Displays counts of success/failures on the migration upgrade complete page. + * + * @param array $results + * An array of result data built during the batch. + */ + protected static function displayResults($results) { + $successes = $results['successes']; + $failures = $results['failures']; + + // If we had any successes log that for the user. + if ($successes > 0) { + if ($results['operation'] == 'import') { + drupal_set_message(static::getTranslation()->formatPlural($successes, 'Completed 1 upgrade task successfully', 'Completed @count upgrade tasks successfully')); + } + } + + // If we had failures, log them and show the migration failed. + if ($failures > 0) { + if ($results['operation'] == 'import') { + drupal_set_message(static::getTranslation()->formatPlural($failures, '1 upgrade failed', '@count upgrades failed')); + drupal_set_message(t('Upgrade process not completed'), 'error'); + } + } + else { + if ($results['operation'] == 'import') { + // Everything went off without a hitch. We may not have had successes + // but we didn't have failures so this is fine. + drupal_set_message(t('Congratulations, you upgraded Drupal!')); + } + } + + if (\Drupal::moduleHandler()->moduleExists('dblog')) { + $url = Url::fromRoute('migrate_drupal_ui.log'); + drupal_set_message(Link::fromTextAndUrl(t('Review the detailed upgrade log'), $url), $failures ? 'error' : 'status'); + } + } + + /** + * Reacts to item import. + * + * @param \Drupal\migrate\Event\MigratePostRowSaveEvent $event + * The post-save event. + */ + public static function onPostRowSave(MigratePostRowSaveEvent $event) { + // We want to interrupt this batch and start a fresh one. + if ((time() - REQUEST_TIME) > static::$maxExecTime) { + $event->getMigration()->interruptMigration(MigrationInterface::RESULT_INCOMPLETE); + } + } + + /** + * Reacts to item deletion. + * + * @param \Drupal\migrate\Event\MigrateRowDeleteEvent $event + * The post-save event. + */ + public static function onPostRowDelete(MigrateRowDeleteEvent $event) { + // We want to interrupt this batch and start a fresh one. + if ((time() - REQUEST_TIME) > static::$maxExecTime) { + $event->getMigration()->interruptMigration(MigrationInterface::RESULT_INCOMPLETE); + } + } + + /** + * Counts up any map save events. + * + * @param \Drupal\migrate\Event\MigrateMapSaveEvent $event + * The map event. + */ + public static function onMapSave(MigrateMapSaveEvent $event) { + static::$numProcessed++; + } + + /** + * Counts up any map delete events. + * + * @param \Drupal\migrate\Event\MigrateMapDeleteEvent $event + * The map event. + */ + public static function onMapDelete(MigrateMapDeleteEvent $event) { + static::$numProcessed++; + } + + /** + * Displays any messages being logged to the ID map. + * + * @param \Drupal\migrate\Event\MigrateIdMapMessageEvent $event + * The message event. + */ + public static function onIdMapMessage(MigrateIdMapMessageEvent $event) { + if ($event->getLevel() == MigrationInterface::MESSAGE_NOTICE || $event->getLevel() == MigrationInterface::MESSAGE_INFORMATIONAL) { + $type = 'status'; + } + else { + $type = 'error'; + } + $source_id_string = implode(',', $event->getSourceIdValues()); + $message = t('Source ID @source_id: @message', ['@source_id' => $source_id_string, '@message' => $event->getMessage()]); + static::$messages->display($message, $type); + } + +} diff --git a/core/modules/migrate_drupal_ui/src/Tests/MigrateAccessTest.php b/core/modules/migrate_drupal_ui/src/Tests/MigrateAccessTest.php new file mode 100644 index 0000000..88fe0bc --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Tests/MigrateAccessTest.php @@ -0,0 +1,42 @@ +drupalLogin($this->rootUser); + $this->drupalGet('upgrade'); + $this->assertResponse(200); + $this->assertText(t('Drupal Upgrade')); + + $user = $this->createUser(['administer software updates']); + $this->drupalLogin($user); + $this->drupalGet('upgrade'); + $this->assertResponse(403); + $this->assertNoText(t('Drupal Upgrade')); + } + +} diff --git a/core/modules/migrate_drupal_ui/src/Tests/MigrateUpgradeTestBase.php b/core/modules/migrate_drupal_ui/src/Tests/MigrateUpgradeTestBase.php new file mode 100644 index 0000000..583dd10 --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Tests/MigrateUpgradeTestBase.php @@ -0,0 +1,200 @@ +createMigrationConnection(); + $this->sourceDatabase = Database::getConnection('default', 'migrate_drupal_ui'); + + // Log in as user 1. Migrations in the UI can only be performed as user 1. + $this->drupalLogin($this->rootUser); + } + + /** + * Loads a database fixture into the source database connection. + * + * @param string $path + * Path to the dump file. + */ + protected function loadFixture($path) { + $default_db = Database::getConnection()->getKey(); + Database::setActiveConnection($this->sourceDatabase->getKey()); + + if (substr($path, -3) == '.gz') { + $path = 'compress.zlib://' . $path; + } + require $path; + + Database::setActiveConnection($default_db); + } + + /** + * Changes the database connection to the prefixed one. + * + * @todo Remove when we don't use global. https://www.drupal.org/node/2552791 + */ + protected function createMigrationConnection() { + $connection_info = Database::getConnectionInfo('default')['default']; + if ($connection_info['driver'] === 'sqlite') { + // Create database file in the test site's public file directory so that + // \Drupal\simpletest\TestBase::restoreEnvironment() will delete this once + // the test is complete. + $file = $this->publicFilesDirectory . '/' . $this->testId . '-migrate.db.sqlite'; + touch($file); + $connection_info['database'] = $file; + $connection_info['prefix'] = ''; + } + else { + $prefix = is_array($connection_info['prefix']) ? $connection_info['prefix']['default'] : $connection_info['prefix']; + // Simpletest uses fixed length prefixes. Create a new prefix for the + // source database. Adding to the end of the prefix ensures that + // \Drupal\simpletest\TestBase::restoreEnvironment() will remove the + // additional tables. + $connection_info['prefix'] = $prefix . '0'; + } + + Database::addConnectionInfo('migrate_drupal_ui', 'default', $connection_info); + } + + /** + * {@inheritdoc} + */ + protected function tearDown() { + Database::removeConnection('migrate_drupal_ui'); + parent::tearDown(); + } + + /** + * Executes all steps of migrations upgrade. + */ + protected function testMigrateUpgrade() { + $connection_options = $this->sourceDatabase->getConnectionOptions(); + $this->drupalGet('/upgrade'); + $this->assertText('Upgrade a Drupal site by importing it into a clean and empty new install of Drupal 8. You will lose any existing configuration once you import your site into it. See the upgrading handbook for more detailed information.'); + + $this->drupalPostForm(NULL, [], t('Continue')); + $this->assertText('Provide credentials for the database of the Drupal site you want to upgrade.'); + $this->assertFieldByName('mysql[host]'); + + $driver = $connection_options['driver']; + $connection_options['prefix'] = $connection_options['prefix']['default']; + + // Use the driver connection form to get the correct options out of the + // database settings. This supports all of the databases we test against. + $drivers = drupal_get_database_types(); + $form = $drivers[$driver]->getFormOptions($connection_options); + $connection_options = array_intersect_key($connection_options, $form + $form['advanced_options']); + $edit = [ + $driver => $connection_options, + 'source_base_path' => $this->getSourceBasePath(), + ]; + if (count($drivers) !== 1) { + $edit['driver'] = $driver; + } + $edits = $this->translatePostValues($edit); + + // Ensure submitting the form with invalid database credentials gives us a + // nice warning. + $this->drupalPostForm(NULL, [$driver . '[database]' => 'wrong'] + $edits, t('Review upgrade')); + $this->assertText('Resolve the issue below to continue the upgrade.'); + + $this->drupalPostForm(NULL, $edits, t('Review upgrade')); + $this->assertResponse(200); + $this->assertText('Are you sure?'); + $this->drupalPostForm(NULL, [], t('Perform upgrade')); + $this->assertText(t('Congratulations, you upgraded Drupal!')); + + // Have to reset all the statics after migration to ensure entities are + // loadable. + $this->resetAll(); + + $expected_counts = $this->getEntityCounts(); + foreach (array_keys(\Drupal::entityTypeManager()->getDefinitions()) as $entity_type) { + $real_count = count(\Drupal::entityTypeManager()->getStorage($entity_type)->loadMultiple()); + $expected_count = isset($expected_counts[$entity_type]) ? $expected_counts[$entity_type] : 0; + $this->assertEqual($expected_count, $real_count, "Found $real_count $entity_type entities, expected $expected_count."); + } + + $version_tag = 'Drupal ' . $this->getLegacyDrupalVersion($this->sourceDatabase); + $plugin_manager = \Drupal::service('plugin.manager.migration'); + /** @var \Drupal\migrate\Plugin\Migration[] $all_migrations */ + $all_migrations = $plugin_manager->createInstancesByTag($version_tag); + foreach ($all_migrations as $migration) { + $id_map = $migration->getIdMap(); + foreach ($id_map as $source_id => $map) { + // Convert $source_id into a keyless array so that + // \Drupal\migrate\Plugin\migrate\id_map\Sql::getSourceHash() works as + // expected. + $source_id_values = array_values(unserialize($source_id)); + $row = $id_map->getRowBySource($source_id_values); + $destination = serialize($id_map->currentDestination()); + $message = "Successful migration of $source_id to $destination as part of the {$migration->id()} migration. The source row status is " . $row['source_row_status']; + // A completed migration should have maps with + // MigrateIdMapInterface::STATUS_IGNORED or + // MigrateIdMapInterface::STATUS_IMPORTED. + if ($row['source_row_status'] == MigrateIdMapInterface::STATUS_FAILED || $row['source_row_status'] == MigrateIdMapInterface::STATUS_NEEDS_UPDATE) { + $this->fail($message); + } + else { + $this->pass($message); + } + } + } + } + + /** + * Gets the source base path for the concrete test. + * + * @return string + * The source base path. + */ + abstract protected function getSourceBasePath(); + + /** + * Gets the expected number of entities per entity type after migration. + * + * @return int[] + * An array of expected counts keyed by entity type ID. + */ + abstract protected function getEntityCounts(); + +} diff --git a/core/modules/migrate_drupal_ui/src/Tests/d6/MigrateUpgrade6Test.php b/core/modules/migrate_drupal_ui/src/Tests/d6/MigrateUpgrade6Test.php new file mode 100644 index 0000000..a7e0e8a --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Tests/d6/MigrateUpgrade6Test.php @@ -0,0 +1,78 @@ +loadFixture(drupal_get_path('module', 'migrate_drupal') . '/tests/fixtures/drupal6.php'); + } + + /** + * {@inheritdoc} + */ + protected function getSourceBasePath() { + return __DIR__ . '/files'; + } + + /** + * {@inheritdoc} + */ + protected function getEntityCounts() { + return [ + 'block' => 30, + 'block_content' => 2, + 'block_content_type' => 1, + 'comment' => 3, + 'comment_type' => 2, + 'contact_form' => 5, + 'editor' => 2, + 'field_config' => 62, + 'field_storage_config' => 43, + 'file' => 7, + 'filter_format' => 8, + 'image_style' => 5, + 'migration' => 105, + 'node' => 9, + 'node_type' => 11, + 'rdf_mapping' => 5, + 'search_page' => 2, + 'shortcut' => 2, + 'shortcut_set' => 1, + 'action' => 22, + 'menu' => 8, + 'taxonomy_term' => 6, + 'taxonomy_vocabulary' => 6, + 'tour' => 1, + 'user' => 7, + 'user_role' => 6, + 'menu_link_content' => 4, + 'view' => 12, + 'date_format' => 11, + 'entity_form_display' => 15, + 'entity_form_mode' => 1, + 'entity_view_display' => 32, + 'entity_view_mode' => 12, + 'base_field_override' => 33, + ]; + } + +} diff --git a/core/modules/migrate_drupal_ui/src/Tests/d6/files/core/modules/simpletest/files/html-1.txt b/core/modules/migrate_drupal_ui/src/Tests/d6/files/core/modules/simpletest/files/html-1.txt new file mode 100644 index 0000000..494470d --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Tests/d6/files/core/modules/simpletest/files/html-1.txt @@ -0,0 +1 @@ +

                          SimpleTest HTML

                          \ No newline at end of file diff --git a/core/modules/migrate_drupal_ui/src/Tests/d6/files/core/modules/simpletest/files/image-1.png b/core/modules/migrate_drupal_ui/src/Tests/d6/files/core/modules/simpletest/files/image-1.png new file mode 100644 index 0000000..09e64d6 --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Tests/d6/files/core/modules/simpletest/files/image-1.png @@ -0,0 +1,179 @@ +PNG + + IHDRh̡dIDATx^deו&}k{߻Ub%*%REV qذ aܰ fdz P1=b>rr(*'!(> m9c B1a4ˁ%-)CT'% b}gԥX)p9p͡3+a I7 +9"epS3S$%hQyRas$IycaUahZs*+hݥj`K.I@DP FHRqv14#P&BE5Q+̓A 0ALJ`g)\ǒaB;0M|+`QS + QJs9EPA$"Y$TH0%3SJP ɤrRZpQ$4o~@)td,bIo(t7b>\j}805rf(*f2 JP1hL(/cDuK\h %{\j'hU1M+SPB= V+&K迫B>xDBxYzpP8ir4L^Y12s@߯UjEIa(۾4%#X2ԭFcdBS_^Lbx N5hϜxUce{`?mB"W~寞{Y}Ds"k!&E3+%^d@mS"ǽ^ Yr;fI\ngݒ,"\oi?Fa/3/t]5f2G~\~mXcD |7qg6_%K W_#٢ D `|H؜yZ Eac8u@[3xX;Ř`hq*go$ko!|*4alz ضW٫j$sWٳb 4C#8X=лWطnW0Ƕanwα2XnHrsбmls%GxgG9n=pw6G]Zi'GDDs(:=Q]'*i-&+G +@'\S2M8ܽxv~X*xͻN_xC/w2lק냛WMuzO흭O9aBRl?Pl6_h4cO}||u={{)Al:AhgO=/-7C7/O.rX~[۳g\:w4kƉ?ֽc|Y lEYiͫqpx6}>>_lv> Fx(347տKNn}oKv|gu,p:F9 GG?p5#cR4иPEc¡d`&Ƽ1Y@S^@ >#3Gt ir@\FU'6nnlȾ; >b@ k-\=ѩn@ 'Oηn5c߬~问۟g"vO6 yb~n&fq٭;2_v-pVm~;s'A,Bɻ #Q,LB0Irba$o-&ňޘ*Lila&F@z3?p 0ۺ6/]k;u}wGl K?uhǿv#7ډzkzOz;~PVֺ.w](,tmsskU?Y?ql_zWW ,zXz(L*I#U +t#1y!L9 g&U셓Dwh LioAg +"ث2?=qQ.z +ȡ4Wi3] ygtvM.~݃G|9sowlޙ5\n. +(;K5XՕN\3Y6.678?Ls[$xt":( Ll2 + 8 8b 0Z`L|B=ENsiL Ԋ>qeÚcoy[ ޏW(ko?g{{7?ۻԹK7/r{٥gZ޺3pps/7 G#:pG\EZ_;8wim{7]*puf{鲍 +wW~&+"FVS LoweL̥U9&{Qe$z~"E5kSF9lo{쁛?Z7*\TX }[/7f˽/if% +o'eucݨk&P.C[lp_ pw(&8,gLU|+2 /H;#ܧ'ǃ1w$_Ż,T )hm1¬#F/\8uGR_߯f_ls/O|?~߼iM@DIΟxp{sTYok64=v ?sVWA_(@7dK%&*oU +BHzFk 0x`AUncD4Y;n~H4,/\ֿ o>AOO$KWB `a;bvƹ$̱Jb׫ [{cvzty{&]]H2SI!F⩾bO 3u6  sfZL]S(/~Vm`%F/^:ȩm,>2 J.)Lis_ZPVlPl(0~x(CyvzDjs-;n"Ey;ʔ""Yve阓N$<) +>e37sݍRnsFU7sDu٣wrT;?{ok]񶘷Чt&3b<杧w!uᑀ WHYKPfFLBOH)5kz_A_H `Nc@g `szm.:[CS(:M淲_v;w?"*"}#L@Qp*{QyѳW7;F5a/=`}OZ6 B xaݤ0=# 5UnAd;4G4ϖuĤbY]4Nh3@:O;b)c>"TkV6dErK + +d`]%Z@洦﯃F1~l$3p^zw;):)%S|1WnZ?)>ta}/,?SY{ 1di IS %{L˥!羷/s8O qw[;D{A)v\(oiqYMbM4&&@IMb|@:{ WeN=U$E$d}|OԿ>%Q _csOϚ&rdZg$j3бBũQ ++М ޞL 5 NU$v8JkR'#$e LJ :I;vA]Susұ_y:MSt,{QJԿ7')g' u :VI hdCUNtpREdQWG^Lj%0q]&uͶŭMh#(8eU#ڐqgz0 # ,FM IΎSh ^sQɬC$(΂&sALhPn}d+ֱ%CkRS)c^P|JF¿Ahք8 TQ˒\fdǿR*aG +0щORpN2V$0%VV`\#<ꊑ,q^VJƈ;U*]="-RR6_Pv 11R +rň3Hpj`BjR] +64MYN&6fXﲮHyCKiG׻dTc'KOkD)'hIhNO= ) YHͦm43wVHWջM{_/t%d3 ) baY`=8楄0*/<~Ղhh(L{In(%4lSDqkT0Fz(O aL$=mIQ$ۄIrIfxg6afIý[-[2p]T?BT(,vaI9wTo`z=,ٳ4i +2pxoLmHČGqH(`1ZKA݃H%;v#9'ŵ A]Z¹{ۛ ̚A?/U(Z]3tq<#T/ik`>kDd0&NΝ4h%CdMcOʪy^53n5ʃ2p̽N| &c.~d -9Ԧ8Vv~={,V8齝뱧LnLѓ5OS]4O|K&D#靌QaƩ v4I1WKӕBZ <'@;'ؐsd|:94IҊn_\Qç]=h߽[_@n^_1xE8xPLVnˏɅre zVe tfM6K1aCQHṚ9h^Ɣw)\({%ļBF#aݍ.@ $.6iG>wh{O}^Y?e{?J7/J\.J03* ]<|V6w(<\V `,Dw6jl3Bi_|PFќ. +.ӐKf4HnXb!4C Ax F,JG ԛ=oaM79GnW?;[~A +;W>^v^]}(n6Gyf#yTlʌkmf[cX9,pY \/5S?2 +*M]-Ġsp9HK1\8y5fhFI)ܡfu,]##p5):2``e3lw^Ch7Ћk/|­Ƣy6@ܖVVr Igy>U+Vbی)[6A :!{[4)aYѐU„Z±IeGnfbnє dP]ava_kY}#mimfP1]߸2涜cxPH3@}*spΎbXVm\l(|;ΐy-u-z8A4zS{F0FގVI +\aw$"DY 1WdkVb <Ȕg@LKo8ݨ79栅w.wõ4+'fs;'~у]eEɋëC3lqdQV + .^TFF" 7lYMKM$`bq8I&SQO9e1m/V3QϞ}c]kn|B :`A{h.77nO=Y+|"ʈgzkx#3|>\ P"+D!ҤeV`B.B,V.J0Ab"v4332`!)bМ^A=uxiaA#tg=vk觬M|dxEC6v~&(&2XIt׽óTaƃE9"6}R6<Ý[`?f#FQZI;TUaɗ +i}@D*\ K1 )lEE*i,0EZQqPh g~6qYҡյ%Cp%fË͓S ݽ+⅝ºwZY_>z{PWs˭Eu/4s?&lfJiJAfcIsȉF h#c +@lV@Ѓ?[]76XDȚABIjP7g{ZJ:uy贽63ƙM" X"R*pa>b(p -Kt $[ Y!L.dUE\{ja=Kh* (3*E0dMNi1(/OlέWM5i v?y3[{3_4$ǽ7S:6 Κ_΀)4UFkJ2F)F91A4 +)J%4O15`KPa_"Aۙ0 +sUlRemgfTj%B %f.wo=9upۑԕgm9\6l,Z}k_ܙ75h@B#ISU_>:]c~m >}-]dHX\4d9]P₊_}iq]|ϾrE)ýw@_vZJ5uUH4%8G4Q~6@QS: @҈@,#+0͡s_!9O fr D1tjhq^ + >3AVVzcfe>rh__9K]k FܳmwI L6Gr}+9r=O䄓V Wb@>)FIsX`|Y Nbq9ULe˧ Dс~@BV݆jK,,F{;0_{r[ZVk$3E愥_=b.^ +Fi8F0-f!:v +wk[U[$4E99rҔ8 Fr0$}hBWz,NYA ,TF[.6TWu9g25IGJnTF@iv(Sr,Dk۴{k+Rem[b71vtX +hX<ҁT%HPN%0: \7„`a/+fh3J (=$D{.Xkb@s;!kbfCL\=9zlwgۃԆ[z4UǼgBgtkBrtg0w9ӊށg*rĢc+EB ;c!x!ͲdhkT]GAFo7rZKLcqM`pl՛wb`E7{#" @iGFb3i&Q!LMuip܅@%4N +DE;L "z > rۻ]7Mh6q3ӺO/evO10<,jOǯ{ ac^ +@U/?R<-ff3COX)Fs9T3bZz:p˃nzbX5Tmlyrn]a CRhdȃBp o|%dSD'$&vhts_j.4Y U a lbH&b)E蟘B/zn=̈́A DeMy=n>=eNC'2=yܓkZjdf{`gB, Bj/BȆ>dzo1Yp!r*UNP]61IT#'!aash0C|;zj.o}ٟSrL)N~*U5$o5T:r$Î2N +.[ΝE^4Ѥmk=fE5m7ͦ2Y9MLcVz15X jK)UO!ڰZ茋4{ǫgw̭N>wA))#ukU⧉!p ze?M%-LsNXPQ·D`׫Q-tA +Q; j@|/]W1,JkgT!T"D!0c@d;;Rli%ui0Yע6Yk%gTBSa֢g郇}]U.옽\+oEdh[#!B*sX&ۀQ+hq9\vCUL7vw rĕ' <ٿ\ ;/IȀ2  04Dڍ$_MyVϤ#Sۗ?yH||kr8[?bHcjPH(B(JTS +(q'ur)\z1zJ43H>8dTt[TNUћώ_y_ữ#BZWZCɂ1u^Ul@y$ +RbEzhһxi' ۟L|ZG1IH /2?5SI%QXc+q\GɎ&)9gʶʶ0̂ +J :#vڵ.=ޜ^}xe$??|{ SGtJ\q&W`'d$8Zw T +| +k9TQx߼&oN0[[tuVb +l +/~鯢O +d DMCZ: +hM¼e7G$I V\^/.atwnVĢ쭖{g7՟9_|zz~1XFEN엿_3ըZOw֍$tv|} ճ{ {o'@Wp6tݺn.>Wo +$eH) Haj0<#>,Z.#㸳.HYh̢mDbEF~vXtO˽>ܱdPj,p֛WsGK'?*=6e[c sj_v^^S& X?} wo?g DElZT!{_14;7rV[޿?}h?]#E +4}dO '>ԉ*LA}j`:iM}R| 2WdgSt2hƳhǰM\8_ɫkttmu:ksV!ceO>|`YQ}(Gŗx {G/| Q?~=\?  h]\,{o9m?;pL=#ȱ(8X%"U=7)o[`F3fXcUb 95{|4"r=髗y}ř:O_:cRx?>CG,?ytՂG5}Iu|'߿gfl'vx7/s?x +`O595jmKr[0Z]. QVB'_FiT>&Ic)5 +Bn98S`i-xY5&+BFCTRPBcAq~z~/?FӪ:G'@^<ۘ[ֿytmdrJջ#T}]FHL +]L܍^vk.;iWJ"-:8Q S,hW]!3D;CH}'_!G+i (* 7͛$jX N ? fs,%@E# -YvƀaY'k#gٷ75 +>n'O|zW'?{ͬGU@U'lIx,0*;̉,Iim'ib(Gdݜ5U@\AM34MEfkJ/ +݅fb@Prx`lɌ#hF + z(d^XX5Xf=♊e]F{ß~j6b }9n6?UcKUkV9Ұ-@HpMr5#:p k$3(6D4͆jK胠[SaLpBe5Bc$4) 7 j-̄7w|&8 +y~ãN]U!mܽk@"ZVp ɸAn&.J*T$wR/+ʉ(E!!1X9rMԎK@-k/fONO/^Q  )UFe"I=Q{졈 +?m4! rrA+oܾ>8j|9ׇY@4zz?>\,Jz]{pHJ` +iFfűb7mK3_(nƢq@ͬh,˵0zD`B?.O%P~TYQeieQ/h |Eq!%5=7fX=<ק+uj"0W D1͓X{S1=ϺyT;Tz&ܽWk!ݱLՓb˙;{d5QRe[I^ heLcY#kh 'wݯپru5A@ġQY@1qJ#/|(_ykVH}Z7ZaTH -`Ӕ9 |R# +Uxaxڭ'瓗oNB 2D3lJ Ib`b(*>Z{ՏF-`xKA@;4zQq>?9NH*B`V׏?oh!poiaFq%ar~4ZƢ,Oko0w6'G{;'_{].2 KU) MgB"U`QT$2ӫUDjH@TU0p0a:=T4Dl"M(tR5`V?m ?qHuJ +PB_{ EŅN[p`Qs9ڊ.%g~,*dv`Lodu0:&x8ΘeU&%4CD'8p9 3.)kx@ n,y~F Q|ώ;PPcMeaKR ,z&U1 +2 v +EC c1K( 1([TUFD Z?=K^ w_k3yOf"¶V@Ox*NN糛7o\U+~i {UʏxliЖhPGStX脬-3R$=֓Gwf]5~dT{-@gI-1P wF/_{&A +0`?aag#4GQUPspwozVmk/%uxY;KRq AAlҤ֠$j@'!rfS Bg6>ai$F'qya3SpDJ337\mUxqO?17W.}h@y9Mw nW7hB{ADt} vg#vó9n\ xuh[u,^/1X1ӹ\D,PHh7Yqu+m !b<=ckWÇg#V&13nEH*Wodrݏbj݅xˆw?}N@b"*F +4q~[Md<>uw~\@<^DF=fˋ@/7L @(p "p->~6k/`j2]EDIO9?j.Mi& +` +E# &;ޖ@\펯:1uyi&+۵r%gnj@@}=~钀4 \ 7>qFH}Ţ(NAVgq5|>8=̪"v7wlC!ciBBnߡ7l}&' :y}b8kPQɣ_@]5 e,E5 +,&Xxrڦ>iU0f@@@Q'TQ(x~7_arIōڵhd?Ճ9 t$պXlsF ^xkt/-hm2((k7g??|gƓpO{{Mߜb=U=s|Ѳ))j0CK􋌦LADyd^8tm-i-<*siulXu6RZqn.եR„,Ë Oa%l-^WR>īl~?%jaC. y.ivS?폡WLYMn_'?3L޿ŷ>_;^7 +_?ĿY%+~K6 Pt1Febog/KS@og/Ը D )VX_>;8_oνh/d IJVLFhQG|//AVhCxNE=˯L|';1}AN'j `⏙")1KvXG1)F5mҡ_Pa"+:PRep΄էmpWͷZ\^u#ŔC"$'w#@U] ⪻_m +lPЋ8 +vz\ha(t:yt1Yz +0#F'> r\U}Cڈ1u\T;{gߙt 4{g|G&>.t'&Po'H@JSFWtgZ3=/VW[.+PwL]gpޙ<ڠ൯.ewقPԅA =~p;|jՃ{|K]zaVs{tL&Y W&s{B$0KS Pg;/?|BLjC"̏£"j'J%/\PAcٍQ%;hsT' FQSfhgw6-޿ە`PtS #dۣɭo~1S<ŋj@KQ#UA/Q3 > +8Y 3ȤWF⠈?R"lv4^?)w'ߺX. 7T Efj +I ? !o9 +Zum IEq+u?~EZq-;bU J >= U7&7uPDԆw +jkPD;7{}ɧW='=& xœҠ L4$c)`H+G& Q׿LCC&7$gPL\jQA ,P1%R,$M$՚;WTmv,fn6L/;WNBT,~Ӄͷ +АVE ظh3VBAN1k?{"{׮_^<4OϥBʊG@T?EYaȈUVP`ct}nA6߫Qe5Fi(G<O4AJd;ë͋SEhƃf9LU@f >m+C?1-6R +AU+4(G ˽,S蟝3P3?{aJyG @`iP?%%%R0RLg(/wsPk!`w[-Q,y4!V8TB!"pgW}\YR3Vkmn4nQ &y&r͝[Td :\Б4̂jY z7mM˝㓵8ޗDI:j]FHT :1y#% "Ũom ^ƤUVQ(";ؙb6r$Ҋd|.v4&;J83B?i-jsuu֣QaR0FD%4W_LW{apP + y}~Ui[ ,_MgͽEɳkT"$EJ dHEi{^\UH[§[ uCl +bch".lVy pݬni'f%G]<6_~U&`1OFSo}甙k!ގ*  e`+=GpfM&G 2S}abL>e>]Z(>l hY ayE5&(f"~5ɔzw F׭T5eWvٙ-9~utF^HPffoA`'kQk薵A^vuM`mo +[ +A;snO'ǭqRF̄q#BQ")I^$()A ƀJ":b@-C-"9R %͒(ukƻ44 /Q=mYt~Wo3 +j(Av6JUԃ,{dԈsDJfc'3=7 zz!Lժ4MrمuDP\HJBC*JG&+:.)flCADD` E˂VU> KTryB(#Cvl/ P=&;2赋߹Xgctm xbETڱ\_ zPUvG8u<œ56D`خk5p!gr=% +k4)*ªZ\:ADg%Cc X\3ZD1skqqs I#[H@UI;V@ɳ{Zՠ Ԑ4 mI+]WH݅1xRlO0۫7F]mVmA:B u8mZz 2* @0|zrLZD! w쬘Fmѕ(呭W`~ Qy5Ǟҩ_w4^}bUL*Da`,B`ܪ&PQlB4PggVYC@&Ox1zhh= scu =4A LCm9}o +iei{IdM\HYX#4@RɺlKYX4̴mK2_@5vWm+ʻ1/.0C,ر*|jjݸB$¨'+"*T{tQ=3^=^C3y󏖈 HpU+gIW4'%g{[t63_A!Bf9tPVŲ~#歌<`$[8wҭwn,B'TPHq^s7_lFZά"@$ňհG^,_&k89j.-$=(PLKRpK! +)rR;o=H$.KS +B$N[[0"RL&Ųb.QS\䔗0s"~kKНNo-|}ҥaWȨő7𿻶XOioT=."z*W^;K8Y/_nFFe[_^QeFgJ H`^Xv{Q”:\<3DNd%ET-`1ҭ 7_ÞpLX^MW4'<Mۺl{PBꩶ~}<6О^uo6O_f֯cUJZY?&sEUHw1* + zr)PfU,XRgHjA%FhɄ?9I[&#wȺXo虭T Af+\-@^g'6c +ī5>H+6Ԯ:G >[YMRYVE%^هQ*0"`ޠPмt67gkh9,´1d080U!Pl_al nb݆ԛ1 r򲿚 lزctZ5'3cj}4vU181*^'==hLQ >LF{cc ‚2PY"'Je'RJdzt)NTSW=jɾ35 if'A_Z VpҪV7v\R_O/'{kAfEO(- f=n\nCzX6 +gpգ ^Ӵ 0bb| " r9GAHnM"[Rr!)=ƍQm?yxg2H%6Ⱗ5%HqiJ9nvWM6<:Ͳ~8b A}_7+t@Bh[NQs(u0ƑY5zs/έ57wzPm%TWpلеWkY ރ+[`ս87';d">)*M4 k[rq#XRN UVYd))"& \"+ՔJg}n EQe弞@B*2kja:F;'+o}+%' nUN[#8]Es{rՕf(|t&* TU(p3#l,řdX:?sJ$LIlJ5r:Jsk>(@E(Rx})<9?~t4'4{_NƜKJ73uv/W_÷q;B*HHiX_w8ʚ!ˡx,N\8 ~BXce5pm"VܡOj +`ȸBy媢T*]!6 j9M|? `Bsz/;oH\}`=w%U]|9w:ju޻(N,d<cu|R8hV!G,|$̒:|/J +TcO#w/E 9Y "_ 0Ä H"v@a,[YLO_ׯ3!mɗ㦗wF{ߣ/_{j~U-j`p7fwsnV9`-7lS._jEƊNT],OANs_B5(%C4)xJj~'M[eX嘻z'`" +1nezl=?rly5;ܜ3L2 >ql 8\ٚOƲ9"gX4?!>*2$U" X=ftɛԤ|c%]4ZPh +L&gEP)2-Z4ޫ7|<%QHRm,];{4iZnV TG +] V*xK@j.Cѓ >Xv{i `JbNuo&OrˊnH3.aƵB Qwчxgo$hbL "jw׼?f HiKe!*=<=L!jPQԽiPMО8lL->? 8[ :H%܊!*ixonP\l;5(IH”fRPD$&- +(H p 29[o&nP6z/J}a뀎 d!{ٙ\]Æ|5jR@厫+닇: a-gkKc͇[iKxIe:I@ @ ի@ *, $skJmg QIYq" XI-BCFh׈#*12AP+ "ηph6C1Aj +0UR 5wR)Z0ҥ+ѪXA3 +Jbxޒ`2"X0 ȈȢAD iJĆ3a_!wP`<߬foZq0􏟝N>K`Z#bVB Hi(ki#nҸ}~/A4 E+h8~UDfj:7*r["xQˈ@2{kd)"dY@hǒ5! 3U'ԯ6AR{2Nx1^rX!)CRA@>orq]&Rr{R:h!Vw☾'Op.V fQ1ΑAÑ5YEQl2FwgX}EQĵsTs4OZOF\N>bU*5,Ϫj< 8|mT+%t hdlۀ &)0Ujvфw3^v.van;FݺOF +|wbfdE~e(PYZ‹m,l +)ǻ-b& @Jq "P\,… OJV,NJ` +XbIIAr50[>j`[/7x,ȾD.XCkl k0@uP'e6ͮ U]IVFFl†DÓT'w]kio69H2A$S: & +i Ows~2"I Ehj(p v#>z dw0Txw +ON;@)/ a.\Es*7J=ijn"bO0I\|^:E!RUjҹCDP-@Q` +$P +`Ռ`:zV߁Mo`\Ѵ>9:]Z1`@9N'M%eBgZJ%l@* i/K듐(Z( |+cGS+aU"0yVPUOܣPy*\z߻==;yCPn߬m^E*"qLaԭ^]ƜX{TȈ81/DMٹ o|8ҁi FNvkndQGtc:\ޘL؉3ٓϮD-p)&E(a-b&qyP9LDjD3_ E8>Wra]y< SQ>hu}ˑŌ! 1퍯OfgU= ,^ +]hqkπ*ˤ=?\=k"gِe3:gN$DBYnK0qIJM/C~.+ tԸ) 5'@<bn,i?;ݩ[zB\r籮l٘UΝ{G4A9.UQ$gQԭW觀B =dQZ*aNÊJ{ђX +>-iJM\TMHD+AsBD3Qd#ޫH% `E`PBK& #M + +A,{WIb$.sI7D2E|ڪ[pTL3âty1&o +lJ6mЎ#qVĞ/r#z>h#U},3x`dgDZ,*Ih";~ +D"6U%&,20@2&% +Voک0:`/76dҞٷ9j, +z-o$F$t 8+RAӣTak{WlK' 8*RA!n#- +4FaQ,,EK7,t4|Ҝs|tA-%_7o^#2,a0VP;#o.W߬5%3Jk SZLh ^AA ]PMN=g"U^J̩5TIӍg" 3pS+mG>ZE$G<|ыL*8vc +~d%w]XZdu>7w2|UEP-A_48Fq4>Z g5WKO)$B4.@](~)QĻIiNw->vwgSEP2ȪPDHV{JL** VrKbBϓ{B 8,6# a%K|(D)]|,;+n{M1dVEYl{v}O:D!L96Dy4Iش``}A 6 nsdz$an #&LZ* P'IE$J [4KcZe`C 3VWy>ͶKݺ2pTЁ=èaey1 WL@j+VOcsA:qj:#4M&zUЀBO{\ׄBl#>nOg{[Q*' J9c"F_ RM실Lel SM~CUi@$Ͷ}$PA#H䲒Ox.àf@"!c@ԯt<!t}ZqV$!,$Lӈ)4üFY M'@ԟѪ%ovY;3SO4OAGcyJde=5ElBTڝsL *C$#xi+$I%vvf 33+L 4,@ؚl%HĪlNS4jj rrDn[⑓11V>]@F + +Ou[qhUrD;茎 `S yX<18@JҎ^(=GXEcʴiݟ3<2)b3#rA5.D`o%&{*"o`F+AhpBK V=4c^:T[A>H׊II ۺn= Cc).F4˫Nh(5>/xgVImL(o " P)tbMHK;IW;R@ JG` 3g\ ImֹM?Ā!ĤWd('n-G|?y2A 3ʢ3I =MTk9 cV 4ޤZY(I",oy"f +ܲP,~+gNIn߷qCu3\(wa$QiЯضJC//qөt0kUz)1<O{J D:oxQPtUUsҥz'͠05C;2<1P, dBI)Y FOtw ~}%scfq- 0JCtLka j^ފ6%Vds<TC)6h%9PJސ0BJK#{,7^_I: jK816N2Ttr +²~əV_̚B +Ń njWk>˧֬ Ce qQJMqcY1JTma5r0bskGbfTe⺐>2pEsD,RR ]se]""᥽"ԓU75ݣZߚau~ٍ{ 952+Tݬf*hF"U/̨f84|nna\x$jd4ӱ܀7v&nPDW¡$l= _s6X-kCޏo\Wk$hHDRfp|(YMuSk늃Ɣ&|Ǻ) @Bc2infR,S,xIJiW DsHnAp &["ZۤBLP +Ȃ;ǗJۣ_F* u*Ktk7qجkLwhmjwB8L%h!F/Dkd|"AY%oh<Ϛ|xI}łd𜴗q,\<[Y(֨8M]w8UY%kt?_V\X4"3@kd"9@dG_$xy+-ƅA 4Iq0e1iL[pPh!rUG?ziBh=P BDunŏ}{9T Jb+tv*v쬂 6BG$P r\<$x=0VXIC#]m+/ټTy'СK*s2|Ud> _>J#*R.D"͊B8ruRz=Ӫk=v0ڹaZa+' ̛ٝ[J.7**j&2#RY+C{4%qJXEdQXi@uΐ_U/YةSE5; ٠-~1H6h@+5Zx~;BC GIhP} !EՒLdePP1DsE$[.RXC齍ځzUCbH64e:ΨwJa(y[aXNGt  +OQxb<4 (VKO H|*99=((ȃ%Z)R:RLȜbWVi{ F4xb APA<[ri8o6Wq4X|IdquX|>2z\c kTHdzmN-:MM3G4JQA[\poqC2iX*Ƃ (nC3 >="EOy2X wek6c*ϕ(T˕b#1+AR^T$웩n";{2[m*UW5Me(BكJb܎L++Rpr51 x悩 ~ +;,s4D4sUߝ]`Uܢ[@l$(/;C=j, 0'<ҹ[cnqpt*jAn3(+_j|nӃTTA C$y#N5  4FŻr-ý;bSC5N* bLՌjv1V@k0Xldۀ0PRݖFH[.7)v#*î*n(`C(&>o؛*4BUd5 i[+3ss8qvCz2 3"x *jfSVų%Xf~^+ !8c9/ٰ%*:>41$ǼdzXѡkܑ?b$"oO:/ u^҄W8L,֣k iFvjucQl "B ]x0H"a:Bed %o"CʔU#IUR%WLi!jG[.Jʇ-B3Q*BU=5<-qf*'0u cwrf\d>Q ԣ-:@lĨX  L"C)ݢ)j(W"1a@fsaM"WW!?(|2 <72}N "Ȗ/ru-}*BL 8ǦN34&,mfoߺ>tGH4 [XXûCߣ!A@= 5E Ť.% 7l1+I82b=矷,OHQ@M:¦W:b:(\rDBL\6/'94_5POaDv555̈́ bH= ZF2)U;s%X#h.UJY"ѢmUs6E[Kv"QףDkh{$Ɯ,IW(/t:a""0%bx $E=v*tgLJxy?[^.\to24l>ؐ +>WkAYB+q'x+R5p8`rEɞ[Ya>Ki1BI$"ͺZ, )A< e&<+ݸyJ zPrG)'/4~LS A:!b\%g8sЬTf`8S Wǂˆ#:!1rOp1  fAwfµr˶䒖Z@Ȏ:۪ᖤB9I\QbMC Yqaq~*1@jϳzheycbe .جZiҸ0 H鷎{?5JH3#r$N@Datq,pF K>2Ű32zAp@iDU. +}xP5Dzs!PsN;km|ƅcs&VI:%92!|F"p]0GjUφF?}ctQ5Tj + ƠU:cQsb]':u0M +pE&NDgf_oN ZTGީif}Q].")̢|Ct t;m}|Έ8)Yi+bhnAd|ƌad</9fR?sK^%BS \rtt5+(x1klLr\YMi +vupA<zXjj'$.1Ч_/uxk֖3n2GQ7 .yzTFrD3 l}B DMw ƚ}[ymQ$QH F7 `ȗX@ӂH3~Q>AJ] + c軳S6?ԺCԿ +tIp:fRA-\UAS$?v2gFq;tnwo zDNVU.Ĝw +}l.~$RV0#n޸37;|7 \ +R+xiKut Edn9x4sOc f|_~GF2TP<[o zx"2]䨅,SSͦgg5–EtIp]#؝0OnUUwo1wG.~l +D %$G'6!2)n]ۈf^gQW8/R@v~vָݚ&vvTXV_fgǴπ{SI{5,ϞJ-Ŝr^rM9<ɳm{ΣJk\Rfa̷LZf d=EA+HYIZp)@bVm:H4YD5)pQjeQɑnHK759iE:\HwL j@n'%`o7 h[ +(ihi-(O-D҅n(ZY 8ߓ ?$f YgyjB$,uwi-0OHF{J߬\e~U?}>q¾c > {CGY[!ن^H:I a"t$͑3l* k뉓\]==k4ҶGt|J V:b@6$dDg<ҳJ4_u3DBK}AMs;24z1SjQ)$r%NW\DڶyjMGл}۟ T~k +7;80܏iDt}L\~Ha$F:\?EF5dmͷMQZ|Z/ ,3U_tBnrKԦEq>VT)ch%4ԕQr힦1,]A;A[%"aUVGWڅQWݍQ%gkиca2ӤL WIj-C--5/-Pޥk੩ո \!13RB0, S@+Fp"C GLĿ6LFa"z }=)$Al!ԝ4dyL 5USYޕUzRPg}595|brI3WY[w'%vvyvIRQ>I4e${zשBSj]/^NLBB3՞i)%9]݄Ya0?c۱ +듽|OF:wSimpleTest HTML \ No newline at end of file diff --git a/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php b/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php new file mode 100644 index 0000000..da48648 --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Tests/d7/MigrateUpgrade7Test.php @@ -0,0 +1,78 @@ +loadFixture(drupal_get_path('module', 'migrate_drupal') . '/tests/fixtures/drupal7.php'); + } + + /** + * {@inheritdoc} + */ + protected function getSourceBasePath() { + return __DIR__ . '/files'; + } + + /** + * {@inheritdoc} + */ + protected function getEntityCounts() { + return [ + 'block' => 25, + 'block_content' => 1, + 'block_content_type' => 1, + 'comment' => 1, + 'comment_type' => 7, + 'contact_form' => 3, + 'editor' => 2, + 'field_config' => 41, + 'field_storage_config' => 31, + 'file' => 1, + 'filter_format' => 7, + 'image_style' => 6, + 'migration' => 59, + 'node' => 2, + 'node_type' => 6, + 'rdf_mapping' => 5, + 'search_page' => 2, + 'shortcut' => 6, + 'shortcut_set' => 2, + 'action' => 18, + 'menu' => 10, + 'taxonomy_term' => 18, + 'taxonomy_vocabulary' => 3, + 'tour' => 1, + 'user' => 3, + 'user_role' => 4, + 'menu_link_content' => 9, + 'view' => 12, + 'date_format' => 11, + 'entity_form_display' => 15, + 'entity_form_mode' => 1, + 'entity_view_display' => 22, + 'entity_view_mode' => 10, + 'base_field_override' => 7, + ]; + } + +} diff --git a/core/modules/migrate_drupal_ui/src/Tests/d7/files/sites/default/files/cube.jpeg b/core/modules/migrate_drupal_ui/src/Tests/d7/files/sites/default/files/cube.jpeg new file mode 100644 index 0000000..652a7db --- /dev/null +++ b/core/modules/migrate_drupal_ui/src/Tests/d7/files/sites/default/files/cube.jpeg @@ -0,0 +1 @@ +******************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** \ No newline at end of file diff --git a/core/modules/node/config/optional/views.view.content.yml b/core/modules/node/config/optional/views.view.content.yml index 526c484..124e900 100644 --- a/core/modules/node/config/optional/views.view.content.yml +++ b/core/modules/node/config/optional/views.view.content.yml @@ -181,7 +181,7 @@ display: relationship: none group_type: group admin_label: '' - label: 'Content Type' + label: 'Content type' exclude: false alter: alter_text: false @@ -421,7 +421,7 @@ display: exposed: true expose: operator_id: type_op - label: Type + label: 'Content type' description: '' use_operator: false operator: type_op diff --git a/core/modules/node/config/schema/node.source.schema.yml b/core/modules/node/config/schema/node.source.schema.yml deleted file mode 100644 index 319072c..0000000 --- a/core/modules/node/config/schema/node.source.schema.yml +++ /dev/null @@ -1,58 +0,0 @@ -migrate.source.d6_view_mode: - type: migrate_source_sql - label: 'Drupal 6 view mode' - mapping: - constants: - type: mapping - label: 'Constants' - mapping: - targetEntityType: - type: string - label: 'Target entity type' - status: - type: boolean - label: 'Status' - -migrate.source.d6_node_type: - type: migrate_source_sql - label: 'Drupal 6 node type' - mapping: - constants: - type: migrate_entity_constant - label: 'Constants' - -migrate.source.d6_node: - type: migrate_source_sql - label: 'Drupal 6 node' - mapping: - node_type: - type: string - label: 'Node type' - -migrate.source.d7_node: - type: migrate_source_sql - label: 'Drupal 7 node' - mapping: - node_type: - type: string - label: 'Node type' - -migrate.source.d6_node_revision: - type: migrate_source_sql - label: 'Drupal 6 node revision' - mapping: - node_type: - type: string - label: 'Node type' - -migrate.source.d7_node_revision: - type: migrate_source_sql - label: 'Drupal 7 node revision' - mapping: - node_type: - type: string - label: 'Node type' - -migrate.source.d7_node_type: - type: migrate_source_sql - label: 'Drupal 7 node type' diff --git a/core/modules/node/content_types.js b/core/modules/node/content_types.js index 96733a2..eed93f0 100644 --- a/core/modules/node/content_types.js +++ b/core/modules/node/content_types.js @@ -3,7 +3,7 @@ * Javascript for the node content editing form. */ -(function ($) { +(function ($, Drupal) { 'use strict'; @@ -59,4 +59,4 @@ } }; -})(jQuery); +})(jQuery, Drupal); diff --git a/core/modules/node/migration_templates/d6_node.yml b/core/modules/node/migration_templates/d6_node.yml index 01642f7..82571b8 100644 --- a/core/modules/node/migration_templates/d6_node.yml +++ b/core/modules/node/migration_templates/d6_node.yml @@ -2,8 +2,7 @@ id: d6_node label: Nodes migration_tags: - Drupal 6 -builder: - plugin: d6_node +deriver: Drupal\node\Plugin\migrate\D6NodeDeriver source: plugin: d6_node process: diff --git a/core/modules/node/migration_templates/d6_node_revision.yml b/core/modules/node/migration_templates/d6_node_revision.yml index e7f3b54..cd32ea0 100644 --- a/core/modules/node/migration_templates/d6_node_revision.yml +++ b/core/modules/node/migration_templates/d6_node_revision.yml @@ -2,8 +2,7 @@ id: d6_node_revision label: Node revisions migration_tags: - Drupal 6 -builder: - plugin: d6_node +deriver: Drupal\node\Plugin\migrate\D6NodeDeriver source: plugin: d6_node_revision process: diff --git a/core/modules/node/migration_templates/d7_node.yml b/core/modules/node/migration_templates/d7_node.yml index a6d3cb8..18e01d1 100644 --- a/core/modules/node/migration_templates/d7_node.yml +++ b/core/modules/node/migration_templates/d7_node.yml @@ -2,8 +2,7 @@ id: d7_node label: Nodes migration_tags: - Drupal 7 -builder: - plugin: d7_node +deriver: Drupal\node\Plugin\migrate\D7NodeDeriver source: plugin: d7_node process: diff --git a/core/modules/node/migration_templates/d7_node_revision.yml b/core/modules/node/migration_templates/d7_node_revision.yml index edb56f3..11f9d0a 100644 --- a/core/modules/node/migration_templates/d7_node_revision.yml +++ b/core/modules/node/migration_templates/d7_node_revision.yml @@ -2,8 +2,7 @@ id: d7_node_revision label: Node revisions migration_tags: - Drupal 7 -builder: - plugin: d7_node +deriver: Drupal\node\Plugin\migrate\D7NodeDeriver source: plugin: d7_node_revision process: @@ -28,4 +27,4 @@ destination: plugin: entity_revision:node migration_dependencies: required: - - d7_node:* + - d7_node diff --git a/core/modules/node/node.admin.inc b/core/modules/node/node.admin.inc index 5566566..49a94d8 100644 --- a/core/modules/node/node.admin.inc +++ b/core/modules/node/node.admin.inc @@ -108,10 +108,10 @@ function _node_mass_update_helper(NodeInterface $node, array $updates, $langcode * @param bool $revisions * (optional) TRUE if $nodes contains an array of revision IDs instead of * node IDs. Defaults to FALSE; will be ignored if $load is FALSE. - * @param array $context + * @param array|\ArrayAccess $context. * An array of contextual key/values. */ -function _node_mass_update_batch_process(array $nodes, array $updates, $load, $revisions, array &$context) { +function _node_mass_update_batch_process(array $nodes, array $updates, $load, $revisions, &$context) { if (!isset($context['sandbox']['progress'])) { $context['sandbox']['progress'] = 0; $context['sandbox']['max'] = count($nodes); diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php index 7be1829..11689c3 100644 --- a/core/modules/node/node.api.php +++ b/core/modules/node/node.api.php @@ -1,15 +1,15 @@ cachePerPermissions(); } else { - return AccessResult::allowedIf($account->hasPermission('edit own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($node); + return AccessResult::allowedIf($account->hasPermission('edit own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->addCacheableDependency($node); } case 'delete': @@ -344,7 +344,7 @@ function hook_node_access(\Drupal\node\NodeInterface $node, $op, \Drupal\Core\Se return AccessResult::allowed()->cachePerPermissions(); } else { - return AccessResult::allowedIf($account->hasPermission('delete own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($node); + return AccessResult::allowedIf($account->hasPermission('delete own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->addCacheableDependency($node); } default: diff --git a/core/modules/node/node.links.menu.yml b/core/modules/node/node.links.menu.yml index 4a85f5e..ae7a9af 100644 --- a/core/modules/node/node.links.menu.yml +++ b/core/modules/node/node.links.menu.yml @@ -1,7 +1,7 @@ entity.node_type.collection: title: 'Content types' parent: system.admin_structure - description: 'Create content types and manage their default settings.' + description: 'Create and manage fields, forms, and display settings for your content.' route_name: entity.node_type.collection node.add_page: title: 'Add content' diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 2a75844..ebfd07f 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -324,12 +324,12 @@ function node_add_body_field(NodeTypeInterface $type, $label = 'Body') { $field_storage = FieldStorageConfig::loadByName('node', 'body'); $field = FieldConfig::loadByName('node', $type->id(), 'body'); if (empty($field)) { - $field = entity_create('field_config', array( + $field = FieldConfig::create([ 'field_storage' => $field_storage, 'bundle' => $type->id(), 'label' => $label, 'settings' => array('display_summary' => TRUE), - )); + ]); $field->save(); // Assign widget settings for the 'default' form mode. @@ -832,6 +832,7 @@ function node_form_system_themes_admin_form_alter(&$form, FormStateInterface $fo $form['admin_theme']['use_admin_theme'] = array( '#type' => 'checkbox', '#title' => t('Use the administration theme when editing or creating content'), + '#description' => t('Control which roles can "View the administration theme" on the Permissions page.', array(':permissions' => Url::fromRoute('user.admin_permissions')->toString())), '#default_value' => \Drupal::configFactory()->getEditable('node.settings')->get('use_admin_theme'), ); $form['#submit'][] = 'node_form_system_themes_admin_form_submit'; @@ -914,7 +915,7 @@ function node_node_access(NodeInterface $node, $op, $account) { return AccessResult::allowed()->cachePerPermissions(); } else { - return AccessResult::allowedIf($account->hasPermission('edit own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($node); + return AccessResult::allowedIf($account->hasPermission('edit own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->addCacheableDependency($node); } case 'delete': @@ -922,7 +923,7 @@ function node_node_access(NodeInterface $node, $op, $account) { return AccessResult::allowed()->cachePerPermissions(); } else { - return AccessResult::allowedIf($account->hasPermission('delete own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($node); + return AccessResult::allowedIf($account->hasPermission('delete own ' . $type . ' content', $account) && ($account->id() == $node->getOwnerId()))->cachePerPermissions()->cachePerUser()->addCacheableDependency($node); } default: @@ -1046,18 +1047,29 @@ function node_query_node_access_alter(AlterableInterface $query) { $tables = $query->getTables(); $base_table = $query->getMetaData('base_table'); - // If the base table is not given, default to node if present. + // If the base table is not given, default to one of the node base tables. if (!$base_table) { + /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ + $table_mapping = \Drupal::entityTypeManager()->getStorage('node')->getTableMapping(); + $node_base_tables = $table_mapping->getTableNames(); + foreach ($tables as $table_info) { if (!($table_info instanceof SelectInterface)) { $table = $table_info['table']; - // If the node table is in the query, it wins immediately. + // Ensure that 'node' and 'node_field_data' are always preferred over + // 'node_revision' and 'node_field_revision'. if ($table == 'node' || $table == 'node_field_data') { $base_table = $table; break; } + // If one of the node base tables are in the query, add it to the list + // of possible base tables to join against. + if (in_array($table, $node_base_tables)) { + $base_table = $table; + } } } + // Bail out if the base table is missing. if (!$base_table) { throw new Exception(t('Query tagged for node access but there is no node table, specify the base_table using meta data.')); @@ -1133,6 +1145,7 @@ function node_access_needs_rebuild($rebuild = NULL) { */ function node_access_rebuild($batch_mode = FALSE) { $node_storage = \Drupal::entityManager()->getStorage('node'); + /** @var \Drupal\node\NodeAccessControlHandlerInterface $access_control_handler */ $access_control_handler = \Drupal::entityManager()->getAccessControlHandler('node'); $access_control_handler->deleteGrants(); // Only recalculate if the site is using a node_access module. @@ -1167,7 +1180,8 @@ function node_access_rebuild($batch_mode = FALSE) { // To preserve database integrity, only write grants if the node // loads successfully. if (!empty($node)) { - $access_control_handler->writeGrants($node); + $grants = $access_control_handler->acquireGrants($node); + \Drupal::service('node.grant_storage')->write($node, $grants); } } } @@ -1222,7 +1236,10 @@ function _node_access_rebuild_batch_operation(&$context) { // To preserve database integrity, only write grants if the node // loads successfully. if (!empty($node)) { - \Drupal::entityManager()->getAccessControlHandler('node')->writeGrants($node); + /** @var \Drupal\node\NodeAccessControlHandlerInterface $access_control_handler */ + $access_control_handler = \Drupal::entityManager()->getAccessControlHandler('node'); + $grants = $access_control_handler->acquireGrants($node); + \Drupal::service('node.grant_storage')->write($node, $grants); } $context['sandbox']['progress']++; $context['sandbox']['current_node'] = $nid; @@ -1275,22 +1292,6 @@ function node_modules_installed($modules) { * Implements hook_modules_uninstalled(). */ function node_modules_uninstalled($modules) { - // Remove module-specific settings from all node types. - $config_names = \Drupal::configFactory()->listAll('node.type.'); - foreach ($config_names as $config_name) { - $config = \Drupal::config($config_name); - $changed = FALSE; - foreach ($modules as $module) { - if ($config->get('settings.' . $module)) { - $config->clear('settings.' . $module); - $changed = TRUE; - } - } - if ($changed) { - $config->save(); - } - } - // Check whether any of the disabled modules implemented hook_node_grants(), // in which case the node access table needs to be rebuilt. foreach ($modules as $module) { diff --git a/core/modules/node/node.views.inc b/core/modules/node/node.views.inc deleted file mode 100644 index e353fc0..0000000 --- a/core/modules/node/node.views.inc +++ /dev/null @@ -1,17 +0,0 @@ -moduleExists('statistics')) { - $plugins['node']['available_sorts']['node_counter-totalcount:DESC'] = t('Number of hits'); - } - -} diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php index 118e862..7b20a3b 100644 --- a/core/modules/node/src/Entity/Node.php +++ b/core/modules/node/src/Entity/Node.php @@ -131,7 +131,10 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) { // default revision. There's no need to delete existing records if the node // is new. if ($this->isDefaultRevision()) { - \Drupal::entityManager()->getAccessControlHandler('node')->writeGrants($this, $update); + /** @var \Drupal\node\NodeAccessControlHandlerInterface $access_control_handler */ + $access_control_handler = \Drupal::entityManager()->getAccessControlHandler('node'); + $grants = $access_control_handler->acquireGrants($this); + \Drupal::service('node.grant_storage')->write($this, $grants, NULL, $update); } // Reindex the node when it is updated. The node is automatically indexed @@ -322,41 +325,7 @@ public function setRevisionAuthorId($uid) { * {@inheritdoc} */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { - $fields['nid'] = BaseFieldDefinition::create('integer') - ->setLabel(t('Node ID')) - ->setDescription(t('The node ID.')) - ->setReadOnly(TRUE) - ->setSetting('unsigned', TRUE); - - $fields['uuid'] = BaseFieldDefinition::create('uuid') - ->setLabel(t('UUID')) - ->setDescription(t('The node UUID.')) - ->setReadOnly(TRUE); - - $fields['vid'] = BaseFieldDefinition::create('integer') - ->setLabel(t('Revision ID')) - ->setDescription(t('The node revision ID.')) - ->setReadOnly(TRUE) - ->setSetting('unsigned', TRUE); - - $fields['type'] = BaseFieldDefinition::create('entity_reference') - ->setLabel(t('Type')) - ->setDescription(t('The node type.')) - ->setSetting('target_type', 'node_type') - ->setReadOnly(TRUE); - - $fields['langcode'] = BaseFieldDefinition::create('language') - ->setLabel(t('Language')) - ->setDescription(t('The node language code.')) - ->setTranslatable(TRUE) - ->setRevisionable(TRUE) - ->setDisplayOptions('view', array( - 'type' => 'hidden', - )) - ->setDisplayOptions('form', array( - 'type' => 'language_select', - 'weight' => 2, - )); + $fields = parent::baseFieldDefinitions($entity_type); $fields['title'] = BaseFieldDefinition::create('string') ->setLabel(t('Title')) diff --git a/core/modules/node/src/Form/NodeRevisionRevertForm.php b/core/modules/node/src/Form/NodeRevisionRevertForm.php index 717cf28..f4899f3 100644 --- a/core/modules/node/src/Form/NodeRevisionRevertForm.php +++ b/core/modules/node/src/Form/NodeRevisionRevertForm.php @@ -7,7 +7,7 @@ namespace Drupal\node\Form; -use Drupal\Core\Datetime\DateFormatter; +use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; @@ -37,7 +37,7 @@ class NodeRevisionRevertForm extends ConfirmFormBase { /** * The date formatter service. * - * @var \Drupal\Core\Datetime\DateFormatter + * @var \Drupal\Core\Datetime\DateFormatterInterface */ protected $dateFormatter; @@ -46,10 +46,10 @@ class NodeRevisionRevertForm extends ConfirmFormBase { * * @param \Drupal\Core\Entity\EntityStorageInterface $node_storage * The node storage. - * @param \Drupal\Core\Datetime\DateFormatter $date_formatter + * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter * The date formatter service. */ - public function __construct(EntityStorageInterface $node_storage, DateFormatter $date_formatter) { + public function __construct(EntityStorageInterface $node_storage, DateFormatterInterface $date_formatter) { $this->nodeStorage = $node_storage; $this->dateFormatter = $date_formatter; } diff --git a/core/modules/node/src/Form/NodeRevisionRevertTranslationForm.php b/core/modules/node/src/Form/NodeRevisionRevertTranslationForm.php index d2c996e..7e8a4eb 100644 --- a/core/modules/node/src/Form/NodeRevisionRevertTranslationForm.php +++ b/core/modules/node/src/Form/NodeRevisionRevertTranslationForm.php @@ -7,7 +7,7 @@ namespace Drupal\node\Form; -use Drupal\Core\Datetime\DateFormatter; +use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageManagerInterface; @@ -38,12 +38,12 @@ class NodeRevisionRevertTranslationForm extends NodeRevisionRevertForm { * * @param \Drupal\Core\Entity\EntityStorageInterface $node_storage * The node storage. - * @param \Drupal\Core\Datetime\DateFormatter $date_formatter + * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter * The date formatter service. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. */ - public function __construct(EntityStorageInterface $node_storage, DateFormatter $date_formatter, LanguageManagerInterface $language_manager) { + public function __construct(EntityStorageInterface $node_storage, DateFormatterInterface $date_formatter, LanguageManagerInterface $language_manager) { parent::__construct($node_storage, $date_formatter); $this->languageManager = $language_manager; } diff --git a/core/modules/node/src/NodeAccessControlHandler.php b/core/modules/node/src/NodeAccessControlHandler.php index 490846f..51ae928 100644 --- a/core/modules/node/src/NodeAccessControlHandler.php +++ b/core/modules/node/src/NodeAccessControlHandler.php @@ -105,7 +105,7 @@ protected function checkAccess(EntityInterface $node, $operation, AccountInterfa // Check if authors can view their own unpublished nodes. if ($operation === 'view' && !$status && $account->hasPermission('view own unpublished content') && $account->isAuthenticated() && $account->id() == $uid) { - return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($node); + return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->addCacheableDependency($node); } // Evaluate node grants. diff --git a/core/modules/node/src/NodeAccessControlHandlerInterface.php b/core/modules/node/src/NodeAccessControlHandlerInterface.php index 9e7c34a..0b629d7 100644 --- a/core/modules/node/src/NodeAccessControlHandlerInterface.php +++ b/core/modules/node/src/NodeAccessControlHandlerInterface.php @@ -35,13 +35,11 @@ public function acquireGrants(NodeInterface $node); /** * Writes a list of grants to the database, deleting any previously saved ones. * - * If a realm is provided, it will only delete grants from that realm, but it - * will always delete a grant from the 'all' realm. Modules that use node - * access can use this function when doing mass updates due to widespread - * permission changes. + * Modules that use node access can use this function when doing mass updates + * due to widespread permission changes. * * Note: Don't call this function directly from a contributed module. Call - * node_access_acquire_grants() instead. + * \Drupal\node\NodeAccessControlHandlerInterface::acquireGrants() instead. * * @param \Drupal\node\NodeInterface $node * The node whose grants are being written. @@ -49,6 +47,9 @@ public function acquireGrants(NodeInterface $node); * (optional) If false, does not delete records. This is only for optimization * purposes, and assumes the caller has already performed a mass delete of * some form. Defaults to TRUE. + * + * @deprecated in Drupal 8.x, will be removed before Drupal 9.0. + * Use \Drupal\node\NodeAccessControlHandlerInterface::acquireGrants(). */ public function writeGrants(NodeInterface $node, $delete = TRUE); diff --git a/core/modules/node/src/NodeGrantDatabaseStorage.php b/core/modules/node/src/NodeGrantDatabaseStorage.php index 58f8540..b71783a 100644 --- a/core/modules/node/src/NodeGrantDatabaseStorage.php +++ b/core/modules/node/src/NodeGrantDatabaseStorage.php @@ -76,7 +76,7 @@ public function access(NodeInterface $node, $operation, AccountInterface $accoun // Return the equivalent of the default grant, defined by // self::writeDefault(). if ($operation === 'view') { - return AccessResult::allowedIf($node->isPublished())->cacheUntilEntityChanges($node); + return AccessResult::allowedIf($node->isPublished())->addCacheableDependency($node); } else { return AccessResult::neutral(); diff --git a/core/modules/node/src/NodeGrantDatabaseStorageInterface.php b/core/modules/node/src/NodeGrantDatabaseStorageInterface.php index c59f941..9a2586f 100644 --- a/core/modules/node/src/NodeGrantDatabaseStorageInterface.php +++ b/core/modules/node/src/NodeGrantDatabaseStorageInterface.php @@ -61,7 +61,7 @@ public function alterQuery($query, array $tables, $op, AccountInterface $account * permission changes. * * Note: Don't call this method directly from a contributed module. Call - * node_access_write_grants() instead. + * \Drupal\node\NodeAccessControlHandlerInterface::acquireGrants() instead. * * @param \Drupal\node\NodeInterface $node * The node whose grants are being written. @@ -78,9 +78,6 @@ public function alterQuery($query, array $tables, $op, AccountInterface $account * (optional) If false, does not delete records. This is only for optimization * purposes, and assumes the caller has already performed a mass delete of * some form. Defaults to TRUE. - * - * @see node_access_write_grants() - * @see node_access_acquire_grants() */ public function write(NodeInterface $node, array $grants, $realm = NULL, $delete = TRUE); diff --git a/core/modules/node/src/NodeTypeAccessControlHandler.php b/core/modules/node/src/NodeTypeAccessControlHandler.php index befaa0b..6e591b1 100644 --- a/core/modules/node/src/NodeTypeAccessControlHandler.php +++ b/core/modules/node/src/NodeTypeAccessControlHandler.php @@ -30,10 +30,10 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter case 'delete': if ($entity->isLocked()) { - return AccessResult::forbidden()->cacheUntilEntityChanges($entity); + return AccessResult::forbidden()->addCacheableDependency($entity); } else { - return parent::checkAccess($entity, $operation, $account)->cacheUntilEntityChanges($entity); + return parent::checkAccess($entity, $operation, $account)->addCacheableDependency($entity); } break; diff --git a/core/modules/node/src/NodeViewsData.php b/core/modules/node/src/NodeViewsData.php index 72a0baa..1c648c4 100644 --- a/core/modules/node/src/NodeViewsData.php +++ b/core/modules/node/src/NodeViewsData.php @@ -37,43 +37,43 @@ public function getViewsData() { $data['node_field_data']['type']['argument']['id'] = 'node_type'; - $data['node_field_data']['langcode']['help'] = t('The language of the content or translation.'); + $data['node_field_data']['langcode']['help'] = $this->t('The language of the content or translation.'); - $data['node_field_data']['status']['filter']['label'] = t('Published status'); + $data['node_field_data']['status']['filter']['label'] = $this->t('Published status'); $data['node_field_data']['status']['filter']['type'] = 'yes-no'; // Use status = 1 instead of status <> 0 in WHERE statement. $data['node_field_data']['status']['filter']['use_equal'] = TRUE; $data['node_field_data']['status_extra'] = array( - 'title' => t('Published status or admin user'), - 'help' => t('Filters out unpublished content if the current user cannot view it.'), + 'title' => $this->t('Published status or admin user'), + 'help' => $this->t('Filters out unpublished content if the current user cannot view it.'), 'filter' => array( 'field' => 'status', 'id' => 'node_status', - 'label' => t('Published status or admin user'), + 'label' => $this->t('Published status or admin user'), ), ); - $data['node_field_data']['promote']['help'] = t('A boolean indicating whether the node is visible on the front page.'); - $data['node_field_data']['promote']['filter']['label'] = t('Promoted to front page status'); + $data['node_field_data']['promote']['help'] = $this->t('A boolean indicating whether the node is visible on the front page.'); + $data['node_field_data']['promote']['filter']['label'] = $this->t('Promoted to front page status'); $data['node_field_data']['promote']['filter']['type'] = 'yes-no'; - $data['node_field_data']['sticky']['help'] = t('A boolean indicating whether the node should sort to the top of content lists.'); - $data['node_field_data']['sticky']['filter']['label'] = t('Sticky status'); + $data['node_field_data']['sticky']['help'] = $this->t('A boolean indicating whether the node should sort to the top of content lists.'); + $data['node_field_data']['sticky']['filter']['label'] = $this->t('Sticky status'); $data['node_field_data']['sticky']['filter']['type'] = 'yes-no'; - $data['node_field_data']['sticky']['sort']['help'] = t('Whether or not the content is sticky. To list sticky content first, set this to descending.'); + $data['node_field_data']['sticky']['sort']['help'] = $this->t('Whether or not the content is sticky. To list sticky content first, set this to descending.'); $data['node']['path'] = array( 'field' => array( - 'title' => t('Path'), - 'help' => t('The aliased path to this content.'), + 'title' => $this->t('Path'), + 'help' => $this->t('The aliased path to this content.'), 'id' => 'node_path', ), ); $data['node']['node_bulk_form'] = array( - 'title' => t('Node operations bulk form'), - 'help' => t('Add a form element that lets you run operations on multiple nodes.'), + 'title' => $this->t('Node operations bulk form'), + 'help' => $this->t('Add a form element that lets you run operations on multiple nodes.'), 'field' => array( 'id' => 'node_bulk_form', ), @@ -84,8 +84,8 @@ public function getViewsData() { // @todo Add similar support to any date field // @see https://www.drupal.org/node/2337507 $data['node_field_data']['created_fulldate'] = array( - 'title' => t('Created date'), - 'help' => t('Date in the form of CCYYMMDD.'), + 'title' => $this->t('Created date'), + 'help' => $this->t('Date in the form of CCYYMMDD.'), 'argument' => array( 'field' => 'created', 'id' => 'date_fulldate', @@ -93,8 +93,8 @@ public function getViewsData() { ); $data['node_field_data']['created_year_month'] = array( - 'title' => t('Created year + month'), - 'help' => t('Date in the form of YYYYMM.'), + 'title' => $this->t('Created year + month'), + 'help' => $this->t('Date in the form of YYYYMM.'), 'argument' => array( 'field' => 'created', 'id' => 'date_year_month', @@ -102,8 +102,8 @@ public function getViewsData() { ); $data['node_field_data']['created_year'] = array( - 'title' => t('Created year'), - 'help' => t('Date in the form of YYYY.'), + 'title' => $this->t('Created year'), + 'help' => $this->t('Date in the form of YYYY.'), 'argument' => array( 'field' => 'created', 'id' => 'date_year', @@ -111,8 +111,8 @@ public function getViewsData() { ); $data['node_field_data']['created_month'] = array( - 'title' => t('Created month'), - 'help' => t('Date in the form of MM (01 - 12).'), + 'title' => $this->t('Created month'), + 'help' => $this->t('Date in the form of MM (01 - 12).'), 'argument' => array( 'field' => 'created', 'id' => 'date_month', @@ -120,8 +120,8 @@ public function getViewsData() { ); $data['node_field_data']['created_day'] = array( - 'title' => t('Created day'), - 'help' => t('Date in the form of DD (01 - 31).'), + 'title' => $this->t('Created day'), + 'help' => $this->t('Date in the form of DD (01 - 31).'), 'argument' => array( 'field' => 'created', 'id' => 'date_day', @@ -129,8 +129,8 @@ public function getViewsData() { ); $data['node_field_data']['created_week'] = array( - 'title' => t('Created week'), - 'help' => t('Date in the form of WW (01 - 53).'), + 'title' => $this->t('Created week'), + 'help' => $this->t('Date in the form of WW (01 - 53).'), 'argument' => array( 'field' => 'created', 'id' => 'date_week', @@ -138,8 +138,8 @@ public function getViewsData() { ); $data['node_field_data']['changed_fulldate'] = array( - 'title' => t('Updated date'), - 'help' => t('Date in the form of CCYYMMDD.'), + 'title' => $this->t('Updated date'), + 'help' => $this->t('Date in the form of CCYYMMDD.'), 'argument' => array( 'field' => 'changed', 'id' => 'date_fulldate', @@ -147,8 +147,8 @@ public function getViewsData() { ); $data['node_field_data']['changed_year_month'] = array( - 'title' => t('Updated year + month'), - 'help' => t('Date in the form of YYYYMM.'), + 'title' => $this->t('Updated year + month'), + 'help' => $this->t('Date in the form of YYYYMM.'), 'argument' => array( 'field' => 'changed', 'id' => 'date_year_month', @@ -156,8 +156,8 @@ public function getViewsData() { ); $data['node_field_data']['changed_year'] = array( - 'title' => t('Updated year'), - 'help' => t('Date in the form of YYYY.'), + 'title' => $this->t('Updated year'), + 'help' => $this->t('Date in the form of YYYY.'), 'argument' => array( 'field' => 'changed', 'id' => 'date_year', @@ -165,8 +165,8 @@ public function getViewsData() { ); $data['node_field_data']['changed_month'] = array( - 'title' => t('Updated month'), - 'help' => t('Date in the form of MM (01 - 12).'), + 'title' => $this->t('Updated month'), + 'help' => $this->t('Date in the form of MM (01 - 12).'), 'argument' => array( 'field' => 'changed', 'id' => 'date_month', @@ -174,8 +174,8 @@ public function getViewsData() { ); $data['node_field_data']['changed_day'] = array( - 'title' => t('Updated day'), - 'help' => t('Date in the form of DD (01 - 31).'), + 'title' => $this->t('Updated day'), + 'help' => $this->t('Date in the form of DD (01 - 31).'), 'argument' => array( 'field' => 'changed', 'id' => 'date_day', @@ -183,30 +183,30 @@ public function getViewsData() { ); $data['node_field_data']['changed_week'] = array( - 'title' => t('Updated week'), - 'help' => t('Date in the form of WW (01 - 53).'), + 'title' => $this->t('Updated week'), + 'help' => $this->t('Date in the form of WW (01 - 53).'), 'argument' => array( 'field' => 'changed', 'id' => 'date_week', ), ); - $data['node_field_data']['uid']['help'] = t('The user authoring the content. If you need more fields than the uid add the content: author relationship'); + $data['node_field_data']['uid']['help'] = $this->t('The user authoring the content. If you need more fields than the uid add the content: author relationship'); $data['node_field_data']['uid']['filter']['id'] = 'user_name'; - $data['node_field_data']['uid']['relationship']['title'] = t('Content author'); - $data['node_field_data']['uid']['relationship']['help'] = t('Relate content to the user who created it.'); - $data['node_field_data']['uid']['relationship']['label'] = t('author'); + $data['node_field_data']['uid']['relationship']['title'] = $this->t('Content author'); + $data['node_field_data']['uid']['relationship']['help'] = $this->t('Relate content to the user who created it.'); + $data['node_field_data']['uid']['relationship']['label'] = $this->t('author'); $data['node']['node_listing_empty'] = array( - 'title' => t('Empty Node Frontpage behavior'), - 'help' => t('Provides a link to the node add overview page.'), + 'title' => $this->t('Empty Node Frontpage behavior'), + 'help' => $this->t('Provides a link to the node add overview page.'), 'area' => array( 'id' => 'node_listing_empty', ), ); - $data['node_field_data']['uid_revision']['title'] = t('User has a revision'); - $data['node_field_data']['uid_revision']['help'] = t('All nodes where a certain user has a revision'); + $data['node_field_data']['uid_revision']['title'] = $this->t('User has a revision'); + $data['node_field_data']['uid_revision']['help'] = $this->t('All nodes where a certain user has a revision'); $data['node_field_data']['uid_revision']['real field'] = 'nid'; $data['node_field_data']['uid_revision']['filter']['id'] = 'node_uid_revision'; $data['node_field_data']['uid_revision']['argument']['id'] = 'node_uid_revision'; @@ -214,7 +214,7 @@ public function getViewsData() { $data['node_field_revision']['table']['wizard_id'] = 'node_revision'; // Advertise this table as a possible base table. - $data['node_field_revision']['table']['base']['help'] = t('Content revision is a history of changes to content.'); + $data['node_field_revision']['table']['base']['help'] = $this->t('Content revision is a history of changes to content.'); $data['node_field_revision']['table']['base']['defaults']['title'] = 'title'; $data['node_field_revision']['nid']['argument'] = [ @@ -227,8 +227,8 @@ public function getViewsData() { $data['node_field_revision']['nid']['relationship']['id'] = 'standard'; $data['node_field_revision']['nid']['relationship']['base'] = 'node_field_data'; $data['node_field_revision']['nid']['relationship']['base field'] = 'nid'; - $data['node_field_revision']['nid']['relationship']['title'] = t('Content'); - $data['node_field_revision']['nid']['relationship']['label'] = t('Get the actual content from a content revision.'); + $data['node_field_revision']['nid']['relationship']['title'] = $this->t('Content'); + $data['node_field_revision']['nid']['relationship']['label'] = $this->t('Get the actual content from a content revision.'); $data['node_field_revision']['vid'] = array( 'argument' => array( @@ -239,35 +239,35 @@ public function getViewsData() { 'id' => 'standard', 'base' => 'node_field_data', 'base field' => 'vid', - 'title' => t('Content'), - 'label' => t('Get the actual content from a content revision.'), + 'title' => $this->t('Content'), + 'label' => $this->t('Get the actual content from a content revision.'), ), ) + $data['node_field_revision']['vid']; - $data['node_field_revision']['langcode']['help'] = t('The language the original content is in.'); + $data['node_field_revision']['langcode']['help'] = $this->t('The language the original content is in.'); - $data['node_revision']['revision_uid']['help'] = t('Relate a content revision to the user who created the revision.'); - $data['node_revision']['revision_uid']['relationship']['label'] = t('revision user'); + $data['node_revision']['revision_uid']['help'] = $this->t('Relate a content revision to the user who created the revision.'); + $data['node_revision']['revision_uid']['relationship']['label'] = $this->t('revision user'); $data['node_field_revision']['table']['wizard_id'] = 'node_field_revision'; $data['node_field_revision']['table']['join']['node_field_data']['left_field'] = 'vid'; $data['node_field_revision']['table']['join']['node_field_data']['field'] = 'vid'; - $data['node_field_revision']['status']['filter']['label'] = t('Published'); + $data['node_field_revision']['status']['filter']['label'] = $this->t('Published'); $data['node_field_revision']['status']['filter']['type'] = 'yes-no'; $data['node_field_revision']['status']['filter']['use_equal'] = TRUE; - $data['node_field_revision']['promote']['help'] = t('A boolean indicating whether the node is visible on the front page.'); + $data['node_field_revision']['promote']['help'] = $this->t('A boolean indicating whether the node is visible on the front page.'); - $data['node_field_revision']['sticky']['help'] = t('A boolean indicating whether the node should sort to the top of content lists.'); + $data['node_field_revision']['sticky']['help'] = $this->t('A boolean indicating whether the node should sort to the top of content lists.'); - $data['node_field_revision']['langcode']['help'] = t('The language of the content or translation.'); + $data['node_field_revision']['langcode']['help'] = $this->t('The language of the content or translation.'); $data['node_field_revision']['link_to_revision'] = array( 'field' => array( - 'title' => t('Link to revision'), - 'help' => t('Provide a simple link to the revision.'), + 'title' => $this->t('Link to revision'), + 'help' => $this->t('Provide a simple link to the revision.'), 'id' => 'node_revision_link', 'click sortable' => FALSE, ), @@ -275,8 +275,8 @@ public function getViewsData() { $data['node_field_revision']['revert_revision'] = array( 'field' => array( - 'title' => t('Link to revert revision'), - 'help' => t('Provide a simple link to revert to the revision.'), + 'title' => $this->t('Link to revert revision'), + 'help' => $this->t('Provide a simple link to revert to the revision.'), 'id' => 'node_revision_link_revert', 'click sortable' => FALSE, ), @@ -284,8 +284,8 @@ public function getViewsData() { $data['node_field_revision']['delete_revision'] = array( 'field' => array( - 'title' => t('Link to delete revision'), - 'help' => t('Provide a simple link to delete the content revision.'), + 'title' => $this->t('Link to delete revision'), + 'help' => $this->t('Provide a simple link to delete the content revision.'), 'id' => 'node_revision_link_delete', 'click sortable' => FALSE, ), @@ -293,7 +293,7 @@ public function getViewsData() { // Define the base group of this table. Fields that don't have a group defined // will go into this field by default. - $data['node_access']['table']['group'] = t('Content access'); + $data['node_access']['table']['group'] = $this->t('Content access'); // For other base tables, explain how we join. $data['node_access']['table']['join'] = array( @@ -303,11 +303,11 @@ public function getViewsData() { ), ); $data['node_access']['nid'] = array( - 'title' => t('Access'), - 'help' => t('Filter by access.'), + 'title' => $this->t('Access'), + 'help' => $this->t('Filter by access.'), 'filter' => array( 'id' => 'node_access', - 'help' => t('Filter for content by view access. Not necessary if you are using node as your base table.'), + 'help' => $this->t('Filter for content by view access. Not necessary if you are using node as your base table.'), ), ); @@ -324,7 +324,7 @@ public function getViewsData() { } if ($enabled) { - $data['node_search_index']['table']['group'] = t('Search'); + $data['node_search_index']['table']['group'] = $this->t('Search'); // Automatically join to the node table (or actually, node_field_data). // Use a Views table alias to allow other modules to use this table too, @@ -357,8 +357,8 @@ public function getViewsData() { ); $data['node_search_index']['score'] = array( - 'title' => t('Score'), - 'help' => t('The score of the search item. This will not be used if the search filter is not also present.'), + 'title' => $this->t('Score'), + 'help' => $this->t('The score of the search item. This will not be used if the search filter is not also present.'), 'field' => array( 'id' => 'search_score', 'float' => TRUE, @@ -371,8 +371,8 @@ public function getViewsData() { ); $data['node_search_index']['keys'] = array( - 'title' => t('Search Keywords'), - 'help' => t('The keywords to search for.'), + 'title' => $this->t('Search Keywords'), + 'help' => $this->t('The keywords to search for.'), 'filter' => array( 'id' => 'search_keywords', 'no group by' => TRUE, diff --git a/core/modules/node/src/Plugin/Search/NodeSearch.php b/core/modules/node/src/Plugin/Search/NodeSearch.php index 0fa464e..85515fd 100644 --- a/core/modules/node/src/Plugin/Search/NodeSearch.php +++ b/core/modules/node/src/Plugin/Search/NodeSearch.php @@ -429,8 +429,23 @@ public function updateIndex() { // per cron run. $limit = (int) $this->searchSettings->get('index.cron_limit'); - $result = $this->database->queryRange("SELECT n.nid, MAX(sd.reindex) FROM {node} n LEFT JOIN {search_dataset} sd ON sd.sid = n.nid AND sd.type = :type WHERE sd.sid IS NULL OR sd.reindex <> 0 GROUP BY n.nid ORDER BY MAX(sd.reindex) is null DESC, MAX(sd.reindex) ASC, n.nid ASC", 0, $limit, array(':type' => $this->getPluginId()), array('target' => 'replica')); - $nids = $result->fetchCol(); + $query = db_select('node', 'n', array('target' => 'replica')); + $query->addField('n', 'nid'); + $query->leftJoin('search_dataset', 'sd', 'sd.sid = n.nid AND sd.type = :type', array(':type' => $this->getPluginId())); + $query->addExpression('CASE MAX(sd.reindex) WHEN NULL THEN 0 ELSE 1 END', 'ex'); + $query->addExpression('MAX(sd.reindex)', 'ex2'); + $query->condition( + $query->orConditionGroup() + ->where('sd.sid IS NULL') + ->condition('sd.reindex', 0, '<>') + ); + $query->orderBy('ex', 'DESC') + ->orderBy('ex2') + ->orderBy('n.nid') + ->groupBy('n.nid') + ->range(0, $limit); + + $nids = $query->execute()->fetchCol(); if (!$nids) { return; } diff --git a/core/modules/node/src/Plugin/migrate/D6NodeDeriver.php b/core/modules/node/src/Plugin/migrate/D6NodeDeriver.php new file mode 100644 index 0000000..f6f8ba3 --- /dev/null +++ b/core/modules/node/src/Plugin/migrate/D6NodeDeriver.php @@ -0,0 +1,142 @@ +basePluginId = $base_plugin_id; + $this->cckPluginManager = $cck_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static( + $base_plugin_id, + $container->get('plugin.manager.migrate.cckfield') + ); + } + + /** + * Gets the definition of all derivatives of a base plugin. + * + * @param array $base_plugin_definition + * The definition array of the base plugin. + * + * @return array + * An array of full derivative definitions keyed on derivative id. + * + * @see \Drupal\Component\Plugin\Derivative\DeriverBase::getDerivativeDefinition() + */ + public function getDerivativeDefinitions($base_plugin_definition) { + // Read all CCK field instance definitions in the source database. + $fields = array(); + try { + $source_plugin = static::getSourcePlugin('d6_field_instance'); + $source_plugin->checkRequirements(); + + foreach ($source_plugin as $row) { + $fields[$row->getSourceProperty('type_name')][$row->getSourceProperty('field_name')] = $row->getSource(); + } + } + catch (RequirementsException $e) { + // If checkRequirements() failed then the content module did not exist and + // we do not have any CCK fields. Therefore, $fields will be empty and + // below we'll create a migration just for the node properties. + } + + try { + foreach (static::getSourcePlugin('d6_node_type') as $row) { + $node_type = $row->getSourceProperty('type'); + $values = $base_plugin_definition; + + $values['label'] = t("@label (@type)", [ + '@label' => $values['label'], + '@type' => $node_type, + ]); + $values['source']['node_type'] = $node_type; + + // If this migration is based on the d6_node_revision migration, it + // should explicitly depend on the corresponding d6_node variant. + if ($base_plugin_definition['id'] == 'd6_node_revision') { + $values['migration_dependencies']['required'][] = 'd6_node:' . $node_type; + } + + $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($values); + if (isset($fields[$node_type])) { + foreach ($fields[$node_type] as $field_name => $info) { + $field_type = $info['type']; + if ($this->cckPluginManager->hasDefinition($info['type'])) { + if (!isset($this->cckPluginCache[$field_type])) { + $this->cckPluginCache[$field_type] = $this->cckPluginManager->createInstance($field_type, [], $migration); + } + $this->cckPluginCache[$field_type] + ->processCckFieldValues($migration, $field_name, $info); + } + else { + $migration->setProcessOfProperty($field_name, $field_name); + } + } + } + $this->derivatives[$node_type] = $migration->getPluginDefinition(); + } + } + catch (DatabaseExceptionWrapper $e) { + // Once we begin iterating the source plugin it is possible that the + // source tables will not exist. This can happen when the + // MigrationPluginManager gathers up the migration definitions but we do + // not actually have a Drupal 6 source database. + } + + return $this->derivatives; + } + +} diff --git a/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php b/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php new file mode 100644 index 0000000..6307fce --- /dev/null +++ b/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php @@ -0,0 +1,130 @@ +basePluginId = $base_plugin_id; + $this->cckPluginManager = $cck_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static( + $base_plugin_id, + $container->get('plugin.manager.migrate.cckfield') + ); + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition) { + $fields = []; + try { + $source_plugin = static::getSourcePlugin('d7_field_instance'); + $source_plugin->checkRequirements(); + + // Read all field instance definitions in the source database. + foreach ($source_plugin as $row) { + if ($row->getSourceProperty('entity_type') == 'node') { + $fields[$row->getSourceProperty('bundle')][$row->getSourceProperty('field_name')] = $row->getSource(); + } + } + } + catch (RequirementsException $e) { + // If checkRequirements() failed then the field module did not exist and + // we do not have any fields. Therefore, $fields will be empty and below + // we'll create a migration just for the node properties. + } + + try { + foreach (static::getSourcePlugin('d7_node_type') as $row) { + $node_type = $row->getSourceProperty('type'); + $values = $base_plugin_definition; + + $values['label'] = t('@label (@type)', [ + '@label' => $values['label'], + '@type' => $row->getSourceProperty('name'), + ]); + $values['source']['node_type'] = $node_type; + + $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($values); + if (isset($fields[$node_type])) { + foreach ($fields[$node_type] as $field_name => $info) { + $field_type = $info['type']; + if ($this->cckPluginManager->hasDefinition($field_type)) { + if (!isset($this->cckPluginCache[$field_type])) { + $this->cckPluginCache[$field_type] = $this->cckPluginManager->createInstance($field_type, [], $migration); + } + $this->cckPluginCache[$field_type] + ->processCckFieldValues($migration, $field_name, $info); + } + else { + $migration->setProcessOfProperty($field_name, $field_name); + } + } + } + $this->derivatives[$node_type] = $migration->getPluginDefinition(); + } + } + catch (DatabaseExceptionWrapper $e) { + // Once we begin iterating the source plugin it is possible that the + // source tables will not exist. This can happen when the + // MigrationPluginManager gathers up the migration definitions but we do + // not actually have a Drupal 7 source database. + } + + return $this->derivatives; + } + +} diff --git a/core/modules/node/src/Plugin/migrate/builder/d6/Node.php b/core/modules/node/src/Plugin/migrate/builder/d6/Node.php deleted file mode 100644 index 99f8d45..0000000 --- a/core/modules/node/src/Plugin/migrate/builder/d6/Node.php +++ /dev/null @@ -1,75 +0,0 @@ -getSourcePlugin('d6_field_instance', $template['source']); - try { - $source_plugin->checkRequirements(); - - foreach ($source_plugin as $field) { - $info = $field->getSource(); - $fields[$info['type_name']][$info['field_name']] = $info; - } - } - catch (RequirementsException $e) { - // Don't do anything; $fields will be empty. - } - - foreach ($this->getSourcePlugin('d6_node_type', $template['source']) as $row) { - $node_type = $row->getSourceProperty('type'); - $values = $template; - $values['id'] = $template['id'] . '__' . $node_type; - - $label = $template['label']; - $values['label'] = $this->t("@label (@type)", ['@label' => $label, '@type' => $node_type]); - $values['source']['node_type'] = $node_type; - - // If this migration is based on the d6_node_revision template, it should - // explicitly depend on the corresponding d6_node variant. - if ($template['id'] == 'd6_node_revision') { - $values['migration_dependencies']['required'][] = 'd6_node__' . $node_type; - } - - $migration = Migration::create($values); - - if (isset($fields[$node_type])) { - foreach ($fields[$node_type] as $field => $info) { - if ($this->cckPluginManager->hasDefinition($info['type'])) { - $this->getCckPlugin($info['type']) - ->processCckFieldValues($migration, $field, $info); - } - else { - $migration->setProcessOfProperty($field, $field); - } - } - } - - $migrations[] = $migration; - } - - return $migrations; - } - -} diff --git a/core/modules/node/src/Plugin/migrate/builder/d7/Node.php b/core/modules/node/src/Plugin/migrate/builder/d7/Node.php deleted file mode 100644 index 44b124c..0000000 --- a/core/modules/node/src/Plugin/migrate/builder/d7/Node.php +++ /dev/null @@ -1,57 +0,0 @@ -getSourcePlugin('d7_field_instance', $template['source']) as $field) { - $info = $field->getSource(); - $fields[$info['entity_type']][$info['bundle']][$info['field_name']] = $info; - } - - foreach ($this->getSourcePlugin('d7_node_type', $template['source']) as $node_type) { - $bundle = $node_type->getSourceProperty('type'); - $values = $template; - $values['id'] .= '__' . $bundle; - $values['label'] = $this->t('@label (@type)', ['@label' => $values['label'], '@type' => $node_type->getSourceProperty('name')]); - $values['source']['node_type'] = $bundle; - $migration = Migration::create($values); - - if (isset($fields['node'][$bundle])) { - foreach ($fields['node'][$bundle] as $field => $data) { - if ($this->cckPluginManager->hasDefinition($data['type'])) { - $this->getCckPlugin($data['type']) - ->processCckFieldValues($migration, $field, $data); - } - else { - $migration->setProcessOfProperty($field, $field); - } - } - } - - $migrations[] = $migration; - } - - return $migrations; - } - -} diff --git a/core/modules/node/src/Tests/Condition/NodeConditionTest.php b/core/modules/node/src/Tests/Condition/NodeConditionTest.php index d739751..6af1d77 100644 --- a/core/modules/node/src/Tests/Condition/NodeConditionTest.php +++ b/core/modules/node/src/Tests/Condition/NodeConditionTest.php @@ -7,6 +7,8 @@ namespace Drupal\node\Tests\Condition; +use Drupal\node\Entity\Node; +use Drupal\node\Entity\NodeType; use Drupal\system\Tests\Entity\EntityUnitTestBase; /** @@ -22,11 +24,11 @@ protected function setUp() { parent::setUp(); // Create the node bundles required for testing. - $type = entity_create('node_type', array('type' => 'page', 'name' => 'page')); + $type = NodeType::create(['type' => 'page', 'name' => 'page']); $type->save(); - $type = entity_create('node_type', array('type' => 'article', 'name' => 'article')); + $type = NodeType::create(['type' => 'article', 'name' => 'article']); $type->save(); - $type = entity_create('node_type', array('type' => 'test', 'name' => 'test')); + $type = NodeType::create(['type' => 'test', 'name' => 'test']); $type->save(); } @@ -38,11 +40,11 @@ function testConditions() { $this->createUser(); // Get some nodes of various types to check against. - $page = entity_create('node', array('type' => 'page', 'title' => $this->randomMachineName(), 'uid' => 1)); + $page = Node::create(['type' => 'page', 'title' => $this->randomMachineName(), 'uid' => 1]); $page->save(); - $article = entity_create('node', array('type' => 'article', 'title' => $this->randomMachineName(), 'uid' => 1)); + $article = Node::create(['type' => 'article', 'title' => $this->randomMachineName(), 'uid' => 1]); $article->save(); - $test = entity_create('node', array('type' => 'test', 'title' => $this->randomMachineName(), 'uid' => 1)); + $test = Node::create(['type' => 'test', 'title' => $this->randomMachineName(), 'uid' => 1]); $test->save(); // Grab the node type condition and configure it to check against node type diff --git a/core/modules/node/src/Tests/Migrate/MigrateNodeStubTest.php b/core/modules/node/src/Tests/Migrate/MigrateNodeStubTest.php deleted file mode 100644 index 22d140b..0000000 --- a/core/modules/node/src/Tests/Migrate/MigrateNodeStubTest.php +++ /dev/null @@ -1,48 +0,0 @@ -installEntitySchema('node'); - // Need at least one node type present. - NodeType::create([ - 'type' => 'testnodetype', - 'name' => 'Test node type', - ])->save(); - } - - /** - * Tests creation of node stubs. - */ - public function testStub() { - $this->performStubTest('node'); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeBuilderTest.php b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeBuilderTest.php deleted file mode 100644 index 774d344..0000000 --- a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeBuilderTest.php +++ /dev/null @@ -1,83 +0,0 @@ -builtMigrations[$id]; - $this->assertTrue($migration instanceof Migration); - $this->assertIdentical($id, $migration->id()); - $this->assertEqual($label, $migration->label()); - } - - /** - * Tests creating migrations from a template, using a builder plugin. - */ - public function testCreateMigrations() { - $templates = [ - 'd6_node' => [ - 'id' => 'd6_node', - 'label' => 'Drupal 6 nodes', - 'builder' => [ - 'plugin' => 'd6_node', - ], - 'source' => [ - 'plugin' => 'd6_node', - ], - 'process' => [ - 'nid' => 'nid', - 'vid' => 'vid', - 'uid' => 'uid', - ], - 'destination' => [ - 'plugin' => 'entity:node', - ], - ], - ]; - - $migrations = \Drupal::service('migrate.migration_builder')->createMigrations($templates); - // Key the array. - foreach ($migrations as $migration) { - $this->builtMigrations[$migration->id()] = $migration; - } - $this->assertIdentical(11, count($this->builtMigrations)); - $this->assertEntity('d6_node__article', 'Drupal 6 nodes (article)'); - $this->assertEntity('d6_node__company', 'Drupal 6 nodes (company)'); - $this->assertEntity('d6_node__employee', 'Drupal 6 nodes (employee)'); - $this->assertEntity('d6_node__event', 'Drupal 6 nodes (event)'); - $this->assertEntity('d6_node__page', 'Drupal 6 nodes (page)'); - $this->assertEntity('d6_node__sponsor', 'Drupal 6 nodes (sponsor)'); - $this->assertEntity('d6_node__story', 'Drupal 6 nodes (story)'); - $this->assertEntity('d6_node__test_event', 'Drupal 6 nodes (test_event)'); - $this->assertEntity('d6_node__test_page', 'Drupal 6 nodes (test_page)'); - $this->assertEntity('d6_node__test_planet', 'Drupal 6 nodes (test_planet)'); - $this->assertEntity('d6_node__test_story', 'Drupal 6 nodes (test_story)'); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeBundleSettingsTest.php b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeBundleSettingsTest.php deleted file mode 100644 index d229661..0000000 --- a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeBundleSettingsTest.php +++ /dev/null @@ -1,66 +0,0 @@ -installConfig(['node']); - $this->executeMigration('d6_node_type'); - - // Create a config entity that already exists. - BaseFieldOverride::create([ - 'field_name' => 'promote', - 'entity_type' => 'node', - 'bundle' => 'page', - ])->save(); - - $this->executeMigrations([ - 'd6_node_setting_promote', - 'd6_node_setting_status', - 'd6_node_setting_sticky' - ]); - } - - /** - * Tests Drupal 6 node type settings to Drupal 8 migration. - */ - public function testNodeBundleSettings() { - // Test settings on test_page bundle. - $node = Node::create(['type' => 'test_page']); - $this->assertIdentical(1, $node->status->value); - $this->assertIdentical(1, $node->promote->value); - $this->assertIdentical(1, $node->sticky->value); - - // Test settings for test_story bundle. - $node = Node::create(['type' => 'test_story']); - $this->assertIdentical(1, $node->status->value); - $this->assertIdentical(1, $node->promote->value); - $this->assertIdentical(0, $node->sticky->value); - - // Test settings for the test_event bundle. - $node = Node::create(['type' => 'test_event']); - $this->assertIdentical(0, $node->status->value); - $this->assertIdentical(0, $node->promote->value); - $this->assertIdentical(1, $node->sticky->value); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeConfigsTest.php b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeConfigsTest.php deleted file mode 100644 index 26c5fcb..0000000 --- a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeConfigsTest.php +++ /dev/null @@ -1,39 +0,0 @@ -executeMigration('d6_node_settings'); - } - - /** - * Tests Drupal 6 node settings to Drupal 8 migration. - */ - public function testNodeSettings() { - $config = $this->config('node.settings'); - $this->assertIdentical(FALSE, $config->get('use_admin_theme')); - $this->assertConfigSchema(\Drupal::service('config.typed'), 'node.settings', $config->get()); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeRevisionTest.php b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeRevisionTest.php index b8ee0c2..19c7c09 100644 --- a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeRevisionTest.php +++ b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeRevisionTest.php @@ -2,10 +2,11 @@ /** * @file - * Contains \Drupal\node\Tests\Migrate\d6\MigrateNodeRevisionTest. + * Contains \Drupal\Tests\node\Kernel\Migrate\d6\MigrateNodeRevisionTest. */ namespace Drupal\node\Tests\Migrate\d6; +use Drupal\Tests\node\Kernel\Migrate\d6\MigrateNodeTestBase; /** * Node content revisions migration. @@ -19,7 +20,7 @@ class MigrateNodeRevisionTest extends MigrateNodeTestBase { */ protected function setUp() { parent::setUp(); - $this->executeMigrations(['d6_node:*', 'd6_node_revision:*']); + $this->executeMigrations(['d6_node', 'd6_node_revision']); } /** diff --git a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeSettingPromoteTest.php b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeSettingPromoteTest.php deleted file mode 100644 index a4c7850..0000000 --- a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeSettingPromoteTest.php +++ /dev/null @@ -1,37 +0,0 @@ -installConfig(['node']); - $this->executeMigration('d6_node_type'); - $this->executeMigration('d6_node_setting_promote'); - } - - /** - * Tests migration of the promote checkbox's settings. - */ - public function testMigration() { - $this->assertIdentical('Promoted to front page', BaseFieldOverride::load('node.article.promote')->label()); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeSettingStickyTest.php b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeSettingStickyTest.php deleted file mode 100644 index 0adbb43..0000000 --- a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeSettingStickyTest.php +++ /dev/null @@ -1,37 +0,0 @@ -installConfig(['node']); - $this->executeMigration('d6_node_type'); - $this->executeMigration('d6_node_setting_sticky'); - } - - /** - * Tests migration of the sticky checkbox's settings. - */ - public function testMigration() { - $this->assertIdentical('Sticky at the top of lists', BaseFieldOverride::load('node.article.sticky')->label()); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTest.php b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTest.php deleted file mode 100644 index cf0c44b..0000000 --- a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTest.php +++ /dev/null @@ -1,136 +0,0 @@ -executeMigrations(['d6_node:*']); - } - - /** - * Test node migration from Drupal 6 to 8. - */ - public function testNode() { - $node = Node::load(1); - $this->assertIdentical('1', $node->id(), 'Node 1 loaded.'); - $this->assertIdentical('und', $node->langcode->value); - $this->assertIdentical('test', $node->body->value); - $this->assertIdentical('test', $node->body->summary); - $this->assertIdentical('filtered_html', $node->body->format); - $this->assertIdentical('story', $node->getType(), 'Node has the correct bundle.'); - $this->assertIdentical('Test title', $node->getTitle(), 'Node has the correct title.'); - $this->assertIdentical('1388271197', $node->getCreatedTime(), 'Node has the correct created time.'); - $this->assertIdentical(FALSE, $node->isSticky()); - $this->assertIdentical('1', $node->getOwnerId()); - $this->assertIdentical('1420861423', $node->getRevisionCreationTime()); - - /** @var \Drupal\node\NodeInterface $node_revision */ - $node_revision = \Drupal::entityManager()->getStorage('node')->loadRevision(1); - $this->assertIdentical('Test title', $node_revision->getTitle()); - $this->assertIdentical('1', $node_revision->getRevisionAuthor()->id(), 'Node revision has the correct user'); - // This is empty on the first revision. - $this->assertIdentical(NULL, $node_revision->revision_log->value); - - $node = Node::load(2); - $this->assertIdentical('Test title rev 3', $node->getTitle()); - $this->assertIdentical('test rev 3', $node->body->value); - $this->assertIdentical('filtered_html', $node->body->format); - - // Test that link fields are migrated. - $this->assertIdentical('http://groups.drupal.org/', $node->field_test_link->uri); - $this->assertIdentical('Drupal Groups', $node->field_test_link->title); - $this->assertIdentical([], $node->field_test_link->options['attributes']); - - // Rerun migration with invalid link attributes and a different URL and - // title. If only the attributes are changed the error does not occur. - Database::getConnection('default', 'migrate') - ->update('content_type_story') - ->fields([ - 'field_test_link_url' => 'https://www.drupal.org/node/2127611', - 'field_test_link_title' => 'Migrate API in Drupal 8', - 'field_test_link_attributes' => '', - ]) - ->condition('nid', '2') - ->condition('vid', '3') - ->execute(); - - $this->rerunMigration(); - $node = Node::load(2); - $this->assertIdentical('https://www.drupal.org/node/2127611', $node->field_test_link->uri); - $this->assertIdentical('Migrate API in Drupal 8', $node->field_test_link->title); - $this->assertIdentical([], $node->field_test_link->options['attributes']); - - // Test that we can re-import using the EntityContentBase destination. - $title = $this->rerunMigration(); - $node = Node::load(2); - $this->assertIdentical($title, $node->getTitle()); - // Test multi-column fields are correctly upgraded. - $this->assertIdentical('test rev 3', $node->body->value); - $this->assertIdentical('full_html', $node->body->format); - - // Now insert a row indicating a failure and set to update later. - $title = $this->rerunMigration(array( - 'sourceid1' => 2, - 'destid1' => NULL, - 'source_row_status' => MigrateIdMapInterface::STATUS_NEEDS_UPDATE, - )); - $node = Node::load(2); - $this->assertIdentical($title, $node->getTitle()); - } - - /** - * Execute the migration a second time. - * - * @param array $new_row - * An optional row to be inserted into the id map. - * - * @return string - * The new title in the source for vid 3. - */ - protected function rerunMigration($new_row = []) { - $title = $this->randomString(); - $migration = Migration::load('d6_node__story'); - $source_connection = Database::getConnection('default', 'migrate'); - $source_connection->update('node_revisions') - ->fields(array( - 'title' => $title, - 'format' => 2, - )) - ->condition('vid', 3) - ->execute(); - $table_name = $migration->getIdMap()->mapTableName(); - $default_connection = \Drupal::database(); - $default_connection->truncate($table_name)->execute(); - if ($new_row) { - $hash = $migration->getIdMap()->getSourceIDsHash(['nid' => $new_row['sourceid1']]); - $new_row['source_ids_hash'] = $hash; - $default_connection->insert($table_name) - ->fields($new_row) - ->execute(); - } - $this->executeMigration($migration); - return $title; - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTestBase.php b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTestBase.php deleted file mode 100644 index f88fba2..0000000 --- a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTestBase.php +++ /dev/null @@ -1,43 +0,0 @@ -installEntitySchema('node'); - $this->installConfig(['node']); - $this->installSchema('node', ['node_access']); - $this->installSchema('system', ['sequences']); - - // Create a new user which needs to have UID 1, because that is expected by - // the assertions from - // \Drupal\migrate_drupal\Tests\d6\MigrateNodeRevisionTest. - User::create([ - 'uid' => 1, - 'name' => $this->randomMachineName(), - 'status' => 1, - ])->enforceIsNew()->save(); - - $this->migrateUsers(FALSE); - $this->migrateFields(); - $this->executeMigration('d6_node_settings'); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTypeTest.php b/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTypeTest.php deleted file mode 100644 index 97d2597..0000000 --- a/core/modules/node/src/Tests/Migrate/d6/MigrateNodeTypeTest.php +++ /dev/null @@ -1,75 +0,0 @@ -installConfig(['node']); - $this->executeMigration('d6_node_type'); - } - - /** - * Tests Drupal 6 node type to Drupal 8 migration. - */ - public function testNodeType() { - $id_map = Migration::load('d6_node_type')->getIdMap(); - // Test the test_page content type. - $node_type_page = NodeType::load('test_page'); - $this->assertIdentical('test_page', $node_type_page->id(), 'Node type test_page loaded'); - $this->assertIdentical(TRUE, $node_type_page->displaySubmitted()); - $this->assertIdentical(FALSE, $node_type_page->isNewRevision()); - $this->assertIdentical(DRUPAL_OPTIONAL, $node_type_page->getPreviewMode()); - $this->assertIdentical($id_map->lookupDestinationID(array('test_page')), array('test_page')); - - // Test we have a body field. - $field = FieldConfig::loadByName('node', 'test_page', 'body'); - $this->assertIdentical('This is the body field label', $field->getLabel(), 'Body field was found.'); - - // Test the test_story content type. - $node_type_story = NodeType::load('test_story'); - $this->assertIdentical('test_story', $node_type_story->id(), 'Node type test_story loaded'); - - $this->assertIdentical(TRUE, $node_type_story->displaySubmitted()); - $this->assertIdentical(FALSE, $node_type_story->isNewRevision()); - $this->assertIdentical(DRUPAL_OPTIONAL, $node_type_story->getPreviewMode()); - $this->assertIdentical($id_map->lookupDestinationID(array('test_story')), array('test_story')); - - // Test we don't have a body field. - $field = FieldConfig::loadByName('node', 'test_story', 'body'); - $this->assertIdentical(NULL, $field, 'No body field found'); - - // Test the test_event content type. - $node_type_event = NodeType::load('test_event'); - $this->assertIdentical('test_event', $node_type_event->id(), 'Node type test_event loaded'); - - $this->assertIdentical(TRUE, $node_type_event->displaySubmitted()); - $this->assertIdentical(TRUE, $node_type_event->isNewRevision()); - $this->assertIdentical(DRUPAL_OPTIONAL, $node_type_event->getPreviewMode()); - $this->assertIdentical($id_map->lookupDestinationID(array('test_event')), array('test_event')); - - // Test we have a body field. - $field = FieldConfig::loadByName('node', 'test_event', 'body'); - $this->assertIdentical('Body', $field->getLabel(), 'Body field was found.'); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d6/MigrateViewModesTest.php b/core/modules/node/src/Tests/Migrate/d6/MigrateViewModesTest.php deleted file mode 100644 index 4aef44c..0000000 --- a/core/modules/node/src/Tests/Migrate/d6/MigrateViewModesTest.php +++ /dev/null @@ -1,41 +0,0 @@ -executeMigration('d6_view_modes'); - } - - /** - * Tests Drupal 6 view modes to Drupal 8 migration. - */ - public function testViewModes() { - // Test a new view mode. - $view_mode = EntityViewMode::load('node.preview'); - $this->assertIdentical(FALSE, is_null($view_mode), 'Preview view mode loaded.'); - $this->assertIdentical('Preview', $view_mode->label(), 'View mode has correct label.'); - // Test the ID map. - $this->assertIdentical(array('node', 'preview'), Migration::load('d6_view_modes')->getIdMap()->lookupDestinationID(array(1))); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d7/MigrateNodeSettingsTest.php b/core/modules/node/src/Tests/Migrate/d7/MigrateNodeSettingsTest.php deleted file mode 100644 index 0bccde9..0000000 --- a/core/modules/node/src/Tests/Migrate/d7/MigrateNodeSettingsTest.php +++ /dev/null @@ -1,45 +0,0 @@ -executeMigration('d7_node_settings'); - } - - /** - * Tests migration of node variables to node.settings config object. - */ - public function testAggregatorSettings() { - $config = $this->config('node.settings'); - $this->assertEqual(1, $config->get('use_admin_theme')); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTest.php b/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTest.php deleted file mode 100644 index deed28c..0000000 --- a/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTest.php +++ /dev/null @@ -1,152 +0,0 @@ -installEntitySchema('node'); - $this->installEntitySchema('comment'); - $this->installEntitySchema('taxonomy_term'); - $this->installEntitySchema('file'); - $this->installConfig(static::$modules); - $this->installSchema('node', ['node_access']); - $this->installSchema('system', ['sequences']); - - $this->executeMigrations([ - 'd7_user_role', - 'd7_user', - 'd7_node_type', - 'd7_comment_type', - 'd7_field', - 'd7_field_instance', - 'd7_node__test_content_type', - 'd7_node__article', - ]); - } - - /** - * Asserts various aspects of a node. - * - * @param string $id - * The node ID. - * @param string $type - * The node type. - * @param string $langcode - * The expected language code. - * @param string $title - * The expected title. - * @param int $uid - * The expected author ID. - * @param bool $status - * The expected status of the node. - * @param int $created - * The expected creation time. - * @param int $changed - * The expected modification time. - * @param bool $promoted - * Whether the node is expected to be promoted to the front page. - * @param bool $sticky - * Whether the node is expected to be sticky. - */ - protected function assertEntity($id, $type, $langcode, $title, $uid, $status, $created, $changed, $promoted, $sticky) { - /** @var \Drupal\node\NodeInterface $node */ - $node = Node::load($id); - $this->assertTrue($node instanceof NodeInterface); - $this->assertIdentical($type, $node->getType()); - $this->assertIdentical($langcode, $node->langcode->value); - $this->assertIdentical($title, $node->getTitle()); - $this->assertIdentical($uid, $node->getOwnerId()); - $this->assertIdentical($status, $node->isPublished()); - $this->assertIdentical($created, $node->getCreatedTime()); - if (isset($changed)) { - $this->assertIdentical($changed, $node->getChangedTime()); - } - $this->assertIdentical($promoted, $node->isPromoted()); - $this->assertIdentical($sticky, $node->isSticky()); - } - - /** - * Asserts various aspects of a node revision. - * - * @param int $id - * The revision ID. - * @param string $title - * The expected title. - * @param int $uid - * The revision author ID. - * @param string $log - * The revision log message. - * @param int $timestamp - * The revision's time stamp. - */ - protected function assertRevision($id, $title, $uid, $log, $timestamp) { - $revision = \Drupal::entityManager()->getStorage('node')->loadRevision($id); - $this->assertTrue($revision instanceof NodeInterface); - $this->assertIdentical($title, $revision->getTitle()); - $this->assertIdentical($uid, $revision->getRevisionAuthor()->id()); - $this->assertIdentical($log, $revision->revision_log->value); - $this->assertIdentical($timestamp, $revision->getRevisionCreationTime()); - } - - /** - * Test node migration from Drupal 7 to 8. - */ - public function testNode() { - $this->assertEntity(1, 'test_content_type', 'en', 'A Node', '2', TRUE, '1421727515', '1441032132', TRUE, FALSE); - $this->assertRevision(1, 'A Node', '1', NULL, '1441032132'); - - $node = Node::load(1); - $this->assertTrue($node->field_boolean->value); - $this->assertIdentical('99-99-99-99', $node->field_phone->value); - // Use assertEqual() here instead, since SQLite interprets floats strictly. - $this->assertEqual('1', $node->field_float->value); - $this->assertIdentical('5', $node->field_integer->value); - $this->assertIdentical('Some more text', $node->field_text_list[0]->value); - $this->assertIdentical('7', $node->field_integer_list[0]->value); - $this->assertIdentical('qwerty', $node->field_text->value); - $this->assertIdentical('2', $node->field_file->target_id); - $this->assertIdentical('file desc', $node->field_file->description); - $this->assertTrue($node->field_file->display); - $this->assertIdentical('1', $node->field_images->target_id); - $this->assertIdentical('alt text', $node->field_images->alt); - $this->assertIdentical('title text', $node->field_images->title); - $this->assertIdentical('93', $node->field_images->width); - $this->assertIdentical('93', $node->field_images->height); - - $node = Node::load(2); - $this->assertIdentical("...is that it's the absolute best show ever. Trust me, I would know.", $node->body->value); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTitleLabelTest.php b/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTitleLabelTest.php deleted file mode 100644 index 93f3749..0000000 --- a/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTitleLabelTest.php +++ /dev/null @@ -1,59 +0,0 @@ -installConfig(static::$modules); - $this->installEntitySchema('node'); - $this->executeMigrations(['d7_node_type', 'd7_node_title_label']); - } - - /** - * Asserts various aspects of a base_field_override entity. - * - * @param string $id - * The override ID. - * @param string $label - * The label's expected (overridden) value. - */ - protected function assertEntity($id, $label) { - $override = BaseFieldOverride::load($id); - $this->assertTrue($override instanceof BaseFieldOverride); - /** @var \Drupal\Core\Field\Entity\BaseFieldOverride $override */ - $this->assertIdentical($label, $override->getLabel()); - } - - /** - * Tests migration of node title field overrides. - */ - public function testMigration() { - $this->assertEntity('node.article.title', 'Title'); - $this->assertEntity('node.blog.title', 'Title'); - $this->assertEntity('node.book.title', 'Title'); - $this->assertEntity('node.forum.title', 'Subject'); - $this->assertEntity('node.page.title', 'Title'); - $this->assertEntity('node.test_content_type.title', 'Title'); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTypeTest.php b/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTypeTest.php deleted file mode 100644 index c3706a9..0000000 --- a/core/modules/node/src/Tests/Migrate/d7/MigrateNodeTypeTest.php +++ /dev/null @@ -1,85 +0,0 @@ -installConfig(array('node')); - $this->executeMigration('d7_node_type'); - } - - /** - * Tests a single node type. - * - * @dataProvider testNodeTypeDataProvider - * @param string $id - * The node type ID. - * @param string $label - * The expected label. - * @param string $description - * The expected node type description. - * @param string $help - * The expected help text. - */ - protected function assertEntity($id, $label, $description, $help, $display_submitted, $new_revision, $body_label = NULL) { - /** @var \Drupal\node\NodeTypeInterface $entity */ - $entity = NodeType::load($id); - $this->assertTrue($entity instanceof NodeTypeInterface); - $this->assertIdentical($label, $entity->label()); - $this->assertIdentical($description, $entity->getDescription()); - $this->assertIdentical($help, $entity->getHelp()); - - $this->assertIdentical($display_submitted, $entity->displaySubmitted(), 'Submission info is displayed'); - $this->assertIdentical($new_revision, $entity->isNewRevision(), 'Is a new revision'); - - if ($body_label) { - /** @var \Drupal\field\FieldConfigInterface $body */ - $body = FieldConfig::load('node.' . $id . '.body'); - $this->assertTrue($body instanceof FieldConfigInterface); - $this->assertIdentical($body_label, $body->label()); - } - } - - /** - * Tests Drupal 7 node type to Drupal 8 migration. - */ - public function testNodeType() { - $this->assertEntity('article', 'Article', 'Use articles for time-sensitive content like news, press releases or blog posts.', 'Help text for articles', TRUE, FALSE, "Body"); - $this->assertEntity('blog', 'Blog entry', 'Use for multi-user blogs. Every user gets a personal blog.', 'Blog away, good sir!', TRUE, FALSE, 'Body'); - // book's display_submitted flag is not set, so it will default to TRUE. - $this->assertEntity('book', 'Book page', 'Books have a built-in hierarchical navigation. Use for handbooks or tutorials.', '', TRUE, TRUE, "Body"); - $this->assertEntity('forum', 'Forum topic', 'A forum topic starts a new discussion thread within a forum.', 'No name-calling, no flame wars. Be nice.', TRUE, FALSE, 'Body'); - $this->assertEntity('page', 'Basic page', "Use basic pages for your static content, such as an 'About us' page.", 'Help text for basic pages', FALSE, FALSE, "Body"); - // This node type does not carry a body field. - $this->assertEntity('test_content_type', 'Test content type', 'This is the description of the test content type.', 'Help text for test content type', FALSE, TRUE); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d7/NodeBuilderTest.php b/core/modules/node/src/Tests/Migrate/d7/NodeBuilderTest.php deleted file mode 100644 index 09172f7..0000000 --- a/core/modules/node/src/Tests/Migrate/d7/NodeBuilderTest.php +++ /dev/null @@ -1,41 +0,0 @@ -getProcess(); - $this->assertIdentical('field_boolean', $process['field_boolean'][0]['source']); - $this->assertIdentical('field_email', $process['field_email'][0]['source']); - $this->assertIdentical('field_phone', $process['field_phone'][0]['source']); - $this->assertIdentical('field_date', $process['field_date'][0]['source']); - $this->assertIdentical('field_date_with_end_time', $process['field_date_with_end_time'][0]['source']); - $this->assertIdentical('field_file', $process['field_file'][0]['source']); - $this->assertIdentical('field_float', $process['field_float'][0]['source']); - $this->assertIdentical('field_images', $process['field_images'][0]['source']); - $this->assertIdentical('field_integer', $process['field_integer'][0]['source']); - $this->assertIdentical('field_link', $process['field_link'][0]['source']); - $this->assertIdentical('field_text_list', $process['field_text_list'][0]['source']); - $this->assertIdentical('field_integer_list', $process['field_integer_list'][0]['source']); - $this->assertIdentical('field_long_text', $process['field_long_text'][0]['source']); - $this->assertIdentical('field_term_reference', $process['field_term_reference'][0]['source']); - $this->assertIdentical('field_text', $process['field_text'][0]['source']); - } - -} diff --git a/core/modules/node/src/Tests/Migrate/d7/NodeMigrateDeriverTest.php b/core/modules/node/src/Tests/Migrate/d7/NodeMigrateDeriverTest.php new file mode 100644 index 0000000..7a08d3f --- /dev/null +++ b/core/modules/node/src/Tests/Migrate/d7/NodeMigrateDeriverTest.php @@ -0,0 +1,40 @@ +getMigration('d7_node:test_content_type')->getProcess(); + $this->assertIdentical('field_boolean', $process['field_boolean'][0]['source']); + $this->assertIdentical('field_email', $process['field_email'][0]['source']); + $this->assertIdentical('field_phone', $process['field_phone'][0]['source']); + $this->assertIdentical('field_date', $process['field_date'][0]['source']); + $this->assertIdentical('field_date_with_end_time', $process['field_date_with_end_time'][0]['source']); + $this->assertIdentical('field_file', $process['field_file'][0]['source']); + $this->assertIdentical('field_float', $process['field_float'][0]['source']); + $this->assertIdentical('field_images', $process['field_images'][0]['source']); + $this->assertIdentical('field_integer', $process['field_integer'][0]['source']); + $this->assertIdentical('field_link', $process['field_link'][0]['source']); + $this->assertIdentical('field_text_list', $process['field_text_list'][0]['source']); + $this->assertIdentical('field_integer_list', $process['field_integer_list'][0]['source']); + $this->assertIdentical('field_long_text', $process['field_long_text'][0]['source']); + $this->assertIdentical('field_term_reference', $process['field_term_reference'][0]['source']); + $this->assertIdentical('field_text', $process['field_text'][0]['source']); + } + +} diff --git a/core/modules/node/src/Tests/MultiStepNodeFormBasicOptionsTest.php b/core/modules/node/src/Tests/MultiStepNodeFormBasicOptionsTest.php index 84ffbe2..9e3803f 100644 --- a/core/modules/node/src/Tests/MultiStepNodeFormBasicOptionsTest.php +++ b/core/modules/node/src/Tests/MultiStepNodeFormBasicOptionsTest.php @@ -7,6 +7,8 @@ namespace Drupal\node\Tests; use Drupal\Component\Utility\Unicode; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests the persistence of basic options through multiple steps. @@ -32,7 +34,7 @@ function testMultiStepNodeFormBasicOptions() { // Create an unlimited cardinality field. $this->fieldName = Unicode::strtolower($this->randomMachineName()); - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'field_name' => $this->fieldName, 'entity_type' => 'node', 'type' => 'text', @@ -40,12 +42,12 @@ function testMultiStepNodeFormBasicOptions() { ))->save(); // Attach an instance of the field to the page content type. - entity_create('field_config', array( + FieldConfig::create([ 'field_name' => $this->fieldName, 'entity_type' => 'node', 'bundle' => 'page', 'label' => $this->randomMachineName() . '_label', - ))->save(); + ])->save(); entity_get_form_display('node', 'page', 'default') ->setComponent($this->fieldName, array( 'type' => 'text_textfield', diff --git a/core/modules/node/src/Tests/NodeAccessFieldTest.php b/core/modules/node/src/Tests/NodeAccessFieldTest.php index ab490bb..d9f39b4 100644 --- a/core/modules/node/src/Tests/NodeAccessFieldTest.php +++ b/core/modules/node/src/Tests/NodeAccessFieldTest.php @@ -7,6 +7,8 @@ namespace Drupal\node\Tests; use Drupal\Component\Utility\Unicode; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests the interaction of the node access system with fields. @@ -54,16 +56,16 @@ protected function setUp() { // Add a custom field to the page content type. $this->fieldName = Unicode::strtolower($this->randomMachineName() . '_field_name'); - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'field_name' => $this->fieldName, 'entity_type' => 'node', 'type' => 'text' ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_name' => $this->fieldName, 'entity_type' => 'node', 'bundle' => 'page', - ))->save(); + ])->save(); entity_get_display('node', 'page', 'default') ->setComponent($this->fieldName) ->save(); diff --git a/core/modules/node/src/Tests/NodeAccessLanguageAwareCombinationTest.php b/core/modules/node/src/Tests/NodeAccessLanguageAwareCombinationTest.php index 3051af1..3d52bc7 100644 --- a/core/modules/node/src/Tests/NodeAccessLanguageAwareCombinationTest.php +++ b/core/modules/node/src/Tests/NodeAccessLanguageAwareCombinationTest.php @@ -8,9 +8,11 @@ namespace Drupal\node\Tests; use Drupal\Core\Language\LanguageInterface; +use Drupal\field\Entity\FieldConfig; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\node\Entity\NodeType; use Drupal\user\Entity\User; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests node access functionality with multiple languages and two node access @@ -55,7 +57,7 @@ protected function setUp() { // Create the 'private' field, which allows the node to be marked as private // (restricted access) in a given translation. - $field_storage = entity_create('field_storage_config', array( + $field_storage = FieldStorageConfig::create(array( 'field_name' => 'field_private', 'entity_type' => 'node', 'type' => 'boolean', @@ -63,7 +65,7 @@ protected function setUp() { )); $field_storage->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_storage' => $field_storage, 'bundle' => 'page', 'widget' => array( @@ -73,7 +75,7 @@ protected function setUp() { 'on_label' => 'Private', 'off_label' => 'Not private', ), - ))->save(); + ])->save(); // After enabling a node access module, the access table has to be rebuild. node_access_rebuild(); diff --git a/core/modules/node/src/Tests/NodeAccessLanguageAwareTest.php b/core/modules/node/src/Tests/NodeAccessLanguageAwareTest.php index 5770746..6715cc0 100644 --- a/core/modules/node/src/Tests/NodeAccessLanguageAwareTest.php +++ b/core/modules/node/src/Tests/NodeAccessLanguageAwareTest.php @@ -8,8 +8,10 @@ namespace Drupal\node\Tests; use Drupal\Core\Language\LanguageInterface; +use Drupal\field\Entity\FieldConfig; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\user\Entity\User; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests node_access and db_select() with node_access tag functionality with @@ -52,7 +54,7 @@ protected function setUp() { // Create the 'private' field, which allows the node to be marked as private // (restricted access) in a given translation. - $field_storage = entity_create('field_storage_config', array( + $field_storage = FieldStorageConfig::create(array( 'field_name' => 'field_private', 'entity_type' => 'node', 'type' => 'boolean', @@ -60,7 +62,7 @@ protected function setUp() { )); $field_storage->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_storage' => $field_storage, 'bundle' => 'page', 'widget' => array( @@ -70,7 +72,7 @@ protected function setUp() { 'on_label' => 'Private', 'off_label' => 'Not private', ), - ))->save(); + ])->save(); // After enabling a node access module, the access table has to be rebuild. node_access_rebuild(); diff --git a/core/modules/node/src/Tests/NodeAccessPagerTest.php b/core/modules/node/src/Tests/NodeAccessPagerTest.php index c442566..6eb9eff 100644 --- a/core/modules/node/src/Tests/NodeAccessPagerTest.php +++ b/core/modules/node/src/Tests/NodeAccessPagerTest.php @@ -10,6 +10,7 @@ use Drupal\comment\CommentInterface; use Drupal\comment\Tests\CommentTestTrait; use Drupal\simpletest\WebTestBase; +use Drupal\comment\Entity\Comment; /** * Tests access controlled node views have the right amount of comment pages. @@ -45,7 +46,7 @@ public function testCommentPager() { // Create 60 comments. for ($i = 0; $i < 60; $i++) { - $comment = entity_create('comment', array( + $comment = Comment::create(array( 'entity_id' => $node->id(), 'entity_type' => 'node', 'field_name' => 'comment', diff --git a/core/modules/node/src/Tests/NodeBodyFieldStorageTest.php b/core/modules/node/src/Tests/NodeBodyFieldStorageTest.php index ddf7559..2441a57 100644 --- a/core/modules/node/src/Tests/NodeBodyFieldStorageTest.php +++ b/core/modules/node/src/Tests/NodeBodyFieldStorageTest.php @@ -29,7 +29,6 @@ class NodeBodyFieldStorageTest extends KernelTestBase { protected function setUp() { parent::setUp(); $this->installSchema('system', 'sequences'); - $this->installSchema('system', array('router')); // Necessary for module uninstall. $this->installSchema('user', 'users_data'); $this->installEntitySchema('user'); diff --git a/core/modules/node/src/Tests/NodeCacheTagsTest.php b/core/modules/node/src/Tests/NodeCacheTagsTest.php index 5940d4c..b10bcb6 100644 --- a/core/modules/node/src/Tests/NodeCacheTagsTest.php +++ b/core/modules/node/src/Tests/NodeCacheTagsTest.php @@ -8,6 +8,8 @@ namespace Drupal\node\Tests; use Drupal\Core\Entity\EntityInterface; +use Drupal\node\Entity\Node; +use Drupal\node\Entity\NodeType; use Drupal\system\Tests\Entity\EntityWithUriCacheTagsTestBase; /** @@ -27,13 +29,13 @@ class NodeCacheTagsTest extends EntityWithUriCacheTagsTestBase { */ protected function createEntity() { // Create a "Camelids" node type. - entity_create('node_type', array( + NodeType::create([ 'name' => 'Camelids', 'type' => 'camelids', - ))->save(); + ])->save(); // Create a "Llama" node. - $node = entity_create('node', array('type' => 'camelids')); + $node = Node::create(['type' => 'camelids']); $node->setTitle('Llama') ->setPublished(TRUE) ->save(); diff --git a/core/modules/node/src/Tests/NodeCreationTest.php b/core/modules/node/src/Tests/NodeCreationTest.php index abe7ba7..d625e52 100644 --- a/core/modules/node/src/Tests/NodeCreationTest.php +++ b/core/modules/node/src/Tests/NodeCreationTest.php @@ -9,6 +9,7 @@ use Drupal\Core\Database\Database; use Drupal\Core\Language\LanguageInterface; +use Drupal\node\Entity\Node; /** * Create a node and test saving it. @@ -90,7 +91,7 @@ function testFailedPageCreation() { try { // An exception is generated by node_test_exception_node_insert() if the // title is 'testing_transaction_exception'. - entity_create('node', $edit)->save(); + Node::create($edit)->save(); $this->fail(t('Expected exception has not been thrown.')); } catch (\Exception $e) { diff --git a/core/modules/node/src/Tests/NodeEditFormTest.php b/core/modules/node/src/Tests/NodeEditFormTest.php index f2701a4..d6c40b6 100644 --- a/core/modules/node/src/Tests/NodeEditFormTest.php +++ b/core/modules/node/src/Tests/NodeEditFormTest.php @@ -8,6 +8,7 @@ namespace Drupal\node\Tests; use Drupal\node\NodeInterface; +use Drupal\user\Entity\User; /** * Create a node and test node edit functionality. @@ -205,10 +206,17 @@ protected function checkVariousAuthoredByValues(NodeInterface $node, $form_eleme // won't do. $this->assertTrue($uid === 0 || $uid === '0', 'Node authored by anonymous user.'); + // Go back to the edit form and check that the correct value is displayed + // in the author widget. + $this->drupalGet('node/' . $node->id() . '/edit'); + $anonymous_user = User::getAnonymousUser(); + $expected = $anonymous_user->label() . ' (' . $anonymous_user->id() . ')'; + $this->assertFieldByName($form_element_name, $expected, 'Authored by field displays the correct value for the anonymous user.'); + // Change the authored by field to another user's name (that is not // logged in). $edit[$form_element_name] = $this->webUser->getUsername(); - $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published')); + $this->drupalPostForm(NULL, $edit, t('Save and keep published')); $this->nodeStorage->resetCache(array($node->id())); $node = $this->nodeStorage->load($node->id()); $this->assertIdentical($node->getOwnerId(), $this->webUser->id(), 'Node authored by normal user.'); diff --git a/core/modules/node/src/Tests/NodeQueryAlterTest.php b/core/modules/node/src/Tests/NodeQueryAlterTest.php index d0d615e..d8e0640 100644 --- a/core/modules/node/src/Tests/NodeQueryAlterTest.php +++ b/core/modules/node/src/Tests/NodeQueryAlterTest.php @@ -73,6 +73,24 @@ function testNodeQueryAlterLowLevelWithAccess() { } /** + * Tests 'node_access' query alter with revision-enabled nodes. + */ + public function testNodeQueryAlterWithRevisions() { + // Execute a query that only deals with the 'node_revision' table. + try { + $query = \Drupal::entityTypeManager()->getStorage('node')->getQuery(); + $result = $query + ->allRevisions() + ->execute(); + + $this->assertEqual(count($result), 4, 'User with access can see correct nodes'); + } + catch (\Exception $e) { + $this->fail('Altered query is malformed'); + } + } + + /** * Tests 'node_access' query alter, for user without access. * * Verifies that a non-standard table alias can be used, and that a user diff --git a/core/modules/node/src/Tests/NodeSaveTest.php b/core/modules/node/src/Tests/NodeSaveTest.php index 3acbf82..7fcea9b 100644 --- a/core/modules/node/src/Tests/NodeSaveTest.php +++ b/core/modules/node/src/Tests/NodeSaveTest.php @@ -64,7 +64,7 @@ function testImport() { 'nid' => $test_nid, ); /** @var \Drupal\node\NodeInterface $node */ - $node = entity_create('node', $node); + $node = Node::create($node); $node->enforceIsNew(); $this->assertEqual($node->getOwnerId(), $this->webUser->id()); @@ -89,7 +89,7 @@ function testTimestamps() { 'title' => $this->randomMachineName(8), ); - entity_create('node', $edit)->save(); + Node::create($edit)->save(); $node = $this->drupalGetNodeByTitle($edit['title']); $this->assertEqual($node->getCreatedTime(), REQUEST_TIME, 'Creating a node sets default "created" timestamp.'); $this->assertEqual($node->getChangedTime(), REQUEST_TIME, 'Creating a node sets default "changed" timestamp.'); @@ -118,7 +118,7 @@ function testTimestamps() { 'changed' => 979534800, // Drupal 1.0 release. ); - entity_create('node', $edit)->save(); + Node::create($edit)->save(); $node = $this->drupalGetNodeByTitle($edit['title']); $this->assertEqual($node->getCreatedTime(), 280299600, 'Creating a node programmatically uses programmatically set "created" timestamp.'); $this->assertEqual($node->getChangedTime(), 979534800, 'Creating a node programmatically uses programmatically set "changed" timestamp.'); @@ -144,11 +144,11 @@ function testTimestamps() { */ function testDeterminingChanges() { // Initial creation. - $node = entity_create('node', array( + $node = Node::create([ 'uid' => $this->webUser->id(), 'type' => 'article', 'title' => 'test_changes', - )); + ]); $node->save(); // Update the node without applying changes. diff --git a/core/modules/node/src/Tests/NodeTokenReplaceTest.php b/core/modules/node/src/Tests/NodeTokenReplaceTest.php index bc6a049..9050de9 100644 --- a/core/modules/node/src/Tests/NodeTokenReplaceTest.php +++ b/core/modules/node/src/Tests/NodeTokenReplaceTest.php @@ -10,6 +10,8 @@ use Drupal\Component\Render\FormattableMarkup; use Drupal\Component\Utility\Html; use Drupal\Core\Render\BubbleableMetadata; +use Drupal\node\Entity\Node; +use Drupal\node\Entity\NodeType; use Drupal\system\Tests\System\TokenReplaceUnitTestBase; /** @@ -34,7 +36,7 @@ protected function setUp() { parent::setUp(); $this->installConfig(array('filter', 'node')); - $node_type = entity_create('node_type', array('type' => 'article', 'name' => 'Article')); + $node_type = NodeType::create(['type' => 'article', 'name' => 'Article']); $node_type->save(); node_add_body_field($node_type); } @@ -51,13 +53,13 @@ function testNodeTokenReplacement() { // Create a user and a node. $account = $this->createUser(); /* @var $node \Drupal\node\NodeInterface */ - $node = entity_create('node', array( + $node = Node::create([ 'type' => 'article', 'tnid' => 0, 'uid' => $account->id(), 'title' => 'Blinking Text', 'body' => [['value' => 'Regular NODE body for the test.', 'summary' => 'Fancy NODE summary.', 'format' => 'plain_text']], - )); + ]); $node->save(); // Generate and test tokens. @@ -110,12 +112,12 @@ function testNodeTokenReplacement() { } // Repeat for a node without a summary. - $node = entity_create('node', array( + $node = Node::create([ 'type' => 'article', 'uid' => $account->id(), 'title' => 'Blinking Text', 'body' => [['value' => 'A string that looks random like TR5c2I', 'format' => 'plain_text']], - )); + ]); $node->save(); // Generate and test token - use full body as expected value. diff --git a/core/modules/node/src/Tests/NodeTranslationUITest.php b/core/modules/node/src/Tests/NodeTranslationUITest.php index 0bb81c0..ca31f3b 100644 --- a/core/modules/node/src/Tests/NodeTranslationUITest.php +++ b/core/modules/node/src/Tests/NodeTranslationUITest.php @@ -30,7 +30,8 @@ class NodeTranslationUITest extends ContentTranslationUITestBase { 'theme', 'route', 'timezone', - 'url', + 'url.path', + 'url.query_args:_wrapper_format', 'user' ]; diff --git a/core/modules/node/src/Tests/NodeTypeTest.php b/core/modules/node/src/Tests/NodeTypeTest.php index 945c83f..8152cf5 100644 --- a/core/modules/node/src/Tests/NodeTypeTest.php +++ b/core/modules/node/src/Tests/NodeTypeTest.php @@ -10,7 +10,6 @@ use Drupal\field\Entity\FieldConfig; use Drupal\node\Entity\NodeType; use Drupal\Core\Url; -use Drupal\node\NodeTypeInterface; /** * Ensures that node type functions work correctly. @@ -126,7 +125,7 @@ function testNodeTypeEditing() { $this->assertRaw('Body', 'Body field was found.'); // Change the name through the API - /** @var NodeTypeInterface $node_type */ + /** @var \Drupal\node\NodeTypeInterface $node_type */ $node_type = NodeType::load('page'); $node_type->set('name', 'NewBar'); $node_type->save(); diff --git a/core/modules/node/src/Tests/NodeTypeTranslationTest.php b/core/modules/node/src/Tests/NodeTypeTranslationTest.php index 10c6865..487a5e5 100644 --- a/core/modules/node/src/Tests/NodeTypeTranslationTest.php +++ b/core/modules/node/src/Tests/NodeTypeTranslationTest.php @@ -14,6 +14,10 @@ /** * Ensures that node types translation work correctly. * + * Note that the child site is installed in French; therefore, when making + * assertions on translated text it is important to provide a langcode. This + * ensures the asserts pass regardless of the Drupal version. + * * @group node */ class NodeTypeTranslationTest extends WebTestBase { @@ -105,12 +109,16 @@ public function testNodeTypeTranslation() { // Check the name is translated without admin theme for editing. $this->drupalPostForm('admin/appearance', array('use_admin_theme' => '0'), t('Save configuration')); $this->drupalGet("$langcode/node/add/$type"); - $this->assertRaw(t('Create @name', array('@name' => $translated_name))); + // This is a Spanish page, so ensure the text asserted is translated in + // Spanish and not French by adding the langcode option. + $this->assertRaw(t('Create @name', array('@name' => $translated_name), array('langcode' => $langcode))); // Check the name is translated with admin theme for editing. $this->drupalPostForm('admin/appearance', array('use_admin_theme' => '1'), t('Save configuration')); $this->drupalGet("$langcode/node/add/$type"); - $this->assertRaw(t('Create @name', array('@name' => $translated_name))); + // This is a Spanish page, so ensure the text asserted is translated in + // Spanish and not French by adding the langcode option. + $this->assertRaw(t('Create @name', array('@name' => $translated_name), array('langcode' => $langcode))); } /** @@ -128,17 +136,19 @@ public function testNodeTypeTitleLabelTranslation() { // Assert that the title label is displayed on the translation form with the right value. $this->drupalGet("admin/structure/types/manage/$type/translate/$langcode/add"); - $this->assertRaw(t('Label')); - $this->assertRaw(t('Edited title')); + $this->assertText('Edited title'); // Translate the title label. $this->drupalPostForm(NULL, array("translation[config_names][core.base_field_override.node.$type.title][label]" => 'Translated title'), t('Save translation')); - // Assert that the right title label is displayed on the node add form. + // Assert that the right title label is displayed on the node add form. The + // translations are created in this test; therefore, the assertions do not + // use t(). If t() were used then the correct langcodes would need to be + // provided. $this->drupalGet("node/add/$type"); - $this->assertRaw(t('Edited title')); + $this->assertText('Edited title'); $this->drupalGet("$langcode/node/add/$type"); - $this->assertRaw(t('Translated title')); + $this->assertText('Translated title'); } } diff --git a/core/modules/node/src/Tests/NodeValidationTest.php b/core/modules/node/src/Tests/NodeValidationTest.php index 9f6bccc..c6e26cc 100644 --- a/core/modules/node/src/Tests/NodeValidationTest.php +++ b/core/modules/node/src/Tests/NodeValidationTest.php @@ -7,6 +7,8 @@ namespace Drupal\node\Tests; +use Drupal\node\Entity\Node; +use Drupal\node\Entity\NodeType; use Drupal\system\Tests\Entity\EntityUnitTestBase; /** @@ -30,7 +32,7 @@ protected function setUp() { parent::setUp(); // Create a node type for testing. - $type = entity_create('node_type', array('type' => 'page', 'name' => 'page')); + $type = NodeType::create(['type' => 'page', 'name' => 'page']); $type->save(); } @@ -39,7 +41,7 @@ protected function setUp() { */ public function testValidation() { $this->createUser(); - $node = entity_create('node', array('type' => 'page', 'title' => 'test', 'uid' => 1)); + $node = Node::create(['type' => 'page', 'title' => 'test', 'uid' => 1]); $violations = $node->validate(); $this->assertEqual(count($violations), 0, 'No violations when validating a default node.'); diff --git a/core/modules/node/src/Tests/PagePreviewTest.php b/core/modules/node/src/Tests/PagePreviewTest.php index 903636a..3c9debe 100644 --- a/core/modules/node/src/Tests/PagePreviewTest.php +++ b/core/modules/node/src/Tests/PagePreviewTest.php @@ -15,6 +15,8 @@ use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; use Drupal\node\Entity\NodeType; +use Drupal\taxonomy\Entity\Term; +use Drupal\taxonomy\Entity\Vocabulary; /** * Tests the node entity preview functionality. @@ -48,24 +50,24 @@ protected function setUp() { $this->drupalLogin($web_user); // Add a vocabulary so we can test different view modes. - $vocabulary = entity_create('taxonomy_vocabulary', array( + $vocabulary = Vocabulary::create([ 'name' => $this->randomMachineName(), 'description' => $this->randomMachineName(), 'vid' => $this->randomMachineName(), 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, 'help' => '', - )); + ]); $vocabulary->save(); $this->vocabulary = $vocabulary; // Add a term to the vocabulary. - $term = entity_create('taxonomy_term', array( + $term = Term::create([ 'name' => $this->randomMachineName(), 'description' => $this->randomMachineName(), 'vid' => $this->vocabulary->id(), 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - )); + ]); $term->save(); $this->term = $term; diff --git a/core/modules/node/src/Tests/Views/NidArgumentTest.php b/core/modules/node/src/Tests/Views/NidArgumentTest.php deleted file mode 100644 index 456dd74..0000000 --- a/core/modules/node/src/Tests/Views/NidArgumentTest.php +++ /dev/null @@ -1,85 +0,0 @@ -installEntitySchema('node'); - $this->installEntitySchema('user'); - $this->installConfig(['node', 'field']); - - ViewTestData::createTestViews(get_class($this), ['node_test_views']); - } - - /** - * Test the nid argument. - */ - public function testNidArgument() { - $view = Views::getView('test_nid_argument'); - $view->setDisplay(); - - $node1 = Node::create([ - 'type' => 'default', - 'title' => $this->randomMachineName(), - ]); - $node1->save(); - $node2 = Node::create([ - 'type' => 'default', - 'title' => $this->randomMachineName(), - ]); - $node2->save(); - - $view->preview(); - $this->assertEqual(count($view->result), 2, 'Found the expected number of results.'); - - // Set an the second node id as an argument. - $view->destroy(); - $view->preview('default', [$node2->id()]); - // Verify that the title is overridden. - $this->assertEqual($view->getTitle(), $node2->getTitle()); - // Verify that the argument filtering works. - $this->assertEqual(count($view->result), 1, 'Found the expected number of results.'); - $this->assertEqual($node2->id(), (string) $view->style_plugin->getField(0, 'nid'), 'Found the correct nid.'); - - // Verify that setting a non-existing id as argument results in no nodes - // being shown. - $view->destroy(); - $view->preview('default', [22]); - $this->assertEqual(count($view->result), 0, 'Found the expected number of results.'); - } - -} diff --git a/core/modules/node/src/Tests/Views/NodeFieldTokensTest.php b/core/modules/node/src/Tests/Views/NodeFieldTokensTest.php index 5cc96c7..46fbb23 100644 --- a/core/modules/node/src/Tests/Views/NodeFieldTokensTest.php +++ b/core/modules/node/src/Tests/Views/NodeFieldTokensTest.php @@ -6,6 +6,8 @@ */ namespace Drupal\node\Tests\Views; +use Drupal\node\Entity\Node; +use Drupal\node\Entity\NodeType; /** * Tests replacement of Views tokens supplied by the Node module. @@ -28,7 +30,7 @@ class NodeFieldTokensTest extends NodeTestBase { public function testViewsTokenReplacement() { // Create the Article content type with a standard body field. /* @var $node_type \Drupal\node\NodeTypeInterface */ - $node_type = entity_create('node_type', ['type' => 'article', 'name' => 'Article']); + $node_type = NodeType::create(['type' => 'article', 'name' => 'Article']); $node_type->save(); node_add_body_field($node_type); @@ -38,7 +40,7 @@ public function testViewsTokenReplacement() { $summary = $this->randomMachineName(16); /** @var $node \Drupal\node\NodeInterface */ - $node = entity_create('node', [ + $node = Node::create([ 'type' => 'article', 'tnid' => 0, 'uid' => $account->id(), diff --git a/core/modules/node/src/Tests/Views/NodeViewsFieldAccessTest.php b/core/modules/node/src/Tests/Views/NodeViewsFieldAccessTest.php deleted file mode 100644 index 379cf30..0000000 --- a/core/modules/node/src/Tests/Views/NodeViewsFieldAccessTest.php +++ /dev/null @@ -1,79 +0,0 @@ -installEntitySchema('node'); - } - - /** - * Check access for node fields. - */ - public function testNodeFields() { - $user = User::create([ - 'name' => 'test user', - ]); - $user->save(); - NodeType::create([ - 'type' => 'article', - 'name' => 'Article', - ])->save(); - $node = Node::create([ - 'type' => 'article', - 'title' => 'Test title', - 'uid' => $user->id(), - 'status' => 1, - 'promote' => 1, - 'sticky' => 0, - 'created' => 123456, - ]); - - $node->save(); - - // @todo Expand the test coverage in https://www.drupal.org/node/2464635 - - $this->assertFieldAccess('node', 'nid', $node->id()); - $this->assertFieldAccess('node', 'uuid', $node->uuid()); - $this->assertFieldAccess('node', 'vid', $node->id()); - $this->assertFieldAccess('node', 'type', $node->type->entity->label()); - $this->assertFieldAccess('node', 'langcode', $node->language()->getName()); - $this->assertFieldAccess('node', 'title', 'Test title'); - $this->assertFieldAccess('node', 'uid', $user->getUsername()); - // @todo Don't we want to display Published / Unpublished by default, - // see https://www.drupal.org/node/2465623 - $this->assertFieldAccess('node', 'status', 'On'); - $this->assertFieldAccess('node', 'promote', 'On'); - $this->assertFieldAccess('node', 'sticky', 'Off'); - - // $this->assertFieldAccess('node', 'created', \Drupal::service('date.formatter')->format(123456)); - // $this->assertFieldAccess('node', 'changed', \Drupal::service('date.formatter')->format(REQUEST_TIME)); - } - -} diff --git a/core/modules/node/src/Tests/Views/RevisionCreateTimestampTest.php b/core/modules/node/src/Tests/Views/RevisionCreateTimestampTest.php deleted file mode 100644 index 54dd1a4..0000000 --- a/core/modules/node/src/Tests/Views/RevisionCreateTimestampTest.php +++ /dev/null @@ -1,80 +0,0 @@ -installSchema('node', 'node_access'); - $this->installEntitySchema('node'); - $this->installEntitySchema('user'); - - if ($import_test_views) { - ViewTestData::createTestViews(get_class($this), ['node_test_views']); - } - } - - public function testRevisionCreateTimestampView() { - $node_type = NodeType::create([ - 'type' => 'article', - 'label' => 'Article', - ]); - $node_type->save(); - $node = Node::create([ - 'title' => 'Test node', - 'type' => 'article', - 'revision_timestamp' => 1000, - ]); - $node->save(); - - $node->setRevisionCreationTime(1200); - $node->setNewRevision(TRUE); - $node->save(); - - $node->setRevisionCreationTime(1400); - $node->setNewRevision(TRUE); - $node->save(); - - $view = Views::getView('test_node_revision_timestamp'); - $this->executeView($view); - - $this->assertIdenticalResultset($view, [ - ['vid' => 3, 'revision_timestamp' => 1400], - ['vid' => 2, 'revision_timestamp' => 1200], - ['vid' => 1, 'revision_timestamp' => 1000], - ], ['vid' => 'vid', 'revision_timestamp' => 'revision_timestamp']); - } - - -} diff --git a/core/modules/node/src/Tests/Views/RevisionRelationshipsTest.php b/core/modules/node/src/Tests/Views/RevisionRelationshipsTest.php index c36b377..306df50 100644 --- a/core/modules/node/src/Tests/Views/RevisionRelationshipsTest.php +++ b/core/modules/node/src/Tests/Views/RevisionRelationshipsTest.php @@ -4,6 +4,7 @@ * @file * Contains \Drupal\node\Tests\Views\RevisionRelationshipsTest. */ + namespace Drupal\node\Tests\Views; use Drupal\views\Views; diff --git a/core/modules/node/tests/src/Kernel/Migrate/MigrateNodeStubTest.php b/core/modules/node/tests/src/Kernel/Migrate/MigrateNodeStubTest.php new file mode 100644 index 0000000..af4e0f8 --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/MigrateNodeStubTest.php @@ -0,0 +1,48 @@ +installEntitySchema('node'); + // Need at least one node type present. + NodeType::create([ + 'type' => 'testnodetype', + 'name' => 'Test node type', + ])->save(); + } + + /** + * Tests creation of node stubs. + */ + public function testStub() { + $this->performStubTest('node'); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeBundleSettingsTest.php b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeBundleSettingsTest.php new file mode 100644 index 0000000..b98d0f3 --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeBundleSettingsTest.php @@ -0,0 +1,66 @@ +installConfig(['node']); + $this->executeMigration('d6_node_type'); + + // Create a config entity that already exists. + BaseFieldOverride::create([ + 'field_name' => 'promote', + 'entity_type' => 'node', + 'bundle' => 'page', + ])->save(); + + $this->executeMigrations([ + 'd6_node_setting_promote', + 'd6_node_setting_status', + 'd6_node_setting_sticky' + ]); + } + + /** + * Tests Drupal 6 node type settings to Drupal 8 migration. + */ + public function testNodeBundleSettings() { + // Test settings on test_page bundle. + $node = Node::create(['type' => 'test_page']); + $this->assertIdentical(1, $node->status->value); + $this->assertIdentical(1, $node->promote->value); + $this->assertIdentical(1, $node->sticky->value); + + // Test settings for test_story bundle. + $node = Node::create(['type' => 'test_story']); + $this->assertIdentical(1, $node->status->value); + $this->assertIdentical(1, $node->promote->value); + $this->assertIdentical(0, $node->sticky->value); + + // Test settings for the test_event bundle. + $node = Node::create(['type' => 'test_event']); + $this->assertIdentical(0, $node->status->value); + $this->assertIdentical(0, $node->promote->value); + $this->assertIdentical(1, $node->sticky->value); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeConfigsTest.php b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeConfigsTest.php new file mode 100644 index 0000000..242ed8f --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeConfigsTest.php @@ -0,0 +1,39 @@ +executeMigration('d6_node_settings'); + } + + /** + * Tests Drupal 6 node settings to Drupal 8 migration. + */ + public function testNodeSettings() { + $config = $this->config('node.settings'); + $this->assertIdentical(FALSE, $config->get('use_admin_theme')); + $this->assertConfigSchema(\Drupal::service('config.typed'), 'node.settings', $config->get()); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeSettingPromoteTest.php b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeSettingPromoteTest.php new file mode 100644 index 0000000..2231b08 --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeSettingPromoteTest.php @@ -0,0 +1,37 @@ +installConfig(['node']); + $this->executeMigration('d6_node_type'); + $this->executeMigration('d6_node_setting_promote'); + } + + /** + * Tests migration of the promote checkbox's settings. + */ + public function testMigration() { + $this->assertIdentical('Promoted to front page', BaseFieldOverride::load('node.article.promote')->label()); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeSettingStickyTest.php b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeSettingStickyTest.php new file mode 100644 index 0000000..904d29e --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeSettingStickyTest.php @@ -0,0 +1,37 @@ +installConfig(['node']); + $this->executeMigration('d6_node_type'); + $this->executeMigration('d6_node_setting_sticky'); + } + + /** + * Tests migration of the sticky checkbox's settings. + */ + public function testMigration() { + $this->assertIdentical('Sticky at the top of lists', BaseFieldOverride::load('node.article.sticky')->label()); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTest.php b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTest.php new file mode 100644 index 0000000..64088a8 --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTest.php @@ -0,0 +1,158 @@ +setUpMigratedFiles(); + $this->installSchema('file', ['file_usage']); + $this->executeMigrations(['d6_node']); + } + + /** + * Test node migration from Drupal 6 to 8. + */ + public function testNode() { + $node = Node::load(1); + $this->assertIdentical('1', $node->id(), 'Node 1 loaded.'); + $this->assertIdentical('und', $node->langcode->value); + $this->assertIdentical('test', $node->body->value); + $this->assertIdentical('test', $node->body->summary); + $this->assertIdentical('filtered_html', $node->body->format); + $this->assertIdentical('story', $node->getType(), 'Node has the correct bundle.'); + $this->assertIdentical('Test title', $node->getTitle(), 'Node has the correct title.'); + $this->assertIdentical('1388271197', $node->getCreatedTime(), 'Node has the correct created time.'); + $this->assertIdentical(FALSE, $node->isSticky()); + $this->assertIdentical('1', $node->getOwnerId()); + $this->assertIdentical('1420861423', $node->getRevisionCreationTime()); + + /** @var \Drupal\node\NodeInterface $node_revision */ + $node_revision = \Drupal::entityManager()->getStorage('node')->loadRevision(1); + $this->assertIdentical('Test title', $node_revision->getTitle()); + $this->assertIdentical('1', $node_revision->getRevisionAuthor()->id(), 'Node revision has the correct user'); + // This is empty on the first revision. + $this->assertIdentical(NULL, $node_revision->revision_log->value); + $this->assertIdentical('This is a shared text field', $node->field_test->value); + $this->assertIdentical('filtered_html', $node->field_test->format); + $this->assertIdentical('10', $node->field_test_two->value); + $this->assertIdentical('20', $node->field_test_two[1]->value); + + $this->assertIdentical('42.42', $node->field_test_three->value, 'Single field second value is correct.'); + $this->assertIdentical('3412', $node->field_test_integer_selectlist[0]->value); + $this->assertIdentical('1', $node->field_test_identical1->value, 'Integer value is correct'); + $this->assertIdentical('1', $node->field_test_identical2->value, 'Integer value is correct'); + $this->assertIdentical('This is a field with exclude unset.', $node->field_test_exclude_unset->value, 'Field with exclude unset is correct.'); + + // Test that link fields are migrated. + $this->assertIdentical('https://www.drupal.org/project/drupal', $node->field_test_link->uri); + $this->assertIdentical('Drupal project page', $node->field_test_link->title); + $this->assertIdentical(['target' => '_blank'], $node->field_test_link->options['attributes']); + + // Test the file field meta. + $this->assertIdentical('desc', $node->field_test_filefield->description); + $this->assertIdentical('5', $node->field_test_filefield->target_id); + + $node = Node::load(2); + $this->assertIdentical('Test title rev 3', $node->getTitle()); + $this->assertIdentical('test rev 3', $node->body->value); + $this->assertIdentical('filtered_html', $node->body->format); + + // Test that link fields are migrated. + $this->assertIdentical('http://groups.drupal.org/', $node->field_test_link->uri); + $this->assertIdentical('Drupal Groups', $node->field_test_link->title); + $this->assertIdentical([], $node->field_test_link->options['attributes']); + + // Rerun migration with invalid link attributes and a different URL and + // title. If only the attributes are changed the error does not occur. + Database::getConnection('default', 'migrate') + ->update('content_type_story') + ->fields([ + 'field_test_link_url' => 'https://www.drupal.org/node/2127611', + 'field_test_link_title' => 'Migrate API in Drupal 8', + 'field_test_link_attributes' => '', + ]) + ->condition('nid', '2') + ->condition('vid', '3') + ->execute(); + + $this->rerunMigration(); + $node = Node::load(2); + $this->assertIdentical('https://www.drupal.org/node/2127611', $node->field_test_link->uri); + $this->assertIdentical('Migrate API in Drupal 8', $node->field_test_link->title); + $this->assertIdentical([], $node->field_test_link->options['attributes']); + + // Test that we can re-import using the EntityContentBase destination. + $title = $this->rerunMigration(); + $node = Node::load(2); + $this->assertIdentical($title, $node->getTitle()); + // Test multi-column fields are correctly upgraded. + $this->assertIdentical('test rev 3', $node->body->value); + $this->assertIdentical('full_html', $node->body->format); + + // Now insert a row indicating a failure and set to update later. + $title = $this->rerunMigration(array( + 'sourceid1' => 2, + 'destid1' => NULL, + 'source_row_status' => MigrateIdMapInterface::STATUS_NEEDS_UPDATE, + )); + $node = Node::load(2); + $this->assertIdentical($title, $node->getTitle()); + } + + /** + * Execute the migration a second time. + * + * @param array $new_row + * An optional row to be inserted into the id map. + * + * @return string + * The new title in the source for vid 3. + */ + protected function rerunMigration($new_row = []) { + $title = $this->randomString(); + $source_connection = Database::getConnection('default', 'migrate'); + $source_connection->update('node_revisions') + ->fields(array( + 'title' => $title, + 'format' => 2, + )) + ->condition('vid', 3) + ->execute(); + $migration = $this->getMigration('d6_node:story'); + $table_name = $migration->getIdMap()->mapTableName(); + $default_connection = \Drupal::database(); + $default_connection->truncate($table_name)->execute(); + if ($new_row) { + $hash = $migration->getIdMap()->getSourceIDsHash(['nid' => $new_row['sourceid1']]); + $new_row['source_ids_hash'] = $hash; + $default_connection->insert($table_name) + ->fields($new_row) + ->execute(); + } + $this->executeMigration($migration); + return $title; + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTestBase.php b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTestBase.php new file mode 100644 index 0000000..3567cc2 --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTestBase.php @@ -0,0 +1,43 @@ +installEntitySchema('node'); + $this->installConfig(['node']); + $this->installSchema('node', ['node_access']); + $this->installSchema('system', ['sequences']); + + // Create a new user which needs to have UID 1, because that is expected by + // the assertions from + // \Drupal\migrate_drupal\Tests\d6\MigrateNodeRevisionTest. + User::create([ + 'uid' => 1, + 'name' => $this->randomMachineName(), + 'status' => 1, + ])->enforceIsNew()->save(); + + $this->migrateUsers(FALSE); + $this->migrateFields(); + $this->executeMigration('d6_node_settings'); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTypeTest.php b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTypeTest.php new file mode 100644 index 0000000..ddfce03 --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateNodeTypeTest.php @@ -0,0 +1,74 @@ +installConfig(['node']); + $this->executeMigration('d6_node_type'); + } + + /** + * Tests Drupal 6 node type to Drupal 8 migration. + */ + public function testNodeType() { + $id_map = $this->getMigration('d6_node_type')->getIdMap(); + // Test the test_page content type. + $node_type_page = NodeType::load('test_page'); + $this->assertIdentical('test_page', $node_type_page->id(), 'Node type test_page loaded'); + $this->assertIdentical(TRUE, $node_type_page->displaySubmitted()); + $this->assertIdentical(FALSE, $node_type_page->isNewRevision()); + $this->assertIdentical(DRUPAL_OPTIONAL, $node_type_page->getPreviewMode()); + $this->assertIdentical($id_map->lookupDestinationID(array('test_page')), array('test_page')); + + // Test we have a body field. + $field = FieldConfig::loadByName('node', 'test_page', 'body'); + $this->assertIdentical('This is the body field label', $field->getLabel(), 'Body field was found.'); + + // Test the test_story content type. + $node_type_story = NodeType::load('test_story'); + $this->assertIdentical('test_story', $node_type_story->id(), 'Node type test_story loaded'); + + $this->assertIdentical(TRUE, $node_type_story->displaySubmitted()); + $this->assertIdentical(FALSE, $node_type_story->isNewRevision()); + $this->assertIdentical(DRUPAL_OPTIONAL, $node_type_story->getPreviewMode()); + $this->assertIdentical($id_map->lookupDestinationID(array('test_story')), array('test_story')); + + // Test we don't have a body field. + $field = FieldConfig::loadByName('node', 'test_story', 'body'); + $this->assertIdentical(NULL, $field, 'No body field found'); + + // Test the test_event content type. + $node_type_event = NodeType::load('test_event'); + $this->assertIdentical('test_event', $node_type_event->id(), 'Node type test_event loaded'); + + $this->assertIdentical(TRUE, $node_type_event->displaySubmitted()); + $this->assertIdentical(TRUE, $node_type_event->isNewRevision()); + $this->assertIdentical(DRUPAL_OPTIONAL, $node_type_event->getPreviewMode()); + $this->assertIdentical($id_map->lookupDestinationID(array('test_event')), array('test_event')); + + // Test we have a body field. + $field = FieldConfig::loadByName('node', 'test_event', 'body'); + $this->assertIdentical('Body', $field->getLabel(), 'Body field was found.'); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateViewModesTest.php b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateViewModesTest.php new file mode 100644 index 0000000..1a40e15 --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d6/MigrateViewModesTest.php @@ -0,0 +1,40 @@ +executeMigration('d6_view_modes'); + } + + /** + * Tests Drupal 6 view modes to Drupal 8 migration. + */ + public function testViewModes() { + // Test a new view mode. + $view_mode = EntityViewMode::load('node.preview'); + $this->assertIdentical(FALSE, is_null($view_mode), 'Preview view mode loaded.'); + $this->assertIdentical('Preview', $view_mode->label(), 'View mode has correct label.'); + // Test the ID map. + $this->assertIdentical(array('node', 'preview'), $this->getMigration('d6_view_modes')->getIdMap()->lookupDestinationID(array(1))); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeSettingsTest.php b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeSettingsTest.php new file mode 100644 index 0000000..10a9f7e --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeSettingsTest.php @@ -0,0 +1,45 @@ +executeMigration('d7_node_settings'); + } + + /** + * Tests migration of node variables to node.settings config object. + */ + public function testAggregatorSettings() { + $config = $this->config('node.settings'); + $this->assertEqual(1, $config->get('use_admin_theme')); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php new file mode 100644 index 0000000..e548fa0 --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTest.php @@ -0,0 +1,152 @@ +installEntitySchema('node'); + $this->installEntitySchema('comment'); + $this->installEntitySchema('taxonomy_term'); + $this->installEntitySchema('file'); + $this->installConfig(static::$modules); + $this->installSchema('node', ['node_access']); + $this->installSchema('system', ['sequences']); + + $this->executeMigrations([ + 'd7_user_role', + 'd7_user', + 'd7_node_type', + 'd7_comment_type', + 'd7_field', + 'd7_field_instance', + 'd7_node:test_content_type', + 'd7_node:article', + ]); + } + + /** + * Asserts various aspects of a node. + * + * @param string $id + * The node ID. + * @param string $type + * The node type. + * @param string $langcode + * The expected language code. + * @param string $title + * The expected title. + * @param int $uid + * The expected author ID. + * @param bool $status + * The expected status of the node. + * @param int $created + * The expected creation time. + * @param int $changed + * The expected modification time. + * @param bool $promoted + * Whether the node is expected to be promoted to the front page. + * @param bool $sticky + * Whether the node is expected to be sticky. + */ + protected function assertEntity($id, $type, $langcode, $title, $uid, $status, $created, $changed, $promoted, $sticky) { + /** @var \Drupal\node\NodeInterface $node */ + $node = Node::load($id); + $this->assertTrue($node instanceof NodeInterface); + $this->assertIdentical($type, $node->getType()); + $this->assertIdentical($langcode, $node->langcode->value); + $this->assertIdentical($title, $node->getTitle()); + $this->assertIdentical($uid, $node->getOwnerId()); + $this->assertIdentical($status, $node->isPublished()); + $this->assertIdentical($created, $node->getCreatedTime()); + if (isset($changed)) { + $this->assertIdentical($changed, $node->getChangedTime()); + } + $this->assertIdentical($promoted, $node->isPromoted()); + $this->assertIdentical($sticky, $node->isSticky()); + } + + /** + * Asserts various aspects of a node revision. + * + * @param int $id + * The revision ID. + * @param string $title + * The expected title. + * @param int $uid + * The revision author ID. + * @param string $log + * The revision log message. + * @param int $timestamp + * The revision's time stamp. + */ + protected function assertRevision($id, $title, $uid, $log, $timestamp) { + $revision = \Drupal::entityManager()->getStorage('node')->loadRevision($id); + $this->assertTrue($revision instanceof NodeInterface); + $this->assertIdentical($title, $revision->getTitle()); + $this->assertIdentical($uid, $revision->getRevisionAuthor()->id()); + $this->assertIdentical($log, $revision->revision_log->value); + $this->assertIdentical($timestamp, $revision->getRevisionCreationTime()); + } + + /** + * Test node migration from Drupal 7 to 8. + */ + public function testNode() { + $this->assertEntity(1, 'test_content_type', 'en', 'A Node', '2', TRUE, '1421727515', '1441032132', TRUE, FALSE); + $this->assertRevision(1, 'A Node', '1', NULL, '1441032132'); + + $node = Node::load(1); + $this->assertTrue($node->field_boolean->value); + $this->assertIdentical('99-99-99-99', $node->field_phone->value); + // Use assertEqual() here instead, since SQLite interprets floats strictly. + $this->assertEqual('1', $node->field_float->value); + $this->assertIdentical('5', $node->field_integer->value); + $this->assertIdentical('Some more text', $node->field_text_list[0]->value); + $this->assertIdentical('7', $node->field_integer_list[0]->value); + $this->assertIdentical('qwerty', $node->field_text->value); + $this->assertIdentical('2', $node->field_file->target_id); + $this->assertIdentical('file desc', $node->field_file->description); + $this->assertTrue($node->field_file->display); + $this->assertIdentical('1', $node->field_images->target_id); + $this->assertIdentical('alt text', $node->field_images->alt); + $this->assertIdentical('title text', $node->field_images->title); + $this->assertIdentical('93', $node->field_images->width); + $this->assertIdentical('93', $node->field_images->height); + + $node = Node::load(2); + $this->assertIdentical("...is that it's the absolute best show ever. Trust me, I would know.", $node->body->value); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTitleLabelTest.php b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTitleLabelTest.php new file mode 100644 index 0000000..ccf2533 --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTitleLabelTest.php @@ -0,0 +1,59 @@ +installConfig(static::$modules); + $this->installEntitySchema('node'); + $this->executeMigrations(['d7_node_type', 'd7_node_title_label']); + } + + /** + * Asserts various aspects of a base_field_override entity. + * + * @param string $id + * The override ID. + * @param string $label + * The label's expected (overridden) value. + */ + protected function assertEntity($id, $label) { + $override = BaseFieldOverride::load($id); + $this->assertTrue($override instanceof BaseFieldOverride); + /** @var \Drupal\Core\Field\Entity\BaseFieldOverride $override */ + $this->assertIdentical($label, $override->getLabel()); + } + + /** + * Tests migration of node title field overrides. + */ + public function testMigration() { + $this->assertEntity('node.article.title', 'Title'); + $this->assertEntity('node.blog.title', 'Title'); + $this->assertEntity('node.book.title', 'Title'); + $this->assertEntity('node.forum.title', 'Subject'); + $this->assertEntity('node.page.title', 'Title'); + $this->assertEntity('node.test_content_type.title', 'Title'); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTypeTest.php b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTypeTest.php new file mode 100644 index 0000000..059b2c9 --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Migrate/d7/MigrateNodeTypeTest.php @@ -0,0 +1,85 @@ +installConfig(array('node')); + $this->executeMigration('d7_node_type'); + } + + /** + * Tests a single node type. + * + * @dataProvider testNodeTypeDataProvider + * @param string $id + * The node type ID. + * @param string $label + * The expected label. + * @param string $description + * The expected node type description. + * @param string $help + * The expected help text. + */ + protected function assertEntity($id, $label, $description, $help, $display_submitted, $new_revision, $body_label = NULL) { + /** @var \Drupal\node\NodeTypeInterface $entity */ + $entity = NodeType::load($id); + $this->assertTrue($entity instanceof NodeTypeInterface); + $this->assertIdentical($label, $entity->label()); + $this->assertIdentical($description, $entity->getDescription()); + $this->assertIdentical($help, $entity->getHelp()); + + $this->assertIdentical($display_submitted, $entity->displaySubmitted(), 'Submission info is displayed'); + $this->assertIdentical($new_revision, $entity->isNewRevision(), 'Is a new revision'); + + if ($body_label) { + /** @var \Drupal\field\FieldConfigInterface $body */ + $body = FieldConfig::load('node.' . $id . '.body'); + $this->assertTrue($body instanceof FieldConfigInterface); + $this->assertIdentical($body_label, $body->label()); + } + } + + /** + * Tests Drupal 7 node type to Drupal 8 migration. + */ + public function testNodeType() { + $this->assertEntity('article', 'Article', 'Use articles for time-sensitive content like news, press releases or blog posts.', 'Help text for articles', TRUE, FALSE, "Body"); + $this->assertEntity('blog', 'Blog entry', 'Use for multi-user blogs. Every user gets a personal blog.', 'Blog away, good sir!', TRUE, FALSE, 'Body'); + // book's display_submitted flag is not set, so it will default to TRUE. + $this->assertEntity('book', 'Book page', 'Books have a built-in hierarchical navigation. Use for handbooks or tutorials.', '', TRUE, TRUE, "Body"); + $this->assertEntity('forum', 'Forum topic', 'A forum topic starts a new discussion thread within a forum.', 'No name-calling, no flame wars. Be nice.', TRUE, FALSE, 'Body'); + $this->assertEntity('page', 'Basic page', "Use basic pages for your static content, such as an 'About us' page.", 'Help text for basic pages', FALSE, FALSE, "Body"); + // This node type does not carry a body field. + $this->assertEntity('test_content_type', 'Test content type', 'This is the description of the test content type.', 'Help text for test content type', FALSE, TRUE); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Views/NidArgumentTest.php b/core/modules/node/tests/src/Kernel/Views/NidArgumentTest.php new file mode 100644 index 0000000..26ac790 --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Views/NidArgumentTest.php @@ -0,0 +1,85 @@ +installEntitySchema('node'); + $this->installEntitySchema('user'); + $this->installConfig(['node', 'field']); + + ViewTestData::createTestViews(get_class($this), ['node_test_views']); + } + + /** + * Test the nid argument. + */ + public function testNidArgument() { + $view = Views::getView('test_nid_argument'); + $view->setDisplay(); + + $node1 = Node::create([ + 'type' => 'default', + 'title' => $this->randomMachineName(), + ]); + $node1->save(); + $node2 = Node::create([ + 'type' => 'default', + 'title' => $this->randomMachineName(), + ]); + $node2->save(); + + $view->preview(); + $this->assertEqual(count($view->result), 2, 'Found the expected number of results.'); + + // Set an the second node id as an argument. + $view->destroy(); + $view->preview('default', [$node2->id()]); + // Verify that the title is overridden. + $this->assertEqual($view->getTitle(), $node2->getTitle()); + // Verify that the argument filtering works. + $this->assertEqual(count($view->result), 1, 'Found the expected number of results.'); + $this->assertEqual($node2->id(), (string) $view->style_plugin->getField(0, 'nid'), 'Found the correct nid.'); + + // Verify that setting a non-existing id as argument results in no nodes + // being shown. + $view->destroy(); + $view->preview('default', [22]); + $this->assertEqual(count($view->result), 0, 'Found the expected number of results.'); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Views/NodeViewsFieldAccessTest.php b/core/modules/node/tests/src/Kernel/Views/NodeViewsFieldAccessTest.php new file mode 100644 index 0000000..ea2185f --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Views/NodeViewsFieldAccessTest.php @@ -0,0 +1,79 @@ +installEntitySchema('node'); + } + + /** + * Check access for node fields. + */ + public function testNodeFields() { + $user = User::create([ + 'name' => 'test user', + ]); + $user->save(); + NodeType::create([ + 'type' => 'article', + 'name' => 'Article', + ])->save(); + $node = Node::create([ + 'type' => 'article', + 'title' => 'Test title', + 'uid' => $user->id(), + 'status' => 1, + 'promote' => 1, + 'sticky' => 0, + 'created' => 123456, + ]); + + $node->save(); + + // @todo Expand the test coverage in https://www.drupal.org/node/2464635 + + $this->assertFieldAccess('node', 'nid', $node->id()); + $this->assertFieldAccess('node', 'uuid', $node->uuid()); + $this->assertFieldAccess('node', 'vid', $node->id()); + $this->assertFieldAccess('node', 'type', $node->type->entity->label()); + $this->assertFieldAccess('node', 'langcode', $node->language()->getName()); + $this->assertFieldAccess('node', 'title', 'Test title'); + $this->assertFieldAccess('node', 'uid', $user->getUsername()); + // @todo Don't we want to display Published / Unpublished by default, + // see https://www.drupal.org/node/2465623 + $this->assertFieldAccess('node', 'status', 'On'); + $this->assertFieldAccess('node', 'promote', 'On'); + $this->assertFieldAccess('node', 'sticky', 'Off'); + + // $this->assertFieldAccess('node', 'created', \Drupal::service('date.formatter')->format(123456)); + // $this->assertFieldAccess('node', 'changed', \Drupal::service('date.formatter')->format(REQUEST_TIME)); + } + +} diff --git a/core/modules/node/tests/src/Kernel/Views/RevisionCreateTimestampTest.php b/core/modules/node/tests/src/Kernel/Views/RevisionCreateTimestampTest.php new file mode 100644 index 0000000..44beaac --- /dev/null +++ b/core/modules/node/tests/src/Kernel/Views/RevisionCreateTimestampTest.php @@ -0,0 +1,80 @@ +installSchema('node', 'node_access'); + $this->installEntitySchema('node'); + $this->installEntitySchema('user'); + + if ($import_test_views) { + ViewTestData::createTestViews(get_class($this), ['node_test_views']); + } + } + + public function testRevisionCreateTimestampView() { + $node_type = NodeType::create([ + 'type' => 'article', + 'label' => 'Article', + ]); + $node_type->save(); + $node = Node::create([ + 'title' => 'Test node', + 'type' => 'article', + 'revision_timestamp' => 1000, + ]); + $node->save(); + + $node->setRevisionCreationTime(1200); + $node->setNewRevision(TRUE); + $node->save(); + + $node->setRevisionCreationTime(1400); + $node->setNewRevision(TRUE); + $node->save(); + + $view = Views::getView('test_node_revision_timestamp'); + $this->executeView($view); + + $this->assertIdenticalResultset($view, [ + ['vid' => 3, 'revision_timestamp' => 1400], + ['vid' => 2, 'revision_timestamp' => 1200], + ['vid' => 1, 'revision_timestamp' => 1000], + ], ['vid' => 'vid', 'revision_timestamp' => 'revision_timestamp']); + } + + +} diff --git a/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php b/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php index 870ad91..ff2e25c 100644 --- a/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php +++ b/core/modules/options/src/Plugin/Field/FieldType/ListFloatItem.php @@ -104,7 +104,7 @@ public static function simplifyAllowedValues(array $structured_values) { // Cast the value to a float first so that .5 and 0.5 are the same value // and then cast to a string so that values like 0.5 can be used as array // keys. - // @see http://php.net/manual/en/language.types.array.php + // @see http://php.net/manual/language.types.array.php $values[(string) (float) $item['value']] = $item['label']; } return $values; diff --git a/core/modules/options/src/Tests/OptionsDynamicValuesTestBase.php b/core/modules/options/src/Tests/OptionsDynamicValuesTestBase.php index 0bc99f0..ed1565d 100644 --- a/core/modules/options/src/Tests/OptionsDynamicValuesTestBase.php +++ b/core/modules/options/src/Tests/OptionsDynamicValuesTestBase.php @@ -7,7 +7,10 @@ namespace Drupal\options\Tests; +use Drupal\field\Entity\FieldConfig; use Drupal\field\Tests\FieldTestBase; +use Drupal\field\Entity\FieldStorageConfig; +use Drupal\entity_test\Entity\EntityTestRev; /** * Base class for testing allowed values of options fields. @@ -39,7 +42,7 @@ protected function setUp() { parent::setUp(); $field_name = 'test_options'; - $this->fieldStorage = entity_create('field_storage_config', [ + $this->fieldStorage = FieldStorageConfig::create([ 'field_name' => $field_name, 'entity_type' => 'entity_test_rev', 'type' => 'list_string', @@ -50,7 +53,7 @@ protected function setUp() { ]); $this->fieldStorage->save(); - $this->field = entity_create('field_config', [ + $this->field = FieldConfig::create([ 'field_name' => $field_name, 'entity_type' => 'entity_test_rev', 'bundle' => 'entity_test_rev', @@ -68,7 +71,7 @@ protected function setUp() { 'user_id' => mt_rand(1, 10), 'name' => $this->randomMachineName(), ]; - $this->entity = entity_create('entity_test_rev', $values); + $this->entity = EntityTestRev::create($values); $this->entity->save(); $this->test = [ 'label' => $this->entity->label(), diff --git a/core/modules/options/src/Tests/OptionsFieldTest.php b/core/modules/options/src/Tests/OptionsFieldTest.php deleted file mode 100644 index a7d3ffe..0000000 --- a/core/modules/options/src/Tests/OptionsFieldTest.php +++ /dev/null @@ -1,101 +0,0 @@ -getForm($entity); - $this->assertTrue(!empty($form[$this->fieldName]['widget'][1]), 'Option 1 exists'); - $this->assertTrue(!empty($form[$this->fieldName]['widget'][2]), 'Option 2 exists'); - $this->assertTrue(!empty($form[$this->fieldName]['widget'][3]), 'Option 3 exists'); - - // Use one of the values in an actual entity, and check that this value - // cannot be removed from the list. - $entity = entity_create('entity_test'); - $entity->{$this->fieldName}->value = 1; - $entity->save(); - $this->fieldStorage->setSetting('allowed_values', [2 => 'Two']); - try { - $this->fieldStorage->save(); - $this->fail(t('Cannot update a list field storage to not include keys with existing data.')); - } - catch (FieldStorageDefinitionUpdateForbiddenException $e) { - $this->pass(t('Cannot update a list field storage to not include keys with existing data.')); - } - // Empty the value, so that we can actually remove the option. - unset($entity->{$this->fieldName}); - $entity->save(); - - // Removed options do not appear. - $this->fieldStorage->setSetting('allowed_values', [2 => 'Two']); - $this->fieldStorage->save(); - $entity = entity_create('entity_test'); - $form = \Drupal::service('entity.form_builder')->getForm($entity); - $this->assertTrue(empty($form[$this->fieldName]['widget'][1]), 'Option 1 does not exist'); - $this->assertTrue(!empty($form[$this->fieldName]['widget'][2]), 'Option 2 exists'); - $this->assertTrue(empty($form[$this->fieldName]['widget'][3]), 'Option 3 does not exist'); - - // Completely new options appear. - $this->fieldStorage->setSetting('allowed_values', [10 => 'Update', 20 => 'Twenty']); - $this->fieldStorage->save(); - // The entity holds an outdated field object with the old allowed values - // setting, so we need to reinitialize the entity object. - $entity = entity_create('entity_test'); - $form = \Drupal::service('entity.form_builder')->getForm($entity); - $this->assertTrue(empty($form[$this->fieldName]['widget'][1]), 'Option 1 does not exist'); - $this->assertTrue(empty($form[$this->fieldName]['widget'][2]), 'Option 2 does not exist'); - $this->assertTrue(empty($form[$this->fieldName]['widget'][3]), 'Option 3 does not exist'); - $this->assertTrue(!empty($form[$this->fieldName]['widget'][10]), 'Option 10 exists'); - $this->assertTrue(!empty($form[$this->fieldName]['widget'][20]), 'Option 20 exists'); - - // Options are reset when a new field with the same name is created. - $this->fieldStorage->delete(); - entity_create('field_storage_config', $this->fieldStorageDefinition)->save(); - entity_create('field_config', array( - 'field_name' => $this->fieldName, - 'entity_type' => 'entity_test', - 'bundle' => 'entity_test', - 'required' => TRUE, - ))->save(); - entity_get_form_display('entity_test', 'entity_test', 'default') - ->setComponent($this->fieldName, array( - 'type' => 'options_buttons', - )) - ->save(); - $entity = entity_create('entity_test'); - $form = \Drupal::service('entity.form_builder')->getForm($entity); - $this->assertTrue(!empty($form[$this->fieldName]['widget'][1]), 'Option 1 exists'); - $this->assertTrue(!empty($form[$this->fieldName]['widget'][2]), 'Option 2 exists'); - $this->assertTrue(!empty($form[$this->fieldName]['widget'][3]), 'Option 3 exists'); - - // Test the generateSampleValue() method. - $entity = entity_create('entity_test'); - $entity->{$this->fieldName}->generateSampleItems(); - $this->entityValidateAndSave($entity); - } -} diff --git a/core/modules/options/src/Tests/OptionsFieldUITest.php b/core/modules/options/src/Tests/OptionsFieldUITest.php index d4219c6..360f548 100644 --- a/core/modules/options/src/Tests/OptionsFieldUITest.php +++ b/core/modules/options/src/Tests/OptionsFieldUITest.php @@ -7,6 +7,7 @@ namespace Drupal\options\Tests; +use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; use Drupal\field\Tests\FieldTestBase; @@ -267,16 +268,16 @@ function testOptionsTrimmedValuesText() { */ protected function createOptionsField($type) { // Create a field. - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'field_name' => $this->fieldName, 'entity_type' => 'node', 'type' => $type, ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_name' => $this->fieldName, 'entity_type' => 'node', 'bundle' => $this->type, - ))->save(); + ])->save(); entity_get_form_display('node', $this->type, 'default')->setComponent($this->fieldName)->save(); diff --git a/core/modules/options/src/Tests/OptionsFieldUnitTestBase.php b/core/modules/options/src/Tests/OptionsFieldUnitTestBase.php deleted file mode 100644 index fcfe4f8..0000000 --- a/core/modules/options/src/Tests/OptionsFieldUnitTestBase.php +++ /dev/null @@ -1,85 +0,0 @@ -container->get('router.builder')->rebuild(); - - $this->fieldStorageDefinition = array( - 'field_name' => $this->fieldName, - 'entity_type' => 'entity_test', - 'type' => 'list_integer', - 'cardinality' => 1, - 'settings' => array( - 'allowed_values' => array(1 => 'One', 2 => 'Two', 3 => 'Three'), - ), - ); - $this->fieldStorage = entity_create('field_storage_config', $this->fieldStorageDefinition); - $this->fieldStorage->save(); - - $this->field = entity_create('field_config', array( - 'field_storage' => $this->fieldStorage, - 'bundle' => 'entity_test', - )); - $this->field->save(); - - entity_get_form_display('entity_test', 'entity_test', 'default') - ->setComponent($this->fieldName, array( - 'type' => 'options_buttons', - )) - ->save(); - } - -} diff --git a/core/modules/options/src/Tests/OptionsFormattersTest.php b/core/modules/options/src/Tests/OptionsFormattersTest.php deleted file mode 100644 index 8d97ab4..0000000 --- a/core/modules/options/src/Tests/OptionsFormattersTest.php +++ /dev/null @@ -1,44 +0,0 @@ -{$this->fieldName}->value = 1; - - $items = $entity->get($this->fieldName); - - $build = $items->view(); - $this->assertEqual($build['#formatter'], 'list_default', 'Ensure to fall back to the default formatter.'); - $this->assertEqual($build[0]['#markup'], 'One'); - - $build = $items->view(array('type' => 'list_key')); - $this->assertEqual($build['#formatter'], 'list_key', 'The chosen formatter is used.'); - $this->assertEqual((string) $build[0]['#markup'], 1); - } - -} diff --git a/core/modules/options/src/Tests/OptionsWidgetsTest.php b/core/modules/options/src/Tests/OptionsWidgetsTest.php index 93293ca..58b666d 100644 --- a/core/modules/options/src/Tests/OptionsWidgetsTest.php +++ b/core/modules/options/src/Tests/OptionsWidgetsTest.php @@ -7,7 +7,10 @@ namespace Drupal\options\Tests; +use Drupal\entity_test\Entity\EntityTest; +use Drupal\field\Entity\FieldConfig; use Drupal\field\Tests\FieldTestBase; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests the Options widgets. @@ -42,7 +45,7 @@ protected function setUp() { parent::setUp(); // Field storage with cardinality 1. - $this->card1 = entity_create('field_storage_config', [ + $this->card1 = FieldStorageConfig::create([ 'field_name' => 'card_1', 'entity_type' => 'entity_test', 'type' => 'list_integer', @@ -62,7 +65,7 @@ protected function setUp() { $this->card1->save(); // Field storage with cardinality 2. - $this->card2 = entity_create('field_storage_config', [ + $this->card2 = FieldStorageConfig::create([ 'field_name' => 'card_2', 'entity_type' => 'entity_test', 'type' => 'list_integer', @@ -88,7 +91,7 @@ protected function setUp() { */ function testRadioButtons() { // Create an instance of the 'single value' field. - $field = entity_create('field_config', [ + $field = FieldConfig::create([ 'field_storage' => $this->card1, 'bundle' => 'entity_test', ]); @@ -100,7 +103,7 @@ function testRadioButtons() { ->save(); // Create an entity. - $entity = entity_create('entity_test', [ + $entity = EntityTest::create([ 'user_id' => 1, 'name' => $this->randomMachineName(), ]); @@ -145,10 +148,10 @@ function testRadioButtons() { */ function testCheckBoxes() { // Create an instance of the 'multiple values' field. - $field = entity_create('field_config', array( + $field = FieldConfig::create([ 'field_storage' => $this->card2, 'bundle' => 'entity_test', - )); + ]); $field->save(); entity_get_form_display('entity_test', 'entity_test', 'default') ->setComponent($this->card2->getName(), array( @@ -157,7 +160,7 @@ function testCheckBoxes() { ->save(); // Create an entity. - $entity = entity_create('entity_test', array( + $entity = EntityTest::create(array( 'user_id' => 1, 'name' => $this->randomMachineName(), )); @@ -234,11 +237,11 @@ function testCheckBoxes() { */ function testSelectListSingle() { // Create an instance of the 'single value' field. - $field = entity_create('field_config', array( + $field = FieldConfig::create([ 'field_storage' => $this->card1, 'bundle' => 'entity_test', 'required' => TRUE, - )); + ]); $field->save(); entity_get_form_display('entity_test', 'entity_test', 'default') ->setComponent($this->card1->getName(), array( @@ -247,7 +250,7 @@ function testSelectListSingle() { ->save(); // Create an entity. - $entity = entity_create('entity_test', array( + $entity = EntityTest::create(array( 'user_id' => 1, 'name' => $this->randomMachineName(), )); @@ -334,10 +337,10 @@ function testSelectListSingle() { */ function testSelectListMultiple() { // Create an instance of the 'multiple values' field. - $field = entity_create('field_config', array( + $field = FieldConfig::create([ 'field_storage' => $this->card2, 'bundle' => 'entity_test', - )); + ]); $field->save(); entity_get_form_display('entity_test', 'entity_test', 'default') ->setComponent($this->card2->getName(), array( @@ -346,7 +349,7 @@ function testSelectListMultiple() { ->save(); // Create an entity. - $entity = entity_create('entity_test', array( + $entity = EntityTest::create(array( 'user_id' => 1, 'name' => $this->randomMachineName(), )); @@ -455,7 +458,7 @@ function testSelectListMultiple() { */ function testEmptyValue() { // Create an instance of the 'single value' field. - $field = entity_create('field_config', [ + $field = FieldConfig::create([ 'field_storage' => $this->card1, 'bundle' => 'entity_test', ]); @@ -469,7 +472,7 @@ function testEmptyValue() { ->save(); // Create an entity. - $entity = entity_create('entity_test', [ + $entity = EntityTest::create([ 'user_id' => 1, 'name' => $this->randomMachineName(), ]); diff --git a/core/modules/options/src/Tests/Views/OptionsListArgumentTest.php b/core/modules/options/src/Tests/Views/OptionsListArgumentTest.php deleted file mode 100644 index 198ebd1..0000000 --- a/core/modules/options/src/Tests/Views/OptionsListArgumentTest.php +++ /dev/null @@ -1,54 +0,0 @@ -executeView($view, [1]); - - $resultset = [ - ['nid' => $this->nodes[0]->nid->value], - ['nid' => $this->nodes[1]->nid->value], - ]; - - $column_map = ['nid' => 'nid']; - $this->assertIdenticalResultset($view, $resultset, $column_map); - - $view = Views::getView('test_options_list_argument_string'); - $this->executeView($view, ['man', 'woman']); - - $resultset = [ - ['nid' => $this->nodes[0]->nid->value], - ['nid' => $this->nodes[1]->nid->value], - ]; - - $column_map = ['nid' => 'nid']; - $this->assertIdenticalResultset($view, $resultset, $column_map); - } - -} diff --git a/core/modules/options/src/Tests/Views/OptionsListFilterTest.php b/core/modules/options/src/Tests/Views/OptionsListFilterTest.php deleted file mode 100644 index 164bf15..0000000 --- a/core/modules/options/src/Tests/Views/OptionsListFilterTest.php +++ /dev/null @@ -1,43 +0,0 @@ -executeView($view); - - $resultset = [ - ['nid' => $this->nodes[0]->nid->value], - ['nid' => $this->nodes[1]->nid->value], - ]; - - $column_map = ['nid' => 'nid']; - $this->assertIdenticalResultset($view, $resultset, $column_map); - } - -} diff --git a/core/modules/options/src/Tests/Views/OptionsTestBase.php b/core/modules/options/src/Tests/Views/OptionsTestBase.php deleted file mode 100644 index 972c00b..0000000 --- a/core/modules/options/src/Tests/Views/OptionsTestBase.php +++ /dev/null @@ -1,125 +0,0 @@ -mockStandardInstall(); - - ViewTestData::createTestViews(get_class($this), ['options_test_views']); - - $settings = []; - $settings['type'] = 'article'; - $settings['title'] = $this->randomString(); - $settings['field_test_list_string'][]['value'] = $this->fieldValues[0]; - $settings['field_test_list_integer'][]['value'] = 0; - - $node = Node::create($settings); - $node->save(); - - $this->nodes[] = $node; - $node = $node->createDuplicate(); - $node->save(); - $this->nodes[] = $node; - } - - /** - * Provides a workaround for the inability to use the standard profile. - * - * @see https://www.drupal.org/node/1708692 - */ - protected function mockStandardInstall() { - $this->installEntitySchema('user'); - $this->installEntitySchema('node'); - - NodeType::create( - ['type' => 'article'] - )->save(); - $this->fieldValues = [ - $this->randomMachineName(), - $this->randomMachineName(), - ]; - - $this->fieldNames = ['field_test_list_string', 'field_test_list_integer']; - - // Create two field entities. - FieldStorageConfig::create([ - 'field_name' => $this->fieldNames[0], - 'entity_type' => 'node', - 'type' => 'list_string', - 'cardinality' => 1, - 'settings' => [ - 'allowed_values' => [ - $this->fieldValues[0] => $this->fieldValues[0], - $this->fieldValues[1] => $this->fieldValues[1], - ], - ], - ])->save(); - FieldStorageConfig::create([ - 'field_name' => $this->fieldNames[1], - 'entity_type' => 'node', - 'type' => 'list_integer', - 'cardinality' => 1, - 'settings' => [ - 'allowed_values' => [ - $this->fieldValues[0], - $this->fieldValues[1], - ], - ], - ])->save(); - foreach ($this->fieldNames as $field_name) { - FieldConfig::create([ - 'field_name' => $field_name, - 'entity_type' => 'node', - 'label' => 'Test options list field', - 'bundle' => 'article', - ])->save(); - } - } - -} diff --git a/core/modules/options/src/Tests/Views/ViewsDataTest.php b/core/modules/options/src/Tests/Views/ViewsDataTest.php deleted file mode 100644 index f743f4c..0000000 --- a/core/modules/options/src/Tests/Views/ViewsDataTest.php +++ /dev/null @@ -1,66 +0,0 @@ -fieldStorage = entity_create('field_storage_config', [ - 'field_name' => $field_name, - 'entity_type' => 'entity_test', - 'type' => 'list_string', - 'cardinality' => 1, - 'settings' => [ - 'allowed_values_function' => 'options_test_dynamic_values_callback', - ], - ]); - $this->fieldStorage->save(); - - $this->field = entity_create('field_config', [ - 'field_name' => $field_name, - 'entity_type' => 'entity_test', - 'bundle' => 'entity_test', - 'required' => TRUE, - ])->save(); - } - - /** - * Tests the option module's implementation of hook_field_views_data(). - */ - public function testOptionsFieldViewsData() { - $field_data = \Drupal::service('views.views_data')->get('entity_test__test_options'); - - // Check that the options module has properly overridden default views data. - $test_options_field = $field_data['test_options_value']; - $this->assertEqual($test_options_field['argument']['id'], 'string_list_field', 'Argument handler is properly set for fields with allowed value callbacks.'); - $this->assertEqual($test_options_field['filter']['id'], 'list_field', 'Filter handler is properly set for fields with allowed value callbacks.'); - } - -} diff --git a/core/modules/options/tests/src/Kernel/OptionsFieldTest.php b/core/modules/options/tests/src/Kernel/OptionsFieldTest.php new file mode 100644 index 0000000..c1c04fe --- /dev/null +++ b/core/modules/options/tests/src/Kernel/OptionsFieldTest.php @@ -0,0 +1,104 @@ +getForm($entity); + $this->assertTrue(!empty($form[$this->fieldName]['widget'][1]), 'Option 1 exists'); + $this->assertTrue(!empty($form[$this->fieldName]['widget'][2]), 'Option 2 exists'); + $this->assertTrue(!empty($form[$this->fieldName]['widget'][3]), 'Option 3 exists'); + + // Use one of the values in an actual entity, and check that this value + // cannot be removed from the list. + $entity = EntityTest::create(); + $entity->{$this->fieldName}->value = 1; + $entity->save(); + $this->fieldStorage->setSetting('allowed_values', [2 => 'Two']); + try { + $this->fieldStorage->save(); + $this->fail(t('Cannot update a list field storage to not include keys with existing data.')); + } + catch (FieldStorageDefinitionUpdateForbiddenException $e) { + $this->pass(t('Cannot update a list field storage to not include keys with existing data.')); + } + // Empty the value, so that we can actually remove the option. + unset($entity->{$this->fieldName}); + $entity->save(); + + // Removed options do not appear. + $this->fieldStorage->setSetting('allowed_values', [2 => 'Two']); + $this->fieldStorage->save(); + $entity = EntityTest::create(); + $form = \Drupal::service('entity.form_builder')->getForm($entity); + $this->assertTrue(empty($form[$this->fieldName]['widget'][1]), 'Option 1 does not exist'); + $this->assertTrue(!empty($form[$this->fieldName]['widget'][2]), 'Option 2 exists'); + $this->assertTrue(empty($form[$this->fieldName]['widget'][3]), 'Option 3 does not exist'); + + // Completely new options appear. + $this->fieldStorage->setSetting('allowed_values', [10 => 'Update', 20 => 'Twenty']); + $this->fieldStorage->save(); + // The entity holds an outdated field object with the old allowed values + // setting, so we need to reinitialize the entity object. + $entity = EntityTest::create(); + $form = \Drupal::service('entity.form_builder')->getForm($entity); + $this->assertTrue(empty($form[$this->fieldName]['widget'][1]), 'Option 1 does not exist'); + $this->assertTrue(empty($form[$this->fieldName]['widget'][2]), 'Option 2 does not exist'); + $this->assertTrue(empty($form[$this->fieldName]['widget'][3]), 'Option 3 does not exist'); + $this->assertTrue(!empty($form[$this->fieldName]['widget'][10]), 'Option 10 exists'); + $this->assertTrue(!empty($form[$this->fieldName]['widget'][20]), 'Option 20 exists'); + + // Options are reset when a new field with the same name is created. + $this->fieldStorage->delete(); + FieldStorageConfig::create($this->fieldStorageDefinition)->save(); + FieldConfig::create([ + 'field_name' => $this->fieldName, + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + 'required' => TRUE, + ])->save(); + entity_get_form_display('entity_test', 'entity_test', 'default') + ->setComponent($this->fieldName, array( + 'type' => 'options_buttons', + )) + ->save(); + $entity = EntityTest::create(); + $form = \Drupal::service('entity.form_builder')->getForm($entity); + $this->assertTrue(!empty($form[$this->fieldName]['widget'][1]), 'Option 1 exists'); + $this->assertTrue(!empty($form[$this->fieldName]['widget'][2]), 'Option 2 exists'); + $this->assertTrue(!empty($form[$this->fieldName]['widget'][3]), 'Option 3 exists'); + + // Test the generateSampleValue() method. + $entity = EntityTest::create(); + $entity->{$this->fieldName}->generateSampleItems(); + $this->entityValidateAndSave($entity); + } +} diff --git a/core/modules/options/tests/src/Kernel/OptionsFieldUnitTestBase.php b/core/modules/options/tests/src/Kernel/OptionsFieldUnitTestBase.php new file mode 100644 index 0000000..33eab18 --- /dev/null +++ b/core/modules/options/tests/src/Kernel/OptionsFieldUnitTestBase.php @@ -0,0 +1,86 @@ +container->get('router.builder')->rebuild(); + + $this->fieldStorageDefinition = array( + 'field_name' => $this->fieldName, + 'entity_type' => 'entity_test', + 'type' => 'list_integer', + 'cardinality' => 1, + 'settings' => array( + 'allowed_values' => array(1 => 'One', 2 => 'Two', 3 => 'Three'), + ), + ); + $this->fieldStorage = FieldStorageConfig::create($this->fieldStorageDefinition); + $this->fieldStorage->save(); + + $this->field = FieldConfig::create([ + 'field_storage' => $this->fieldStorage, + 'bundle' => 'entity_test', + ]); + $this->field->save(); + + entity_get_form_display('entity_test', 'entity_test', 'default') + ->setComponent($this->fieldName, array( + 'type' => 'options_buttons', + )) + ->save(); + } + +} diff --git a/core/modules/options/tests/src/Kernel/OptionsFormattersTest.php b/core/modules/options/tests/src/Kernel/OptionsFormattersTest.php new file mode 100644 index 0000000..53eb36d --- /dev/null +++ b/core/modules/options/tests/src/Kernel/OptionsFormattersTest.php @@ -0,0 +1,46 @@ +{$this->fieldName}->value = 1; + + $items = $entity->get($this->fieldName); + + $build = $items->view(); + $this->assertEqual($build['#formatter'], 'list_default', 'Ensure to fall back to the default formatter.'); + $this->assertEqual($build[0]['#markup'], 'One'); + + $build = $items->view(array('type' => 'list_key')); + $this->assertEqual($build['#formatter'], 'list_key', 'The chosen formatter is used.'); + $this->assertEqual((string) $build[0]['#markup'], 1); + } + +} diff --git a/core/modules/options/tests/src/Kernel/Views/FileViewsDataTest.php b/core/modules/options/tests/src/Kernel/Views/FileViewsDataTest.php new file mode 100644 index 0000000..3d25b8f --- /dev/null +++ b/core/modules/options/tests/src/Kernel/Views/FileViewsDataTest.php @@ -0,0 +1,97 @@ + 'entity_test', + 'field_name' => 'field_base_file', + 'type' => 'file', + ))->save(); + FieldConfig::create(array( + 'entity_type' => 'entity_test', + 'field_name' => 'field_base_file', + 'bundle' => 'entity_test', + ))->save(); + // Check the generated views data. + $views_data = Views::viewsData()->get('entity_test__field_base_file'); + $relationship = $views_data['field_base_file_target_id']['relationship']; + $this->assertEqual($relationship['id'], 'standard'); + $this->assertEqual($relationship['base'], 'file_managed'); + $this->assertEqual($relationship['base field'], 'fid'); + $this->assertEqual($relationship['entity type'], 'file'); + // Check the backwards reference. + $views_data = Views::viewsData()->get('file_managed'); + $relationship = $views_data['reverse_field_base_file_entity_test']['relationship']; + $this->assertEqual($relationship['id'], 'entity_reverse'); + $this->assertEqual($relationship['base'], 'entity_test'); + $this->assertEqual($relationship['base field'], 'id'); + $this->assertEqual($relationship['field table'], 'entity_test__field_base_file'); + $this->assertEqual($relationship['field field'], 'field_base_file_target_id'); + $this->assertEqual($relationship['field_name'], 'field_base_file'); + $this->assertEqual($relationship['entity_type'], 'entity_test'); + $this->assertEqual($relationship['join_extra'][0], ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE]); + + // Create file field to entity_test_mul. + FieldStorageConfig::create(array( + 'entity_type' => 'entity_test_mul', + 'field_name' => 'field_data_file', + 'type' => 'file', + ))->save(); + FieldConfig::create(array( + 'entity_type' => 'entity_test_mul', + 'field_name' => 'field_data_file', + 'bundle' => 'entity_test_mul', + ))->save(); + // Check the generated views data. + $views_data = Views::viewsData()->get('entity_test_mul__field_data_file'); + $relationship = $views_data['field_data_file_target_id']['relationship']; + $this->assertEqual($relationship['id'], 'standard'); + $this->assertEqual($relationship['base'], 'file_managed'); + $this->assertEqual($relationship['base field'], 'fid'); + $this->assertEqual($relationship['entity type'], 'file'); + // Check the backwards reference. + $views_data = Views::viewsData()->get('file_managed'); + $relationship = $views_data['reverse_field_data_file_entity_test_mul']['relationship']; + $this->assertEqual($relationship['id'], 'entity_reverse'); + $this->assertEqual($relationship['base'], 'entity_test_mul_property_data'); + $this->assertEqual($relationship['base field'], 'id'); + $this->assertEqual($relationship['field table'], 'entity_test_mul__field_data_file'); + $this->assertEqual($relationship['field field'], 'field_data_file_target_id'); + $this->assertEqual($relationship['field_name'], 'field_data_file'); + $this->assertEqual($relationship['entity_type'], 'entity_test_mul'); + $this->assertEqual($relationship['join_extra'][0], ['field' => 'deleted', 'value' => 0, 'numeric' => TRUE]); + } + +} diff --git a/core/modules/options/tests/src/Kernel/Views/OptionsListArgumentTest.php b/core/modules/options/tests/src/Kernel/Views/OptionsListArgumentTest.php new file mode 100644 index 0000000..15302ad --- /dev/null +++ b/core/modules/options/tests/src/Kernel/Views/OptionsListArgumentTest.php @@ -0,0 +1,54 @@ +executeView($view, [1]); + + $resultset = [ + ['nid' => $this->nodes[0]->nid->value], + ['nid' => $this->nodes[1]->nid->value], + ]; + + $column_map = ['nid' => 'nid']; + $this->assertIdenticalResultset($view, $resultset, $column_map); + + $view = Views::getView('test_options_list_argument_string'); + $this->executeView($view, ['man', 'woman']); + + $resultset = [ + ['nid' => $this->nodes[0]->nid->value], + ['nid' => $this->nodes[1]->nid->value], + ]; + + $column_map = ['nid' => 'nid']; + $this->assertIdenticalResultset($view, $resultset, $column_map); + } + +} diff --git a/core/modules/options/tests/src/Kernel/Views/OptionsListFilterTest.php b/core/modules/options/tests/src/Kernel/Views/OptionsListFilterTest.php new file mode 100644 index 0000000..afd4b2d --- /dev/null +++ b/core/modules/options/tests/src/Kernel/Views/OptionsListFilterTest.php @@ -0,0 +1,43 @@ +executeView($view); + + $resultset = [ + ['nid' => $this->nodes[0]->nid->value], + ['nid' => $this->nodes[1]->nid->value], + ]; + + $column_map = ['nid' => 'nid']; + $this->assertIdenticalResultset($view, $resultset, $column_map); + } + +} diff --git a/core/modules/options/tests/src/Kernel/Views/OptionsTestBase.php b/core/modules/options/tests/src/Kernel/Views/OptionsTestBase.php new file mode 100644 index 0000000..419dd6e --- /dev/null +++ b/core/modules/options/tests/src/Kernel/Views/OptionsTestBase.php @@ -0,0 +1,125 @@ +mockStandardInstall(); + + ViewTestData::createTestViews(get_class($this), ['options_test_views']); + + $settings = []; + $settings['type'] = 'article'; + $settings['title'] = $this->randomString(); + $settings['field_test_list_string'][]['value'] = $this->fieldValues[0]; + $settings['field_test_list_integer'][]['value'] = 0; + + $node = Node::create($settings); + $node->save(); + + $this->nodes[] = $node; + $node = $node->createDuplicate(); + $node->save(); + $this->nodes[] = $node; + } + + /** + * Provides a workaround for the inability to use the standard profile. + * + * @see https://www.drupal.org/node/1708692 + */ + protected function mockStandardInstall() { + $this->installEntitySchema('user'); + $this->installEntitySchema('node'); + + NodeType::create( + ['type' => 'article'] + )->save(); + $this->fieldValues = [ + $this->randomMachineName(), + $this->randomMachineName(), + ]; + + $this->fieldNames = ['field_test_list_string', 'field_test_list_integer']; + + // Create two field entities. + FieldStorageConfig::create([ + 'field_name' => $this->fieldNames[0], + 'entity_type' => 'node', + 'type' => 'list_string', + 'cardinality' => 1, + 'settings' => [ + 'allowed_values' => [ + $this->fieldValues[0] => $this->fieldValues[0], + $this->fieldValues[1] => $this->fieldValues[1], + ], + ], + ])->save(); + FieldStorageConfig::create([ + 'field_name' => $this->fieldNames[1], + 'entity_type' => 'node', + 'type' => 'list_integer', + 'cardinality' => 1, + 'settings' => [ + 'allowed_values' => [ + $this->fieldValues[0], + $this->fieldValues[1], + ], + ], + ])->save(); + foreach ($this->fieldNames as $field_name) { + FieldConfig::create([ + 'field_name' => $field_name, + 'entity_type' => 'node', + 'label' => 'Test options list field', + 'bundle' => 'article', + ])->save(); + } + } + +} diff --git a/core/modules/options/tests/src/Kernel/Views/ViewsDataTest.php b/core/modules/options/tests/src/Kernel/Views/ViewsDataTest.php new file mode 100644 index 0000000..dcfaba7 --- /dev/null +++ b/core/modules/options/tests/src/Kernel/Views/ViewsDataTest.php @@ -0,0 +1,69 @@ +fieldStorage = FieldStorageConfig::create([ + 'field_name' => $field_name, + 'entity_type' => 'entity_test', + 'type' => 'list_string', + 'cardinality' => 1, + 'settings' => [ + 'allowed_values_function' => 'options_test_dynamic_values_callback', + ], + ]); + $this->fieldStorage->save(); + + $this->field = FieldConfig::create([ + 'field_name' => $field_name, + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + 'required' => TRUE, + ])->save(); + } + + /** + * Tests the option module's implementation of hook_field_views_data(). + */ + public function testOptionsFieldViewsData() { + $field_data = \Drupal::service('views.views_data')->get('entity_test__test_options'); + + // Check that the options module has properly overridden default views data. + $test_options_field = $field_data['test_options_value']; + $this->assertEqual($test_options_field['argument']['id'], 'string_list_field', 'Argument handler is properly set for fields with allowed value callbacks.'); + $this->assertEqual($test_options_field['filter']['id'], 'list_field', 'Filter handler is properly set for fields with allowed value callbacks.'); + } + +} diff --git a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php index ab1c8f2..039772d 100644 --- a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php +++ b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php @@ -7,6 +7,7 @@ namespace Drupal\page_cache\Tests; +use Drupal\Core\EventSubscriber\MainContentViewSubscriber; use Drupal\Core\Language\LanguageInterface; use Drupal\simpletest\WebTestBase; use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait; @@ -77,7 +78,8 @@ function testPageCacheTags() { 'user', // The placed block is only visible on certain URLs through a visibility // condition. - 'url', + 'url.path', + 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, ]; // Full node page 1. diff --git a/core/modules/page_cache/src/Tests/PageCacheTest.php b/core/modules/page_cache/src/Tests/PageCacheTest.php index 0183b31..42fbec5 100644 --- a/core/modules/page_cache/src/Tests/PageCacheTest.php +++ b/core/modules/page_cache/src/Tests/PageCacheTest.php @@ -263,7 +263,7 @@ function testPageCache() { $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar'))); $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Caching was bypassed.'); $this->assertTrue(strpos(strtolower($this->drupalGetHeader('Vary')), 'cookie') === FALSE, 'Vary: Cookie header was not sent.'); - $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'must-revalidate, no-cache, post-check=0, pre-check=0, private', 'Cache-Control header was sent.'); + $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'must-revalidate, no-cache, private', 'Cache-Control header was sent.'); $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.'); $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.'); @@ -452,12 +452,12 @@ public function testCacheableResponseResponses() { // GET a URL, which would be marked as a cache miss if it were cacheable. $this->drupalGet('/system-test/respond-reponse'); $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Drupal page cache header not found.'); - $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'must-revalidate, no-cache, post-check=0, pre-check=0, private', 'Cache-Control header was sent'); + $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'must-revalidate, no-cache, private', 'Cache-Control header was sent'); // GET it again, verify it's still not cached. $this->drupalGet('/system-test/respond-reponse'); $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Drupal page cache header not found.'); - $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'must-revalidate, no-cache, post-check=0, pre-check=0, private', 'Cache-Control header was sent'); + $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'must-revalidate, no-cache, private', 'Cache-Control header was sent'); // GET a URL, which would be marked as a cache miss if it were cacheable. $this->drupalGet('/system-test/respond-public-response'); diff --git a/core/modules/path/path.js b/core/modules/path/path.js index 4cca889..dfa2c05 100644 --- a/core/modules/path/path.js +++ b/core/modules/path/path.js @@ -2,7 +2,7 @@ * @file * Attaches behaviors for the Path module. */ -(function ($) { +(function ($, Drupal) { 'use strict'; @@ -26,4 +26,4 @@ } }; -})(jQuery); +})(jQuery, Drupal); diff --git a/core/modules/path/path.module b/core/modules/path/path.module index 24309b7..06efa7b 100644 --- a/core/modules/path/path.module +++ b/core/modules/path/path.module @@ -7,7 +7,6 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeInterface; -use Drupal\Core\Field\FieldDefinition; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Routing\RouteMatchInterface; diff --git a/core/modules/path/src/Form/EditForm.php b/core/modules/path/src/Form/EditForm.php index a1f4a77..2f9739e 100644 --- a/core/modules/path/src/Form/EditForm.php +++ b/core/modules/path/src/Form/EditForm.php @@ -40,28 +40,25 @@ public function buildForm(array $form, FormStateInterface $form_state, $pid = NU '#type' => 'hidden', '#value' => $this->path['pid'], ); - $form['actions']['delete'] = array( - '#type' => 'submit', - '#value' => $this->t('Delete'), - '#submit' => array('::deleteSubmit'), - ); - return $form; - } - /** - * Submits the delete form. - */ - public function deleteSubmit(array &$form, FormStateInterface $form_state) { $url = new Url('path.delete', array( - 'pid' => $form_state->getValue('pid'), + 'pid' => $this->path['pid'], )); if ($this->getRequest()->query->has('destination')) { $url->setOption('query', $this->getDestinationArray()); - $this->getRequest()->query->remove('destination'); } - $form_state->setRedirectUrl($url); + $form['actions']['delete'] = array( + '#type' => 'link', + '#title' => $this->t('Delete'), + '#url' => $url, + '#attributes' => array( + 'class' => array('button', 'button--danger'), + ), + ); + + return $form; } } diff --git a/core/modules/path/src/Form/PathFormBase.php b/core/modules/path/src/Form/PathFormBase.php index 95d261a..80b99cb 100644 --- a/core/modules/path/src/Form/PathFormBase.php +++ b/core/modules/path/src/Form/PathFormBase.php @@ -116,7 +116,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $pid = NU '#default_value' => $this->path['alias'], '#maxlength' => 255, '#size' => 45, - '#description' => $this->t('Specify an alternative path by which this data can be accessed. For example, type "/about" when writing an about page. Use a relative path with a slash in front..'), + '#description' => $this->t('Specify an alternative path by which this data can be accessed. For example, type "/about" when writing an about page. Use a relative path with a slash in front.'), '#field_prefix' => $this->requestContext->getCompleteBaseUrl(), '#required' => TRUE, ); @@ -151,6 +151,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $pid = NU $form['actions']['submit'] = array( '#type' => 'submit', '#value' => $this->t('Save'), + '#button_type' => 'primary', ); return $form; diff --git a/core/modules/path/src/Plugin/migrate/destination/UrlAlias.php b/core/modules/path/src/Plugin/migrate/destination/UrlAlias.php index 20b2c31..c617c55 100644 --- a/core/modules/path/src/Plugin/migrate/destination/UrlAlias.php +++ b/core/modules/path/src/Plugin/migrate/destination/UrlAlias.php @@ -8,7 +8,7 @@ namespace Drupal\path\Plugin\migrate\destination; use Drupal\Core\Path\AliasStorage; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; use Drupal\migrate\Plugin\migrate\destination\DestinationBase; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -37,7 +37,7 @@ class UrlAlias extends DestinationBase implements ContainerFactoryPluginInterfac * The plugin_id for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. - * @param MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration. * @param \Drupal\Core\Path\AliasStorage $alias_storage * The alias storage service. @@ -90,8 +90,8 @@ public function fields(MigrationInterface $migration = NULL) { return [ 'pid' => 'The path id', 'source' => 'The source path.', - 'alias' => 'The url alias.', - 'langcode' => 'The language code for the url.', + 'alias' => 'The URL alias.', + 'langcode' => 'The language code for the URL.', ]; } diff --git a/core/modules/path/src/Plugin/migrate/source/UrlAliasBase.php b/core/modules/path/src/Plugin/migrate/source/UrlAliasBase.php index a45f404..c77b41c 100644 --- a/core/modules/path/src/Plugin/migrate/source/UrlAliasBase.php +++ b/core/modules/path/src/Plugin/migrate/source/UrlAliasBase.php @@ -27,7 +27,7 @@ public function query() { public function fields() { return array( 'pid' => $this->t('The numeric identifier of the path alias.'), - 'language' => $this->t('The language code of the url alias.'), + 'language' => $this->t('The language code of the URL alias.'), ); } diff --git a/core/modules/path/src/Tests/Migrate/d6/MigrateUrlAliasTest.php b/core/modules/path/src/Tests/Migrate/d6/MigrateUrlAliasTest.php deleted file mode 100644 index a37cb65..0000000 --- a/core/modules/path/src/Tests/Migrate/d6/MigrateUrlAliasTest.php +++ /dev/null @@ -1,104 +0,0 @@ -installSchema('system', ['url_alias']); - $this->executeMigration('d6_url_alias'); - } - - /** - * Assert a path. - * - * @param string pid - * The path id. - * @param array $conditions - * The path conditions. - * @param array $path - * The path. - */ - private function assertPath($pid, $conditions, $path) { - $this->assertTrue($path, "Path alias for " . $conditions['source'] . " successfully loaded."); - $this->assertIdentical($conditions['alias'], $path['alias']); - $this->assertIdentical($conditions['langcode'], $path['langcode']); - $this->assertIdentical($conditions['source'], $path['source']); - } - - /** - * Test the url alias migration. - */ - public function testUrlAlias() { - $id_map = Migration::load('d6_url_alias')->getIdMap(); - $conditions = array( - 'source' => '/node/1', - 'alias' => '/alias-one', - 'langcode' => 'af', - ); - $path = \Drupal::service('path.alias_storage')->load($conditions); - $this->assertPath('1', $conditions, $path); - $this->assertIdentical($id_map->lookupDestinationID(array($path['pid'])), array('1'), "Test IdMap"); - - $conditions = array( - 'source' => '/node/2', - 'alias' => '/alias-two', - 'langcode' => 'en', - ); - $path = \Drupal::service('path.alias_storage')->load($conditions); - $this->assertPath('2', $conditions, $path); - - // Test that we can re-import using the UrlAlias destination. - Database::getConnection('default', 'migrate') - ->update('url_alias') - ->fields(array('dst' => 'new-url-alias')) - ->condition('src', 'node/2') - ->execute(); - - \Drupal::database() - ->update($id_map->mapTableName()) - ->fields(array('source_row_status' => MigrateIdMapInterface::STATUS_NEEDS_UPDATE)) - ->execute(); - $migration = \Drupal::entityManager() - ->getStorage('migration') - ->loadUnchanged('d6_url_alias'); - $this->executeMigration($migration); - - $path = \Drupal::service('path.alias_storage')->load(array('pid' => $path['pid'])); - $conditions['alias'] = '/new-url-alias'; - $this->assertPath('2', $conditions, $path); - - $conditions = array( - 'source' => '/node/3', - 'alias' => '/alias-three', - 'langcode' => 'und', - ); - $path = \Drupal::service('path.alias_storage')->load($conditions); - $this->assertPath('3', $conditions, $path); - } - -} diff --git a/core/modules/path/src/Tests/Migrate/d7/MigrateUrlAliasTest.php b/core/modules/path/src/Tests/Migrate/d7/MigrateUrlAliasTest.php deleted file mode 100644 index 829a872..0000000 --- a/core/modules/path/src/Tests/Migrate/d7/MigrateUrlAliasTest.php +++ /dev/null @@ -1,47 +0,0 @@ -installSchema('system', ['url_alias']); - $this->executeMigration('d7_url_alias'); - } - - /** - * Test the URL alias migration. - */ - public function testUrlAlias() { - $path = \Drupal::service('path.alias_storage')->load([ - 'source' => '/taxonomy/term/4', - 'alias' => '/term33', - 'langcode' => 'und', - ]); - $this->assertIdentical('/taxonomy/term/4', $path['source']); - $this->assertIdentical('/term33', $path['alias']); - $this->assertIdentical('und', $path['langcode']); - } - -} diff --git a/core/modules/path/src/Tests/PathAliasTest.php b/core/modules/path/src/Tests/PathAliasTest.php index 7e85cda..7a58787 100644 --- a/core/modules/path/src/Tests/PathAliasTest.php +++ b/core/modules/path/src/Tests/PathAliasTest.php @@ -145,7 +145,9 @@ function testAdminAlias() { ]), 'Attempt to move upper-case alias was rejected.'); // Delete alias. - $this->drupalPostForm('admin/config/search/path/edit/' . $pid, array(), t('Delete')); + $this->drupalGet('admin/config/search/path/edit/' . $pid); + $this->clickLink(t('Delete')); + $this->assertRaw(t('Are you sure you want to delete path alias %name?', array('%name' => $edit['alias']))); $this->drupalPostForm(NULL, array(), t('Confirm')); // Confirm that the alias no longer works. diff --git a/core/modules/path/src/Tests/PathTaxonomyTermTest.php b/core/modules/path/src/Tests/PathTaxonomyTermTest.php index 98372df..c11594e 100644 --- a/core/modules/path/src/Tests/PathTaxonomyTermTest.php +++ b/core/modules/path/src/Tests/PathTaxonomyTermTest.php @@ -27,10 +27,10 @@ protected function setUp() { parent::setUp(); // Create a Tags vocabulary for the Article node type. - $vocabulary = entity_create('taxonomy_vocabulary', array( + $vocabulary = Vocabulary::create([ 'name' => t('Tags'), 'vid' => 'tags', - )); + ]); $vocabulary->save(); // Create and login user. diff --git a/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php b/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php new file mode 100644 index 0000000..02d4583 --- /dev/null +++ b/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php @@ -0,0 +1,101 @@ +executeMigration('d6_url_alias'); + } + + /** + * Assert a path. + * + * @param string pid + * The path id. + * @param array $conditions + * The path conditions. + * @param array $path + * The path. + */ + private function assertPath($pid, $conditions, $path) { + $this->assertTrue($path, "Path alias for " . $conditions['source'] . " successfully loaded."); + $this->assertIdentical($conditions['alias'], $path['alias']); + $this->assertIdentical($conditions['langcode'], $path['langcode']); + $this->assertIdentical($conditions['source'], $path['source']); + } + + /** + * Test the url alias migration. + */ + public function testUrlAlias() { + $id_map = $this->getMigration('d6_url_alias')->getIdMap(); + // Test that the field exists. + $conditions = array( + 'source' => '/node/1', + 'alias' => '/alias-one', + 'langcode' => 'af', + ); + $path = \Drupal::service('path.alias_storage')->load($conditions); + $this->assertPath('1', $conditions, $path); + $this->assertIdentical($id_map->lookupDestinationID(array($path['pid'])), array('1'), "Test IdMap"); + + $conditions = array( + 'source' => '/node/2', + 'alias' => '/alias-two', + 'langcode' => 'en', + ); + $path = \Drupal::service('path.alias_storage')->load($conditions); + $this->assertPath('2', $conditions, $path); + + // Test that we can re-import using the UrlAlias destination. + Database::getConnection('default', 'migrate') + ->update('url_alias') + ->fields(array('dst' => 'new-url-alias')) + ->condition('src', 'node/2') + ->execute(); + + \Drupal::database() + ->update($id_map->mapTableName()) + ->fields(array('source_row_status' => MigrateIdMapInterface::STATUS_NEEDS_UPDATE)) + ->execute(); + $migration = $this->getMigration('d6_url_alias'); + $this->executeMigration($migration); + + $path = \Drupal::service('path.alias_storage')->load(array('pid' => $path['pid'])); + $conditions['alias'] = '/new-url-alias'; + $this->assertPath('2', $conditions, $path); + + $conditions = array( + 'source' => '/node/3', + 'alias' => '/alias-three', + 'langcode' => 'und', + ); + $path = \Drupal::service('path.alias_storage')->load($conditions); + $this->assertPath('3', $conditions, $path); + } + +} diff --git a/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php b/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php new file mode 100644 index 0000000..ad5121c --- /dev/null +++ b/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php @@ -0,0 +1,46 @@ +executeMigration('d7_url_alias'); + } + + /** + * Test the URL alias migration. + */ + public function testUrlAlias() { + $path = \Drupal::service('path.alias_storage')->load([ + 'source' => '/taxonomy/term/4', + 'alias' => '/term33', + 'langcode' => 'und', + ]); + $this->assertIdentical('/taxonomy/term/4', $path['source']); + $this->assertIdentical('/term33', $path['alias']); + $this->assertIdentical('und', $path['langcode']); + } + +} diff --git a/core/modules/path/tests/src/Kernel/PathNoCanonicalLinkTest.php b/core/modules/path/tests/src/Kernel/PathNoCanonicalLinkTest.php index 6fa9c1b..3a545b4 100644 --- a/core/modules/path/tests/src/Kernel/PathNoCanonicalLinkTest.php +++ b/core/modules/path/tests/src/Kernel/PathNoCanonicalLinkTest.php @@ -30,7 +30,6 @@ protected function setUp() { $this->installEntitySchema('entity_test'); $this->installEntitySchema('entity_test_mul'); - $this->installSchema('system', 'router'); \Drupal::service('router.builder')->rebuild(); // Adding german language. diff --git a/core/modules/quickedit/js/editors/formEditor.js b/core/modules/quickedit/js/editors/formEditor.js index 49fc6f0..374e5c2 100644 --- a/core/modules/quickedit/js/editors/formEditor.js +++ b/core/modules/quickedit/js/editors/formEditor.js @@ -3,7 +3,7 @@ * Form-based in-place editor. Works for any field type. */ -(function ($, Drupal) { +(function ($, Drupal, _) { 'use strict'; @@ -252,4 +252,4 @@ } }); -})(jQuery, Drupal); +})(jQuery, Drupal, _); diff --git a/core/modules/quickedit/js/models/BaseModel.js b/core/modules/quickedit/js/models/BaseModel.js index f1c31d7..7579b6f 100644 --- a/core/modules/quickedit/js/models/BaseModel.js +++ b/core/modules/quickedit/js/models/BaseModel.js @@ -3,7 +3,7 @@ * A Backbone Model subclass that enforces validation when calling set(). */ -(function (Backbone) { +(function (Drupal, Backbone) { 'use strict'; @@ -57,4 +57,4 @@ }); -}(Backbone)); +}(Drupal, Backbone)); diff --git a/core/modules/quickedit/quickedit.module b/core/modules/quickedit/quickedit.module index 16d236a..2d69a66 100644 --- a/core/modules/quickedit/quickedit.module +++ b/core/modules/quickedit/quickedit.module @@ -124,6 +124,11 @@ function quickedit_preprocess_page_title(&$variables) { * Implements hook_preprocess_HOOK() for field templates. */ function quickedit_preprocess_field(&$variables) { + $variables['#cache']['contexts'][] = 'user.permissions'; + if (!\Drupal::currentUser()->hasPermission('access in-place editing')) { + return; + } + $element = $variables['element']; /** @var $entity \Drupal\Core\Entity\EntityInterface */ $entity = $element['#object']; @@ -148,5 +153,10 @@ function quickedit_preprocess_field(&$variables) { * Implements hook_entity_view_alter(). */ function quickedit_entity_view_alter(&$build, EntityInterface $entity, EntityViewDisplayInterface $display) { + $build['#cache']['contexts'][] = 'user.permissions'; + if (!\Drupal::currentUser()->hasPermission('access in-place editing')) { + return; + } + $build['#attributes']['data-quickedit-entity-id'] = $entity->getEntityTypeId() . '/' . $entity->id(); } diff --git a/core/modules/quickedit/src/Tests/EditorSelectionTest.php b/core/modules/quickedit/src/Tests/EditorSelectionTest.php index 25e0095..dfc73f0 100644 --- a/core/modules/quickedit/src/Tests/EditorSelectionTest.php +++ b/core/modules/quickedit/src/Tests/EditorSelectionTest.php @@ -7,6 +7,7 @@ namespace Drupal\quickedit\Tests; +use Drupal\entity_test\Entity\EntityTest; use Drupal\quickedit\EditorSelector; /** @@ -65,7 +66,7 @@ public function testText() { ); // Create an entity with values for this text field. - $entity = entity_create('entity_test'); + $entity = EntityTest::create(); $entity->{$field_name}->value = 'Hello, world!'; $entity->save(); @@ -104,7 +105,7 @@ public function testTextWysiwyg() { ); // Create an entity with values for this text field. - $entity = entity_create('entity_test'); + $entity = EntityTest::create(); $entity->{$field_name}->value = 'Hello, world!'; $entity->{$field_name}->format = 'filtered_html'; $entity->save(); @@ -141,7 +142,7 @@ public function testNumber() { ); // Create an entity with values for this text field. - $entity = entity_create('entity_test'); + $entity = EntityTest::create(); $entity->{$field_name}->value = 42; $entity->save(); diff --git a/core/modules/quickedit/src/Tests/MetadataGeneratorTest.php b/core/modules/quickedit/src/Tests/MetadataGeneratorTest.php index 0335703..f4633e1 100644 --- a/core/modules/quickedit/src/Tests/MetadataGeneratorTest.php +++ b/core/modules/quickedit/src/Tests/MetadataGeneratorTest.php @@ -7,9 +7,11 @@ namespace Drupal\quickedit\Tests; +use Drupal\entity_test\Entity\EntityTest; use Drupal\quickedit\EditorSelector; use Drupal\quickedit\MetadataGenerator; use Drupal\quickedit_test\MockEditEntityFieldAccessCheck; +use Drupal\filter\Entity\FilterFormat; /** * Tests in-place field editing metadata. @@ -92,7 +94,7 @@ public function testSimpleEntityType() { ); // Create an entity with values for this text field. - $entity = entity_create('entity_test'); + $entity = EntityTest::create(); $entity->{$field_1_name}->value = 'Test'; $entity->{$field_2_name}->value = 42; $entity->save(); @@ -123,8 +125,6 @@ public function testSimpleEntityType() { * Tests a field whose associated in-place editor generates custom metadata. */ public function testEditorWithCustomMetadata() { - $this->installSchema('system', 'url_alias'); - $this->editorManager = $this->container->get('plugin.manager.quickedit.editor'); $this->editorSelector = new EditorSelector($this->editorManager, $this->container->get('plugin.manager.field.formatter')); $this->metadataGenerator = new MetadataGenerator($this->accessChecker, $this->editorSelector, $this->editorManager); @@ -149,7 +149,7 @@ public function testEditorWithCustomMetadata() { ); // Create a text format. - $full_html_format = entity_create('filter_format', array( + $full_html_format = FilterFormat::create(array( 'format' => 'full_html', 'name' => 'Full HTML', 'weight' => 1, @@ -160,7 +160,7 @@ public function testEditorWithCustomMetadata() { $full_html_format->save(); // Create an entity with values for this rich text field. - $entity = entity_create('entity_test'); + $entity = EntityTest::create(); $entity->{$field_name}->value = 'Test'; $entity->{$field_name}->format = 'full_html'; $entity->save(); diff --git a/core/modules/quickedit/src/Tests/QuickEditAutocompleteTermTest.php b/core/modules/quickedit/src/Tests/QuickEditAutocompleteTermTest.php index 677c927..a728694 100644 --- a/core/modules/quickedit/src/Tests/QuickEditAutocompleteTermTest.php +++ b/core/modules/quickedit/src/Tests/QuickEditAutocompleteTermTest.php @@ -12,6 +12,8 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait; use Drupal\simpletest\WebTestBase; +use Drupal\taxonomy\Entity\Vocabulary; +use Drupal\taxonomy\Entity\Term; /** * Tests in-place editing of autocomplete tags. @@ -78,7 +80,7 @@ protected function setUp() { 'type' => 'article', )); // Create the vocabulary for the tag field. - $this->vocabulary = entity_create('taxonomy_vocabulary', [ + $this->vocabulary = Vocabulary::create([ 'name' => 'quickedit testing tags', 'vid' => 'quickedit_testing_tags', ]); @@ -202,14 +204,14 @@ public function testAutocompleteQuickEdit() { protected function createTerm() { $filter_formats = filter_formats(); $format = array_pop($filter_formats); - $term = entity_create('taxonomy_term', array( + $term = Term::create([ 'name' => $this->randomMachineName(), 'description' => $this->randomMachineName(), // Use the first available text format. 'format' => $format->id(), 'vid' => $this->vocabulary->id(), 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - )); + ]); $term->save(); return $term; } diff --git a/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php b/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php index 6e6fffb..4c05469 100644 --- a/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php +++ b/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php @@ -17,6 +17,7 @@ use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; use Drupal\simpletest\WebTestBase; +use Drupal\filter\Entity\FilterFormat; /** * Tests loading of in-place editing functionality and lazy loading of its @@ -57,7 +58,7 @@ protected function setUp() { parent::setUp(); // Create a text format. - $filtered_html_format = entity_create('filter_format', array( + $filtered_html_format = FilterFormat::create(array( 'format' => 'filtered_html', 'name' => 'Filtered HTML', 'weight' => 0, @@ -101,9 +102,10 @@ public function testUserWithoutPermission() { $this->assertNoRaw('core/modules/quickedit/js/quickedit.js', 'Quick Edit library not loaded.'); $this->assertNoRaw('core/modules/quickedit/js/editors/formEditor.js', "'form' in-place editor not loaded."); - // HTML annotation must always exist (to not break the render cache). - $this->assertRaw('data-quickedit-entity-id="node/1"'); - $this->assertRaw('data-quickedit-field-id="node/1/body/en/full"'); + // HTML annotation does not exist for users without permission to in-place + // edit. + $this->assertNoRaw('data-quickedit-entity-id="node/1"'); + $this->assertNoRaw('data-quickedit-field-id="node/1/body/en/full"'); // Retrieving the metadata should result in an empty 403 response. $post = array('fields[0]' => 'node/1/body/en/full'); @@ -518,6 +520,7 @@ public function testContentBlock() { $this->drupalPlaceBlock('block_content:' . $block->uuid()); // Check that the data- attribute is present. + $this->drupalLogin($this->editorUser); $this->drupalGet(''); $this->assertRaw('data-quickedit-entity-id="block_content/1"'); } diff --git a/core/modules/quickedit/src/Tests/QuickEditTestBase.php b/core/modules/quickedit/src/Tests/QuickEditTestBase.php index eb779ad..85884ff 100644 --- a/core/modules/quickedit/src/Tests/QuickEditTestBase.php +++ b/core/modules/quickedit/src/Tests/QuickEditTestBase.php @@ -7,7 +7,9 @@ namespace Drupal\quickedit\Tests; +use Drupal\field\Entity\FieldConfig; use Drupal\simpletest\KernelTestBase; +use Drupal\field\Entity\FieldStorageConfig; /** * Base class for testing Quick Edit functionality. @@ -70,7 +72,7 @@ protected function setUp() { */ protected function createFieldWithStorage($field_name, $type, $cardinality, $label, $field_settings, $widget_type, $widget_settings, $formatter_type, $formatter_settings) { $field_storage = $field_name . '_field_storage'; - $this->fields->$field_storage = entity_create('field_storage_config', array( + $this->fields->$field_storage = FieldStorageConfig::create(array( 'field_name' => $field_name, 'entity_type' => 'entity_test', 'type' => $type, @@ -79,14 +81,14 @@ protected function createFieldWithStorage($field_name, $type, $cardinality, $lab $this->fields->$field_storage->save(); $field = $field_name . '_field'; - $this->fields->$field = entity_create('field_config', array( + $this->fields->$field = FieldConfig::create([ 'field_storage' => $this->fields->$field_storage, 'bundle' => 'entity_test', 'label' => $label, 'description' => $label, 'weight' => mt_rand(0, 127), 'settings' => $field_settings, - )); + ]); $this->fields->$field->save(); entity_get_form_display('entity_test', 'entity_test', 'default') diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module index 33a3e92..0c7cf5c 100644 --- a/core/modules/rdf/rdf.module +++ b/core/modules/rdf/rdf.module @@ -7,6 +7,7 @@ use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Template\Attribute; +use Drupal\rdf\Entity\RdfMapping; /** * Implements hook_help(). @@ -73,7 +74,7 @@ function rdf_get_mapping($entity_type, $bundle) { // If not found, create a fresh mapping object. if (!$mapping) { - $mapping = entity_create('rdf_mapping', array( + $mapping = RdfMapping::create(array( 'targetEntityType' => $entity_type, 'bundle' => $bundle, )); diff --git a/core/modules/rdf/src/Tests/CommentAttributesTest.php b/core/modules/rdf/src/Tests/CommentAttributesTest.php index 8d3beb7..552fbaf 100644 --- a/core/modules/rdf/src/Tests/CommentAttributesTest.php +++ b/core/modules/rdf/src/Tests/CommentAttributesTest.php @@ -11,6 +11,7 @@ use Drupal\comment\CommentManagerInterface; use Drupal\comment\Tests\CommentTestBase; use Drupal\user\RoleInterface; +use Drupal\comment\Entity\Comment; /** * Tests the RDFa markup of comments. @@ -372,7 +373,7 @@ function saveComment($nid, $uid, $contact = NULL, $pid = 0) { $values += $contact; } - $comment = entity_create('comment', $values); + $comment = Comment::create($values); $comment->save(); return $comment; } diff --git a/core/modules/rdf/src/Tests/Field/DateTimeFieldRdfaTest.php b/core/modules/rdf/src/Tests/Field/DateTimeFieldRdfaTest.php deleted file mode 100644 index 1b47b40..0000000 --- a/core/modules/rdf/src/Tests/Field/DateTimeFieldRdfaTest.php +++ /dev/null @@ -1,55 +0,0 @@ -createTestField(); - - // Add the mapping. - $mapping = rdf_get_mapping('entity_test', 'entity_test'); - $mapping->setFieldMapping($this->fieldName, array( - 'properties' => array('schema:dateCreated'), - ))->save(); - - // Set up test entity. - $this->entity = entity_create('entity_test', array()); - $this->entity->{$this->fieldName}->value = $this->testValue; - } - - /** - * Tests the default formatter. - */ - public function testDefaultFormatter() { - $this->assertFormatterRdfa(array('type'=>'datetime_default'), 'http://schema.org/dateCreated', array('value' => $this->testValue . 'Z', 'type' => 'literal', 'datatype' => 'http://www.w3.org/2001/XMLSchema#dateTime')); - } -} diff --git a/core/modules/rdf/src/Tests/Field/EmailFieldRdfaTest.php b/core/modules/rdf/src/Tests/Field/EmailFieldRdfaTest.php deleted file mode 100644 index cda7ee7..0000000 --- a/core/modules/rdf/src/Tests/Field/EmailFieldRdfaTest.php +++ /dev/null @@ -1,52 +0,0 @@ -createTestField(); - - // Add the mapping. - $mapping = rdf_get_mapping('entity_test', 'entity_test'); - $mapping->setFieldMapping($this->fieldName, array( - 'properties' => array('schema:email'), - ))->save(); - - // Set up test values. - $this->testValue = 'test@example.com'; - $this->entity = entity_create('entity_test', array()); - $this->entity->{$this->fieldName}->value = $this->testValue; - } - - /** - * Tests all email formatters. - */ - public function testAllFormatters() { - // Test the plain formatter. - $this->assertFormatterRdfa(array('type'=>'string'), 'http://schema.org/email', array('value' => $this->testValue)); - // Test the mailto formatter. - $this->assertFormatterRdfa(array('type'=>'email_mailto'), 'http://schema.org/email', array('value' => $this->testValue)); - } -} diff --git a/core/modules/rdf/src/Tests/Field/EntityReferenceRdfaTest.php b/core/modules/rdf/src/Tests/Field/EntityReferenceRdfaTest.php deleted file mode 100644 index 73e6e53..0000000 --- a/core/modules/rdf/src/Tests/Field/EntityReferenceRdfaTest.php +++ /dev/null @@ -1,95 +0,0 @@ -installEntitySchema('entity_test_rev'); - - // Give anonymous users permission to view test entities. - $this->installConfig(array('user')); - Role::load(RoleInterface::ANONYMOUS_ID) - ->grantPermission('view test entity') - ->save(); - - $this->createEntityReferenceField($this->entityType, $this->bundle, $this->fieldName, 'Field test', $this->entityType); - - // Add the mapping. - $mapping = rdf_get_mapping('entity_test', 'entity_test'); - $mapping->setFieldMapping($this->fieldName, array( - 'properties' => array('schema:knows'), - ))->save(); - - // Create the entity to be referenced. - $this->targetEntity = entity_create($this->entityType, array('name' => $this->randomMachineName())); - $this->targetEntity->save(); - - // Create the entity that will have the entity reference field. - $this->entity = entity_create($this->entityType, array('name' => $this->randomMachineName())); - $this->entity->save(); - $this->entity->{$this->fieldName}->entity = $this->targetEntity; - $this->uri = $this->getAbsoluteUri($this->entity); - } - - /** - * Tests all the entity reference formatters. - */ - public function testAllFormatters() { - $entity_uri = $this->getAbsoluteUri($this->targetEntity); - - // Tests the label formatter. - $this->assertFormatterRdfa(array('type' => 'entity_reference_label'), 'http://schema.org/knows', array('value' => $entity_uri, 'type' => 'uri')); - // Tests the entity formatter. - $this->assertFormatterRdfa(array('type' => 'entity_reference_entity_view'), 'http://schema.org/knows', array('value' => $entity_uri, 'type' => 'uri')); - } - -} diff --git a/core/modules/rdf/src/Tests/Field/FieldRdfaDatatypeCallbackTest.php b/core/modules/rdf/src/Tests/Field/FieldRdfaDatatypeCallbackTest.php deleted file mode 100644 index 0b43c30..0000000 --- a/core/modules/rdf/src/Tests/Field/FieldRdfaDatatypeCallbackTest.php +++ /dev/null @@ -1,60 +0,0 @@ -createTestField(); - - $this->installConfig(array('filter')); - - // Add the mapping. - $mapping = rdf_get_mapping('entity_test', 'entity_test'); - $mapping->setFieldMapping($this->fieldName, array( - 'properties' => array('schema:interactionCount'), - 'datatype_callback' => array( - 'callable' => 'Drupal\rdf\Tests\Field\TestDataConverter::convertFoo', - ), - ))->save(); - - // Set up test values. - $this->testValue = $this->randomMachineName(); - $this->entity = entity_create('entity_test'); - $this->entity->{$this->fieldName}->value = $this->testValue; - $this->entity->save(); - - $this->uri = $this->getAbsoluteUri($this->entity); - } - - /** - * Tests the default formatter. - */ - public function testDefaultFormatter() { - // Expected value is the output of the datatype callback, not the raw value. - $this->assertFormatterRdfa(array('type'=>'text_default'), 'http://schema.org/interactionCount', array('value' => 'foo' . $this->testValue)); - } - -} - diff --git a/core/modules/rdf/src/Tests/Field/FieldRdfaTestBase.php b/core/modules/rdf/src/Tests/Field/FieldRdfaTestBase.php deleted file mode 100644 index cb8f584..0000000 --- a/core/modules/rdf/src/Tests/Field/FieldRdfaTestBase.php +++ /dev/null @@ -1,191 +0,0 @@ -rebuild(); - } - - /** - * Helper function to test the formatter's RDFa. - * - * @param array $formatter - * An associative array describing the formatter to test and its settings - * containing: - * - type: The machine name of the field formatter to test. - * - settings: The settings of the field formatter to test. - * @param string $property - * The property that should be found. - * @param array $expected_rdf_value - * An associative array describing the expected value of the property - * containing: - * - value: The actual value of the string or URI. - * - type: The type of RDF value, e.g. 'literal' for a string, or 'uri'. - * Defaults to 'literal'. - * - datatype: (optional) The datatype of the value (e.g. xsd:dateTime). - */ - protected function assertFormatterRdfa($formatter, $property, $expected_rdf_value) { - $expected_rdf_value += array('type' => 'literal'); - - // The field formatter will be rendered inside the entity. Set the field - // formatter in the entity display options before rendering the entity. - entity_get_display('entity_test', 'entity_test', 'default') - ->setComponent($this->fieldName, $formatter) - ->save(); - $build = entity_view($this->entity, 'default'); - $output = \Drupal::service('renderer')->renderRoot($build); - $graph = new \EasyRdf_Graph($this->uri, $output, 'rdfa'); - $this->setRawContent($output); - - // If verbose debugging is turned on, display the HTML and parsed RDF - // in the results. - if ($this->debug) { - debug($output); - debug($graph->toRdfPhp()); - } - - $this->assertTrue($graph->hasProperty($this->uri, $property, $expected_rdf_value), "Formatter {$formatter['type']} exposes data correctly for {$this->fieldType} fields."); - } - - /** - * Creates the field for testing. - * - * @param array $field_settings - * (optional) An array of field settings. - */ - protected function createTestField($field_settings = array()) { - entity_create('field_storage_config', array( - 'field_name' => $this->fieldName, - 'entity_type' => 'entity_test', - 'type' => $this->fieldType, - ))->save(); - entity_create('field_config', array( - 'entity_type' => 'entity_test', - 'field_name' => $this->fieldName, - 'bundle' => 'entity_test', - 'settings' => $field_settings, - ))->save(); - } - - /** - * Gets the absolute URI of an entity. - * - * @param \Drupal\Core\Entity\ContentEntityBase $entity - * The entity for which to generate the URI. - * - * @return string - * The absolute URI. - */ - protected function getAbsoluteUri($entity) { - return $entity->url('canonical', array('absolute' => TRUE)); - } - - /** - * Parses a content and return the html element. - * - * @param string $content - * The html to parse. - * - * @return array - * An array containing simplexml objects. - */ - protected function parseContent($content) { - $htmlDom = new \DOMDocument(); - @$htmlDom->loadHTML('' . $content); - $elements = simplexml_import_dom($htmlDom); - - return $elements; - } - - /** - * Performs an xpath search on a certain content. - * - * The search is relative to the root element of the $content variable. - * - * @param string $content - * The html to parse. - * @param string $xpath - * The xpath string to use in the search. - * @param array $arguments - * Some arguments for the xpath. - * - * @return array|FALSE - * The return value of the xpath search. For details on the xpath string - * format and return values see the SimpleXML documentation, - * http://php.net/manual/function.simplexml-element-xpath.php. - */ - protected function xpathContent($content, $xpath, array $arguments = array()) { - if ($elements = $this->parseContent($content)) { - $xpath = $this->buildXPathQuery($xpath, $arguments); - $result = $elements->xpath($xpath); - // Some combinations of PHP / libxml versions return an empty array - // instead of the documented FALSE. Forcefully convert any falsish values - // to an empty array to allow foreach(...) constructions. - return $result ? $result : array(); - } - else { - return FALSE; - } - } - -} diff --git a/core/modules/rdf/src/Tests/Field/LinkFieldRdfaTest.php b/core/modules/rdf/src/Tests/Field/LinkFieldRdfaTest.php deleted file mode 100644 index 3a58b80..0000000 --- a/core/modules/rdf/src/Tests/Field/LinkFieldRdfaTest.php +++ /dev/null @@ -1,186 +0,0 @@ -createTestField(); - - // Add the mapping. - $mapping = rdf_get_mapping('entity_test', 'entity_test'); - $mapping->setFieldMapping($this->fieldName, array( - 'properties' => array('schema:link'), - ))->save(); - - } - - /** - * Tests all formatters with link to external page. - */ - public function testAllFormattersExternal() { - // Set up test values. - $this->testValue = 'http://test.me/foo/bar/neque/porro/quisquam/est/qui-dolorem?foo/bar/neque/porro/quisquam/est/qui-dolorem'; - $this->entity = entity_create('entity_test', array()); - $this->entity->{$this->fieldName}->uri = $this->testValue; - - // Set up the expected result. - $expected_rdf = array( - 'value' => $this->testValue, - 'type' => 'uri', - ); - - $this->runTestAllFormatters($expected_rdf, 'external'); - } - - /** - * Tests all formatters with link to internal page. - */ - public function testAllFormattersInternal() { - // Set up test values. - $this->testValue = 'admin'; - $this->entity = entity_create('entity_test', array()); - $this->entity->{$this->fieldName}->uri = 'internal:/admin'; - - // Set up the expected result. - // AssertFormatterRdfa looks for a full path. - $expected_rdf = array( - 'value' => $this->uri . '/' . $this->testValue, - 'type' => 'uri', - ); - - $this->runTestAllFormatters($expected_rdf, 'internal'); - } - - /** - * Tests all formatters with link to frontpage. - */ - public function testAllFormattersFront() { - // Set up test values. - $this->testValue = '/'; - $this->entity = entity_create('entity_test', array()); - $this->entity->{$this->fieldName}->uri = 'internal:/'; - - // Set up the expected result. - $expected_rdf = array( - 'value' => $this->uri . '/', - 'type' => 'uri', - ); - - $this->runTestAllFormatters($expected_rdf, 'front'); - } - - /** - * Helper function to test all link formatters. - */ - public function runTestAllFormatters($expected_rdf, $type = NULL) { - - // Test the link formatter: trim at 80, no other settings. - $formatter = array( - 'type' => 'link', - 'settings' => array( - 'trim_length' => 80, - 'url_only' => FALSE, - 'url_plain' => FALSE, - 'rel' => '', - 'target' => '', - ), - ); - $this->assertFormatterRdfa($formatter, 'http://schema.org/link', $expected_rdf); - - // Test the link formatter: trim at 40, nofollow, new window. - $formatter = array( - 'type' => 'link', - 'settings' => array( - 'trim_length' => 40, - 'url_only' => FALSE, - 'url_plain' => FALSE, - 'rel' => 'nofollow', - 'target' => '_blank', - ), - ); - $this->assertFormatterRdfa($formatter, 'http://schema.org/link', $expected_rdf); - - // Test the link formatter: trim at 40, URL only (not plaintext) nofollow, - // new window. - $formatter = array( - 'type' => 'link', - 'settings' => array( - 'trim_length' => 40, - 'url_only' => TRUE, - 'url_plain' => FALSE, - 'rel' => 'nofollow', - 'target' => '_blank', - ), - ); - $this->assertFormatterRdfa($formatter, 'http://schema.org/link', $expected_rdf); - - // Test the link_separate formatter: trim at 40, nofollow, new window. - $formatter = array( - 'type' => 'link_separate', - 'settings' => array( - 'trim_length' => 40, - 'rel' => 'nofollow', - 'target' => '_blank', - ), - ); - $this->assertFormatterRdfa($formatter, 'http://schema.org/link', $expected_rdf); - - // Change the expected value here to literal. When formatted as plaintext - // then the RDF is expecting a 'literal' not a 'uri'. - $expected_rdf = array( - 'value' => $this->testValue, - 'type' => 'literal', - ); - // Test the link formatter: trim at 20, url only (as plaintext.) - $formatter = array( - 'type' => 'link', - 'settings' => array( - 'trim_length' => 20, - 'url_only' => TRUE, - 'url_plain' => TRUE, - 'rel' => '0', - 'target' => '0', - ), - ); - $this->assertFormatterRdfa($formatter, 'http://schema.org/link', $expected_rdf); - - // Test the link formatter: do not trim, url only (as plaintext.) - $formatter = array( - 'type' => 'link', - 'settings' => array( - 'trim_length' => 0, - 'url_only' => TRUE, - 'url_plain' => TRUE, - 'rel' => '0', - 'target' => '0', - ), - ); - $this->assertFormatterRdfa($formatter, 'http://schema.org/link', $expected_rdf); - } - -} diff --git a/core/modules/rdf/src/Tests/Field/NumberFieldRdfaTest.php b/core/modules/rdf/src/Tests/Field/NumberFieldRdfaTest.php deleted file mode 100644 index 120d6f0..0000000 --- a/core/modules/rdf/src/Tests/Field/NumberFieldRdfaTest.php +++ /dev/null @@ -1,205 +0,0 @@ -fieldType = 'integer'; - $testValue = 3; - $this->createTestField(); - $this->createTestEntity($testValue); - $this->assertFormatterRdfa(array('type' => 'number_integer'), 'http://schema.org/baseSalary', array('value' => $testValue)); - - // Test that the content attribute is not created. - $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__items") and @content]'); - $this->assertFalse($result); - } - - /** - * Tests the integer formatter with settings. - */ - public function testIntegerFormatterWithSettings() { - \Drupal::service('theme_handler')->install(['classy']); - $this->config('system.theme')->set('default', 'classy')->save(); - $this->fieldType = 'integer'; - $formatter = array( - 'type' => 'number_integer', - 'settings' => array( - 'thousand_separator' => '.', - 'prefix_suffix' => TRUE, - ), - ); - $testValue = 3333333.33; - $field_settings = array( - 'prefix' => '#', - 'suffix' => ' llamas.', - ); - $this->createTestField($field_settings); - $this->createTestEntity($testValue); - $this->assertFormatterRdfa($formatter, 'http://schema.org/baseSalary', array('value' => $testValue)); - - // Test that the content attribute is created. - $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__item") and @content=:testValue]', array(':testValue' => $testValue)); - $this->assertTrue($result); - } - - /** - * Tests the float formatter. - */ - public function testFloatFormatter() { - $this->fieldType = 'float'; - $testValue = 3.33; - $this->createTestField(); - $this->createTestEntity($testValue); - $this->assertFormatterRdfa(array('type' => 'number_unformatted'), 'http://schema.org/baseSalary', array('value' => $testValue)); - - // Test that the content attribute is not created. - $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__items") and @content]'); - $this->assertFalse($result); - } - - /** - * Tests the float formatter with settings. - */ - public function testFloatFormatterWithSettings() { - \Drupal::service('theme_handler')->install(['classy']); - $this->config('system.theme')->set('default', 'classy')->save(); - $this->fieldType = 'float'; - $formatter = array( - 'type' => 'number_decimal', - 'settings' => array( - 'thousand_separator' => '.', - 'decimal_separator' => ',', - 'prefix_suffix' => TRUE, - ), - ); - $testValue = 3333333.33; - $field_settings = array( - 'prefix' => '$', - 'suffix' => ' more.', - ); - $this->createTestField($field_settings); - $this->createTestEntity($testValue); - $this->assertFormatterRdfa($formatter, 'http://schema.org/baseSalary', array('value' => $testValue)); - - // Test that the content attribute is created. - $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__item") and @content=:testValue]', array(':testValue' => $testValue)); - $this->assertTrue($result); - } - - /** - * Tests the float formatter with a scale. Scale is not exercised. - */ - public function testFloatFormatterWithScale() { - $this->fieldType = 'float'; - $formatter = array( - 'type' => 'number_decimal', - 'settings' => array( - 'scale' => 5, - ), - ); - $testValue = 3.33; - $this->createTestField(); - $this->createTestEntity($testValue); - $this->assertFormatterRdfa($formatter, 'http://schema.org/baseSalary', array('value' => $testValue)); - - // Test that the content attribute is not created. - $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__items") and @content]'); - $this->assertFalse($result); - } - - /** - * Tests the float formatter with a scale. Scale is exercised. - */ - public function testFloatFormatterWithScaleExercised() { - \Drupal::service('theme_handler')->install(['classy']); - $this->config('system.theme')->set('default', 'classy')->save(); - $this->fieldType = 'float'; - $formatter = array( - 'type' => 'number_decimal', - 'settings' => array( - 'scale' => 5, - ), - ); - $testValue = 3.1234567; - $this->createTestField(); - $this->createTestEntity($testValue); - $this->assertFormatterRdfa($formatter, 'http://schema.org/baseSalary', array('value' => $testValue)); - - // Test that the content attribute is created. - $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__item") and @content=:testValue]', array(':testValue' => $testValue)); - $this->assertTrue($result); - } - - /** - * Tests the decimal formatter. - */ - public function testDecimalFormatter() { - $this->fieldType = 'decimal'; - $testValue = 3.33; - $this->createTestField(); - $this->createTestEntity($testValue); - $this->assertFormatterRdfa(array('type' => 'number_decimal'), 'http://schema.org/baseSalary', array('value' => $testValue)); - - // Test that the content attribute is not created. - $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__items") and @content]'); - $this->assertFalse($result); - } - - /** - * Tests the decimal formatter with settings. - */ - public function testDecimalFormatterWithSettings() { - \Drupal::service('theme_handler')->install(['classy']); - $this->config('system.theme')->set('default', 'classy')->save(); - $this->fieldType = 'decimal'; - $formatter = array( - 'type' => 'number_decimal', - 'settings' => array( - 'thousand_separator' => 't', - 'decimal_separator' => '#', - 'prefix_suffix' => TRUE, - ), - ); - $testValue = 3333333.33; - $field_settings = array( - 'prefix' => '$', - 'suffix' => ' more.', - ); - $this->createTestField($field_settings); - $this->createTestEntity($testValue); - $this->assertFormatterRdfa($formatter, 'http://schema.org/baseSalary', array('value' => $testValue)); - - // Test that the content attribute is created. - $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__item") and @content=:testValue]', array(':testValue' => $testValue)); - $this->assertTrue($result); - } - - /** - * Creates the RDF mapping for the field. - */ - protected function createTestEntity($testValue) { - // Add the mapping. - $mapping = rdf_get_mapping('entity_test', 'entity_test'); - $mapping->setFieldMapping($this->fieldName, array( - 'properties' => array('schema:baseSalary'), - ))->save(); - - // Set up test entity. - $this->entity = entity_create('entity_test', array()); - $this->entity->{$this->fieldName}->value = $testValue; - } -} diff --git a/core/modules/rdf/src/Tests/Field/StringFieldRdfaTest.php b/core/modules/rdf/src/Tests/Field/StringFieldRdfaTest.php deleted file mode 100644 index 9805682..0000000 --- a/core/modules/rdf/src/Tests/Field/StringFieldRdfaTest.php +++ /dev/null @@ -1,59 +0,0 @@ -createTestField(); - - // Add the mapping. - $mapping = rdf_get_mapping('entity_test', 'entity_test'); - $mapping->setFieldMapping($this->fieldName, array( - 'properties' => array('schema:text'), - ))->save(); - - // Set up test entity. - $this->entity = entity_create('entity_test'); - $this->entity->{$this->fieldName}->value = $this->testValue; - $this->entity->{$this->fieldName}->summary = $this->testSummary; - } - - /** - * Tests string formatters. - */ - public function testStringFormatters() { - // Tests the string formatter. - $this->assertFormatterRdfa(array('type'=>'string'), 'http://schema.org/text', array('value' => $this->testValue)); - } -} diff --git a/core/modules/rdf/src/Tests/Field/TelephoneFieldRdfaTest.php b/core/modules/rdf/src/Tests/Field/TelephoneFieldRdfaTest.php deleted file mode 100644 index 002db43..0000000 --- a/core/modules/rdf/src/Tests/Field/TelephoneFieldRdfaTest.php +++ /dev/null @@ -1,70 +0,0 @@ -createTestField(); - - // Add the mapping. - $mapping = rdf_get_mapping('entity_test', 'entity_test'); - $mapping->setFieldMapping($this->fieldName, array( - 'properties' => array('schema:telephone'), - ))->save(); - - // Set up test values. - $this->testValue = '555-555-5555'; - $this->entity = entity_create('entity_test', array()); - $this->entity->{$this->fieldName}->value = $this->testValue; - } - - /** - * Tests the field formatters. - */ - public function testAllFormatters() { - // Tests the plain formatter. - $this->assertFormatterRdfa(array('type' => 'string'), 'http://schema.org/telephone', array('value' => $this->testValue)); - // Tests the telephone link formatter. - $this->assertFormatterRdfa(array('type' => 'telephone_link'), 'http://schema.org/telephone', array('value' => 'tel:' . $this->testValue, 'type' => 'uri')); - - $formatter = array( - 'type' => 'telephone_link', - 'settings' => array('title' => 'Contact us'), - ); - $expected_rdf_value = array( - 'value' => 'tel:' . $this->testValue, - 'type' => 'uri', - ); - // Tests the telephone link formatter with custom title. - $this->assertFormatterRdfa($formatter, 'http://schema.org/telephone', $expected_rdf_value); - } -} diff --git a/core/modules/rdf/src/Tests/Field/TextFieldRdfaTest.php b/core/modules/rdf/src/Tests/Field/TextFieldRdfaTest.php deleted file mode 100644 index 0e97b27..0000000 --- a/core/modules/rdf/src/Tests/Field/TextFieldRdfaTest.php +++ /dev/null @@ -1,74 +0,0 @@ -installConfig(array('filter')); - - $this->createTestField(); - - // Add the mapping. - $mapping = rdf_get_mapping('entity_test', 'entity_test'); - $mapping->setFieldMapping($this->fieldName, array( - 'properties' => array('schema:text'), - ))->save(); - - // Set up test entity. - $this->entity = entity_create('entity_test'); - $this->entity->{$this->fieldName}->value = $this->testValue; - $this->entity->{$this->fieldName}->summary = $this->testSummary; - } - - /** - * Tests all formatters. - * - * @todo Check for the summary mapping. - */ - public function testAllFormatters() { - $formatted_value = strip_tags($this->entity->{$this->fieldName}->processed); - - // Tests the default formatter. - $this->assertFormatterRdfa(array('type'=>'text_default'), 'http://schema.org/text', array('value' => $formatted_value)); - // Tests the summary formatter. - $this->assertFormatterRdfa(array('type'=>'text_summary_or_trimmed'), 'http://schema.org/text', array('value' => $formatted_value)); - // Tests the trimmed formatter. - $this->assertFormatterRdfa(array('type'=>'text_trimmed'), 'http://schema.org/text', array('value' => $formatted_value)); - } -} diff --git a/core/modules/rdf/src/Tests/StandardProfileTest.php b/core/modules/rdf/src/Tests/StandardProfileTest.php index 57f4c3f..e5b42ac 100644 --- a/core/modules/rdf/src/Tests/StandardProfileTest.php +++ b/core/modules/rdf/src/Tests/StandardProfileTest.php @@ -8,10 +8,13 @@ namespace Drupal\rdf\Tests; use Drupal\Core\Url; +use Drupal\file\Entity\File; use Drupal\image\Entity\ImageStyle; use Drupal\node\Entity\NodeType; use Drupal\node\NodeInterface; use Drupal\simpletest\WebTestBase; +use Drupal\comment\Entity\Comment; +use Drupal\taxonomy\Entity\Term; /** * Tests the RDF mappings and RDFa markup of the standard profile. @@ -109,7 +112,7 @@ protected function setUp() { // Use Classy theme for testing markup output. \Drupal::service('theme_handler')->install(['classy']); - $this->config('system.theme')->set('default', 'classy')->save(); + \Drupal::service('theme_handler')->setDefault('classy'); $this->baseUri = \Drupal::url('', [], ['absolute' => TRUE]); @@ -130,16 +133,16 @@ protected function setUp() { $this->drupalLogin($this->adminUser); // Create term. - $this->term = entity_create('taxonomy_term', array( + $this->term = Term::create([ 'name' => $this->randomMachineName(), 'description' => $this->randomMachineName(), 'vid' => 'tags', - )); + ]); $this->term->save(); // Create image. file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', 'public://example.jpg'); - $this->image = entity_create('file', array('uri' => 'public://example.jpg')); + $this->image = File::create(['uri' => 'public://example.jpg']); $this->image->save(); // Create article. @@ -515,7 +518,7 @@ protected function saveComment($nid, $uid, $contact = NULL, $pid = 0) { $values += $contact; } - $comment = entity_create('comment', $values); + $comment = Comment::create($values); $comment->save(); return $comment; } diff --git a/core/modules/rdf/tests/src/Kernel/Field/DateTimeFieldRdfaTest.php b/core/modules/rdf/tests/src/Kernel/Field/DateTimeFieldRdfaTest.php new file mode 100644 index 0000000..e128c74 --- /dev/null +++ b/core/modules/rdf/tests/src/Kernel/Field/DateTimeFieldRdfaTest.php @@ -0,0 +1,57 @@ +createTestField(); + + // Add the mapping. + $mapping = rdf_get_mapping('entity_test', 'entity_test'); + $mapping->setFieldMapping($this->fieldName, array( + 'properties' => array('schema:dateCreated'), + ))->save(); + + // Set up test entity. + $this->entity = EntityTest::create(array()); + $this->entity->{$this->fieldName}->value = $this->testValue; + } + + /** + * Tests the default formatter. + */ + public function testDefaultFormatter() { + $this->assertFormatterRdfa(array('type'=>'datetime_default'), 'http://schema.org/dateCreated', array('value' => $this->testValue . 'Z', 'type' => 'literal', 'datatype' => 'http://www.w3.org/2001/XMLSchema#dateTime')); + } +} diff --git a/core/modules/rdf/tests/src/Kernel/Field/EmailFieldRdfaTest.php b/core/modules/rdf/tests/src/Kernel/Field/EmailFieldRdfaTest.php new file mode 100644 index 0000000..3b57a81 --- /dev/null +++ b/core/modules/rdf/tests/src/Kernel/Field/EmailFieldRdfaTest.php @@ -0,0 +1,54 @@ +createTestField(); + + // Add the mapping. + $mapping = rdf_get_mapping('entity_test', 'entity_test'); + $mapping->setFieldMapping($this->fieldName, array( + 'properties' => array('schema:email'), + ))->save(); + + // Set up test values. + $this->testValue = 'test@example.com'; + $this->entity = EntityTest::create(array()); + $this->entity->{$this->fieldName}->value = $this->testValue; + } + + /** + * Tests all email formatters. + */ + public function testAllFormatters() { + // Test the plain formatter. + $this->assertFormatterRdfa(array('type'=>'string'), 'http://schema.org/email', array('value' => $this->testValue)); + // Test the mailto formatter. + $this->assertFormatterRdfa(array('type'=>'email_mailto'), 'http://schema.org/email', array('value' => $this->testValue)); + } +} diff --git a/core/modules/rdf/tests/src/Kernel/Field/EntityReferenceRdfaTest.php b/core/modules/rdf/tests/src/Kernel/Field/EntityReferenceRdfaTest.php new file mode 100644 index 0000000..27d2028 --- /dev/null +++ b/core/modules/rdf/tests/src/Kernel/Field/EntityReferenceRdfaTest.php @@ -0,0 +1,99 @@ +installEntitySchema('entity_test_rev'); + + // Give anonymous users permission to view test entities. + $this->installConfig(array('user')); + Role::load(RoleInterface::ANONYMOUS_ID) + ->grantPermission('view test entity') + ->save(); + + $this->createEntityReferenceField($this->entityType, $this->bundle, $this->fieldName, 'Field test', $this->entityType); + + // Add the mapping. + $mapping = rdf_get_mapping('entity_test', 'entity_test'); + $mapping->setFieldMapping($this->fieldName, array( + 'properties' => array('schema:knows'), + ))->save(); + + // Create the entity to be referenced. + $this->targetEntity = $this->container->get('entity_type.manager') + ->getStorage($this->entityType) + ->create(array('name' => $this->randomMachineName())); + $this->targetEntity->save(); + + // Create the entity that will have the entity reference field. + $this->entity = $this->container->get('entity_type.manager') + ->getStorage($this->entityType) + ->create(array('name' => $this->randomMachineName())); + $this->entity->save(); + $this->entity->{$this->fieldName}->entity = $this->targetEntity; + $this->uri = $this->getAbsoluteUri($this->entity); + } + + /** + * Tests all the entity reference formatters. + */ + public function testAllFormatters() { + $entity_uri = $this->getAbsoluteUri($this->targetEntity); + + // Tests the label formatter. + $this->assertFormatterRdfa(array('type' => 'entity_reference_label'), 'http://schema.org/knows', array('value' => $entity_uri, 'type' => 'uri')); + // Tests the entity formatter. + $this->assertFormatterRdfa(array('type' => 'entity_reference_entity_view'), 'http://schema.org/knows', array('value' => $entity_uri, 'type' => 'uri')); + } + +} diff --git a/core/modules/rdf/tests/src/Kernel/Field/FieldRdfaDatatypeCallbackTest.php b/core/modules/rdf/tests/src/Kernel/Field/FieldRdfaDatatypeCallbackTest.php new file mode 100644 index 0000000..fe338be --- /dev/null +++ b/core/modules/rdf/tests/src/Kernel/Field/FieldRdfaDatatypeCallbackTest.php @@ -0,0 +1,62 @@ +createTestField(); + + $this->installConfig(array('filter')); + + // Add the mapping. + $mapping = rdf_get_mapping('entity_test', 'entity_test'); + $mapping->setFieldMapping($this->fieldName, array( + 'properties' => array('schema:interactionCount'), + 'datatype_callback' => array( + 'callable' => 'Drupal\rdf\Tests\Field\TestDataConverter::convertFoo', + ), + ))->save(); + + // Set up test values. + $this->testValue = $this->randomMachineName(); + $this->entity = EntityTest::create(); + $this->entity->{$this->fieldName}->value = $this->testValue; + $this->entity->save(); + + $this->uri = $this->getAbsoluteUri($this->entity); + } + + /** + * Tests the default formatter. + */ + public function testDefaultFormatter() { + // Expected value is the output of the datatype callback, not the raw value. + $this->assertFormatterRdfa(array('type'=>'text_default'), 'http://schema.org/interactionCount', array('value' => 'foo' . $this->testValue)); + } + +} + diff --git a/core/modules/rdf/tests/src/Kernel/Field/FieldRdfaTestBase.php b/core/modules/rdf/tests/src/Kernel/Field/FieldRdfaTestBase.php new file mode 100644 index 0000000..30f9cd1 --- /dev/null +++ b/core/modules/rdf/tests/src/Kernel/Field/FieldRdfaTestBase.php @@ -0,0 +1,193 @@ +rebuild(); + } + + /** + * Helper function to test the formatter's RDFa. + * + * @param array $formatter + * An associative array describing the formatter to test and its settings + * containing: + * - type: The machine name of the field formatter to test. + * - settings: The settings of the field formatter to test. + * @param string $property + * The property that should be found. + * @param array $expected_rdf_value + * An associative array describing the expected value of the property + * containing: + * - value: The actual value of the string or URI. + * - type: The type of RDF value, e.g. 'literal' for a string, or 'uri'. + * Defaults to 'literal'. + * - datatype: (optional) The datatype of the value (e.g. xsd:dateTime). + */ + protected function assertFormatterRdfa($formatter, $property, $expected_rdf_value) { + $expected_rdf_value += array('type' => 'literal'); + + // The field formatter will be rendered inside the entity. Set the field + // formatter in the entity display options before rendering the entity. + entity_get_display('entity_test', 'entity_test', 'default') + ->setComponent($this->fieldName, $formatter) + ->save(); + $build = entity_view($this->entity, 'default'); + $output = \Drupal::service('renderer')->renderRoot($build); + $graph = new \EasyRdf_Graph($this->uri, $output, 'rdfa'); + $this->setRawContent($output); + + // If verbose debugging is turned on, display the HTML and parsed RDF + // in the results. + if ($this->debug) { + print_r($output); + print_r($graph->toRdfPhp()); + } + + $this->assertTrue($graph->hasProperty($this->uri, $property, $expected_rdf_value), "Formatter {$formatter['type']} exposes data correctly for {$this->fieldType} fields."); + } + + /** + * Creates the field for testing. + * + * @param array $field_settings + * (optional) An array of field settings. + */ + protected function createTestField($field_settings = array()) { + FieldStorageConfig::create(array( + 'field_name' => $this->fieldName, + 'entity_type' => 'entity_test', + 'type' => $this->fieldType, + ))->save(); + FieldConfig::create([ + 'entity_type' => 'entity_test', + 'field_name' => $this->fieldName, + 'bundle' => 'entity_test', + 'settings' => $field_settings, + ])->save(); + } + + /** + * Gets the absolute URI of an entity. + * + * @param \Drupal\Core\Entity\ContentEntityBase $entity + * The entity for which to generate the URI. + * + * @return string + * The absolute URI. + */ + protected function getAbsoluteUri($entity) { + return $entity->url('canonical', array('absolute' => TRUE)); + } + + /** + * Parses a content and return the html element. + * + * @param string $content + * The html to parse. + * + * @return array + * An array containing simplexml objects. + */ + protected function parseContent($content) { + $htmlDom = new \DOMDocument(); + @$htmlDom->loadHTML('' . $content); + $elements = simplexml_import_dom($htmlDom); + + return $elements; + } + + /** + * Performs an xpath search on a certain content. + * + * The search is relative to the root element of the $content variable. + * + * @param string $content + * The html to parse. + * @param string $xpath + * The xpath string to use in the search. + * @param array $arguments + * Some arguments for the xpath. + * + * @return array|FALSE + * The return value of the xpath search. For details on the xpath string + * format and return values see the SimpleXML documentation, + * http://php.net/manual/function.simplexml-element-xpath.php. + */ + protected function xpathContent($content, $xpath, array $arguments = array()) { + if ($elements = $this->parseContent($content)) { + $xpath = $this->buildXPathQuery($xpath, $arguments); + $result = $elements->xpath($xpath); + // Some combinations of PHP / libxml versions return an empty array + // instead of the documented FALSE. Forcefully convert any falsish values + // to an empty array to allow foreach(...) constructions. + return $result ? $result : array(); + } + else { + return FALSE; + } + } + +} diff --git a/core/modules/rdf/tests/src/Kernel/Field/LinkFieldRdfaTest.php b/core/modules/rdf/tests/src/Kernel/Field/LinkFieldRdfaTest.php new file mode 100644 index 0000000..e65f271 --- /dev/null +++ b/core/modules/rdf/tests/src/Kernel/Field/LinkFieldRdfaTest.php @@ -0,0 +1,188 @@ +createTestField(); + + // Add the mapping. + $mapping = rdf_get_mapping('entity_test', 'entity_test'); + $mapping->setFieldMapping($this->fieldName, array( + 'properties' => array('schema:link'), + ))->save(); + + } + + /** + * Tests all formatters with link to external page. + */ + public function testAllFormattersExternal() { + // Set up test values. + $this->testValue = 'http://test.me/foo/bar/neque/porro/quisquam/est/qui-dolorem?foo/bar/neque/porro/quisquam/est/qui-dolorem'; + $this->entity = EntityTest::create(array()); + $this->entity->{$this->fieldName}->uri = $this->testValue; + + // Set up the expected result. + $expected_rdf = array( + 'value' => $this->testValue, + 'type' => 'uri', + ); + + $this->runTestAllFormatters($expected_rdf, 'external'); + } + + /** + * Tests all formatters with link to internal page. + */ + public function testAllFormattersInternal() { + // Set up test values. + $this->testValue = 'admin'; + $this->entity = EntityTest::create(array()); + $this->entity->{$this->fieldName}->uri = 'internal:/admin'; + + // Set up the expected result. + // AssertFormatterRdfa looks for a full path. + $expected_rdf = array( + 'value' => $this->uri . '/' . $this->testValue, + 'type' => 'uri', + ); + + $this->runTestAllFormatters($expected_rdf, 'internal'); + } + + /** + * Tests all formatters with link to frontpage. + */ + public function testAllFormattersFront() { + // Set up test values. + $this->testValue = '/'; + $this->entity = EntityTest::create(array()); + $this->entity->{$this->fieldName}->uri = 'internal:/'; + + // Set up the expected result. + $expected_rdf = array( + 'value' => $this->uri . '/', + 'type' => 'uri', + ); + + $this->runTestAllFormatters($expected_rdf, 'front'); + } + + /** + * Helper function to test all link formatters. + */ + public function runTestAllFormatters($expected_rdf, $type = NULL) { + + // Test the link formatter: trim at 80, no other settings. + $formatter = array( + 'type' => 'link', + 'settings' => array( + 'trim_length' => 80, + 'url_only' => FALSE, + 'url_plain' => FALSE, + 'rel' => '', + 'target' => '', + ), + ); + $this->assertFormatterRdfa($formatter, 'http://schema.org/link', $expected_rdf); + + // Test the link formatter: trim at 40, nofollow, new window. + $formatter = array( + 'type' => 'link', + 'settings' => array( + 'trim_length' => 40, + 'url_only' => FALSE, + 'url_plain' => FALSE, + 'rel' => 'nofollow', + 'target' => '_blank', + ), + ); + $this->assertFormatterRdfa($formatter, 'http://schema.org/link', $expected_rdf); + + // Test the link formatter: trim at 40, URL only (not plaintext) nofollow, + // new window. + $formatter = array( + 'type' => 'link', + 'settings' => array( + 'trim_length' => 40, + 'url_only' => TRUE, + 'url_plain' => FALSE, + 'rel' => 'nofollow', + 'target' => '_blank', + ), + ); + $this->assertFormatterRdfa($formatter, 'http://schema.org/link', $expected_rdf); + + // Test the link_separate formatter: trim at 40, nofollow, new window. + $formatter = array( + 'type' => 'link_separate', + 'settings' => array( + 'trim_length' => 40, + 'rel' => 'nofollow', + 'target' => '_blank', + ), + ); + $this->assertFormatterRdfa($formatter, 'http://schema.org/link', $expected_rdf); + + // Change the expected value here to literal. When formatted as plaintext + // then the RDF is expecting a 'literal' not a 'uri'. + $expected_rdf = array( + 'value' => $this->testValue, + 'type' => 'literal', + ); + // Test the link formatter: trim at 20, url only (as plaintext.) + $formatter = array( + 'type' => 'link', + 'settings' => array( + 'trim_length' => 20, + 'url_only' => TRUE, + 'url_plain' => TRUE, + 'rel' => '0', + 'target' => '0', + ), + ); + $this->assertFormatterRdfa($formatter, 'http://schema.org/link', $expected_rdf); + + // Test the link formatter: do not trim, url only (as plaintext.) + $formatter = array( + 'type' => 'link', + 'settings' => array( + 'trim_length' => 0, + 'url_only' => TRUE, + 'url_plain' => TRUE, + 'rel' => '0', + 'target' => '0', + ), + ); + $this->assertFormatterRdfa($formatter, 'http://schema.org/link', $expected_rdf); + } + +} diff --git a/core/modules/rdf/tests/src/Kernel/Field/NumberFieldRdfaTest.php b/core/modules/rdf/tests/src/Kernel/Field/NumberFieldRdfaTest.php new file mode 100644 index 0000000..52553a9 --- /dev/null +++ b/core/modules/rdf/tests/src/Kernel/Field/NumberFieldRdfaTest.php @@ -0,0 +1,207 @@ +fieldType = 'integer'; + $testValue = 3; + $this->createTestField(); + $this->createTestEntity($testValue); + $this->assertFormatterRdfa(array('type' => 'number_integer'), 'http://schema.org/baseSalary', array('value' => $testValue)); + + // Test that the content attribute is not created. + $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__items") and @content]'); + $this->assertFalse($result); + } + + /** + * Tests the integer formatter with settings. + */ + public function testIntegerFormatterWithSettings() { + \Drupal::service('theme_handler')->install(['classy']); + \Drupal::service('theme_handler')->setDefault('classy'); + $this->fieldType = 'integer'; + $formatter = array( + 'type' => 'number_integer', + 'settings' => array( + 'thousand_separator' => '.', + 'prefix_suffix' => TRUE, + ), + ); + $testValue = 3333333.33; + $field_settings = array( + 'prefix' => '#', + 'suffix' => ' llamas.', + ); + $this->createTestField($field_settings); + $this->createTestEntity($testValue); + $this->assertFormatterRdfa($formatter, 'http://schema.org/baseSalary', array('value' => $testValue)); + + // Test that the content attribute is created. + $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__item") and @content=:testValue]', array(':testValue' => $testValue)); + $this->assertTrue($result); + } + + /** + * Tests the float formatter. + */ + public function testFloatFormatter() { + $this->fieldType = 'float'; + $testValue = 3.33; + $this->createTestField(); + $this->createTestEntity($testValue); + $this->assertFormatterRdfa(array('type' => 'number_unformatted'), 'http://schema.org/baseSalary', array('value' => $testValue)); + + // Test that the content attribute is not created. + $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__items") and @content]'); + $this->assertFalse($result); + } + + /** + * Tests the float formatter with settings. + */ + public function testFloatFormatterWithSettings() { + \Drupal::service('theme_handler')->install(['classy']); + \Drupal::service('theme_handler')->setDefault('classy'); + $this->fieldType = 'float'; + $formatter = array( + 'type' => 'number_decimal', + 'settings' => array( + 'thousand_separator' => '.', + 'decimal_separator' => ',', + 'prefix_suffix' => TRUE, + ), + ); + $testValue = 3333333.33; + $field_settings = array( + 'prefix' => '$', + 'suffix' => ' more.', + ); + $this->createTestField($field_settings); + $this->createTestEntity($testValue); + $this->assertFormatterRdfa($formatter, 'http://schema.org/baseSalary', array('value' => $testValue)); + + // Test that the content attribute is created. + $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__item") and @content=:testValue]', array(':testValue' => $testValue)); + $this->assertTrue($result); + } + + /** + * Tests the float formatter with a scale. Scale is not exercised. + */ + public function testFloatFormatterWithScale() { + $this->fieldType = 'float'; + $formatter = array( + 'type' => 'number_decimal', + 'settings' => array( + 'scale' => 5, + ), + ); + $testValue = 3.33; + $this->createTestField(); + $this->createTestEntity($testValue); + $this->assertFormatterRdfa($formatter, 'http://schema.org/baseSalary', array('value' => $testValue)); + + // Test that the content attribute is not created. + $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__items") and @content]'); + $this->assertFalse($result); + } + + /** + * Tests the float formatter with a scale. Scale is exercised. + */ + public function testFloatFormatterWithScaleExercised() { + \Drupal::service('theme_handler')->install(['classy']); + \Drupal::service('theme_handler')->setDefault('classy'); + $this->fieldType = 'float'; + $formatter = array( + 'type' => 'number_decimal', + 'settings' => array( + 'scale' => 5, + ), + ); + $testValue = 3.1234567; + $this->createTestField(); + $this->createTestEntity($testValue); + $this->assertFormatterRdfa($formatter, 'http://schema.org/baseSalary', array('value' => $testValue)); + + // Test that the content attribute is created. + $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__item") and @content=:testValue]', array(':testValue' => $testValue)); + $this->assertTrue($result); + } + + /** + * Tests the decimal formatter. + */ + public function testDecimalFormatter() { + $this->fieldType = 'decimal'; + $testValue = 3.33; + $this->createTestField(); + $this->createTestEntity($testValue); + $this->assertFormatterRdfa(array('type' => 'number_decimal'), 'http://schema.org/baseSalary', array('value' => $testValue)); + + // Test that the content attribute is not created. + $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__items") and @content]'); + $this->assertFalse($result); + } + + /** + * Tests the decimal formatter with settings. + */ + public function testDecimalFormatterWithSettings() { + \Drupal::service('theme_handler')->install(['classy']); + \Drupal::service('theme_handler')->setDefault('classy'); + $this->fieldType = 'decimal'; + $formatter = array( + 'type' => 'number_decimal', + 'settings' => array( + 'thousand_separator' => 't', + 'decimal_separator' => '#', + 'prefix_suffix' => TRUE, + ), + ); + $testValue = 3333333.33; + $field_settings = array( + 'prefix' => '$', + 'suffix' => ' more.', + ); + $this->createTestField($field_settings); + $this->createTestEntity($testValue); + $this->assertFormatterRdfa($formatter, 'http://schema.org/baseSalary', array('value' => $testValue)); + + // Test that the content attribute is created. + $result = $this->xpathContent($this->getRawContent(), '//div[contains(@class, "field__item") and @content=:testValue]', array(':testValue' => $testValue)); + $this->assertTrue($result); + } + + /** + * Creates the RDF mapping for the field. + */ + protected function createTestEntity($testValue) { + // Add the mapping. + $mapping = rdf_get_mapping('entity_test', 'entity_test'); + $mapping->setFieldMapping($this->fieldName, array( + 'properties' => array('schema:baseSalary'), + ))->save(); + + // Set up test entity. + $this->entity = EntityTest::create(array()); + $this->entity->{$this->fieldName}->value = $testValue; + } +} diff --git a/core/modules/rdf/tests/src/Kernel/Field/StringFieldRdfaTest.php b/core/modules/rdf/tests/src/Kernel/Field/StringFieldRdfaTest.php new file mode 100644 index 0000000..42da79b --- /dev/null +++ b/core/modules/rdf/tests/src/Kernel/Field/StringFieldRdfaTest.php @@ -0,0 +1,61 @@ +createTestField(); + + // Add the mapping. + $mapping = rdf_get_mapping('entity_test', 'entity_test'); + $mapping->setFieldMapping($this->fieldName, array( + 'properties' => array('schema:text'), + ))->save(); + + // Set up test entity. + $this->entity = EntityTest::create(); + $this->entity->{$this->fieldName}->value = $this->testValue; + $this->entity->{$this->fieldName}->summary = $this->testSummary; + } + + /** + * Tests string formatters. + */ + public function testStringFormatters() { + // Tests the string formatter. + $this->assertFormatterRdfa(array('type'=>'string'), 'http://schema.org/text', array('value' => $this->testValue)); + } +} diff --git a/core/modules/rdf/tests/src/Kernel/Field/TelephoneFieldRdfaTest.php b/core/modules/rdf/tests/src/Kernel/Field/TelephoneFieldRdfaTest.php new file mode 100644 index 0000000..6f7bcae --- /dev/null +++ b/core/modules/rdf/tests/src/Kernel/Field/TelephoneFieldRdfaTest.php @@ -0,0 +1,72 @@ +createTestField(); + + // Add the mapping. + $mapping = rdf_get_mapping('entity_test', 'entity_test'); + $mapping->setFieldMapping($this->fieldName, array( + 'properties' => array('schema:telephone'), + ))->save(); + + // Set up test values. + $this->testValue = '555-555-5555'; + $this->entity = EntityTest::create(array()); + $this->entity->{$this->fieldName}->value = $this->testValue; + } + + /** + * Tests the field formatters. + */ + public function testAllFormatters() { + // Tests the plain formatter. + $this->assertFormatterRdfa(array('type' => 'string'), 'http://schema.org/telephone', array('value' => $this->testValue)); + // Tests the telephone link formatter. + $this->assertFormatterRdfa(array('type' => 'telephone_link'), 'http://schema.org/telephone', array('value' => 'tel:' . $this->testValue, 'type' => 'uri')); + + $formatter = array( + 'type' => 'telephone_link', + 'settings' => array('title' => 'Contact us'), + ); + $expected_rdf_value = array( + 'value' => 'tel:' . $this->testValue, + 'type' => 'uri', + ); + // Tests the telephone link formatter with custom title. + $this->assertFormatterRdfa($formatter, 'http://schema.org/telephone', $expected_rdf_value); + } +} diff --git a/core/modules/rdf/tests/src/Kernel/Field/TextFieldRdfaTest.php b/core/modules/rdf/tests/src/Kernel/Field/TextFieldRdfaTest.php new file mode 100644 index 0000000..c8ad5e2 --- /dev/null +++ b/core/modules/rdf/tests/src/Kernel/Field/TextFieldRdfaTest.php @@ -0,0 +1,76 @@ +installConfig(array('filter')); + + $this->createTestField(); + + // Add the mapping. + $mapping = rdf_get_mapping('entity_test', 'entity_test'); + $mapping->setFieldMapping($this->fieldName, array( + 'properties' => array('schema:text'), + ))->save(); + + // Set up test entity. + $this->entity = EntityTest::create(); + $this->entity->{$this->fieldName}->value = $this->testValue; + $this->entity->{$this->fieldName}->summary = $this->testSummary; + } + + /** + * Tests all formatters. + * + * @todo Check for the summary mapping. + */ + public function testAllFormatters() { + $formatted_value = strip_tags($this->entity->{$this->fieldName}->processed); + + // Tests the default formatter. + $this->assertFormatterRdfa(array('type'=>'text_default'), 'http://schema.org/text', array('value' => $formatted_value)); + // Tests the summary formatter. + $this->assertFormatterRdfa(array('type'=>'text_summary_or_trimmed'), 'http://schema.org/text', array('value' => $formatted_value)); + // Tests the trimmed formatter. + $this->assertFormatterRdfa(array('type'=>'text_trimmed'), 'http://schema.org/text', array('value' => $formatted_value)); + } +} diff --git a/core/modules/responsive_image/responsive_image.module b/core/modules/responsive_image/responsive_image.module index aa959a3..7df1da8 100644 --- a/core/modules/responsive_image/responsive_image.module +++ b/core/modules/responsive_image/responsive_image.module @@ -37,7 +37,7 @@ function responsive_image_help($route_name, RouteMatchInterface $route_match) { $output .= '
                          ' . t('Breakpoint groups: viewport sizing vs art direction') . '
                          '; $output .= '
                          ' . t('The breakpoint group typically only needs a single breakpoint with an empty media query in order to do viewport sizing. Multiple breakpoints are used for changing the crop or aspect ratio of images at different viewport sizes, which is often referred to as art direction. Once you select a breakpoint group, you can choose which breakpoints to use for the responsive image style. By default, the option do not use this breakpoint is selected for each breakpoint. See the help page of the Breakpoint module for more information.', array(':breakpoint_help' => \Drupal::url('help.page', array('name' => 'breakpoint')))) . '
                          '; $output .= '
                          ' . t('Breakpoint settings: sizes vs image styles') . '
                          '; - $output .= '
                          ' . t('While you have the option to provide only image style per breakpoint, the sizes option allows you to provide more options to browsers as to which image file it can display, even when using multiple breakpoints for art direction. Breakpoints are defined in the configuration files of the theme.
                          '); + $output .= '
                          ' . t('While you have the option to provide only one image style per breakpoint, the sizes option allows you to provide more options to browsers as to which image file it can display, even when using multiple breakpoints for art direction. Breakpoints are defined in the configuration files of the theme.
                          '); $output .= '
                          ' . t('Sizes field') . '
                          '; $output .= '
                          ' . t('Once the sizes option is selected, you can let the browser know the size of this image in relation to the site layout, using the Sizes field. For a hero image that always fills the entire screen, you could simply enter 100vw, which means 100% of the viewport width. For an image that fills 90% of the screen for small viewports, but only fills 40% of the screen when the viewport is larger than 40em (typically 640px), you could enter "(min-width: 40em) 40vw, 90vw" in the Sizes field. The last item in the comma-separated list is the smallest viewport size: other items in the comma-separated list should have a media condition paired with an image width. Media conditions are similar to a media query, often a min-width paired with a viewport width using em or px units: e.g. (min-width: 640px) or (min-width: 40em). This is paired with the image width at that viewport size using px, em or vw units. The vw unit is viewport width and is used instead of a percentage because the percentage always refers to the width of the entire viewport.
                          '); $output .= '
                          ' . t('Image styles for sizes') . '
                          '; diff --git a/core/modules/responsive_image/src/Tests/ResponsiveImageFieldDisplayTest.php b/core/modules/responsive_image/src/Tests/ResponsiveImageFieldDisplayTest.php index a6f2930..e8e7aae 100644 --- a/core/modules/responsive_image/src/Tests/ResponsiveImageFieldDisplayTest.php +++ b/core/modules/responsive_image/src/Tests/ResponsiveImageFieldDisplayTest.php @@ -13,6 +13,7 @@ use Drupal\node\Entity\Node; use Drupal\file\Entity\File; use Drupal\responsive_image\Plugin\Field\FieldFormatter\ResponsiveImageFormatter; +use Drupal\responsive_image\Entity\ResponsiveImageStyle; use Drupal\user\RoleInterface; /** @@ -60,7 +61,7 @@ protected function setUp() { )); $this->drupalLogin($this->adminUser); // Add responsive image style. - $this->responsiveImgStyle = entity_create('responsive_image_style', array( + $this->responsiveImgStyle = ResponsiveImageStyle::create(array( 'id' => 'style_one', 'label' => 'Style One', 'breakpoint_group' => 'responsive_image_test_module', diff --git a/core/modules/responsive_image/src/Tests/ResponsiveImageFieldUiTest.php b/core/modules/responsive_image/src/Tests/ResponsiveImageFieldUiTest.php index af5a955..214ba2b 100644 --- a/core/modules/responsive_image/src/Tests/ResponsiveImageFieldUiTest.php +++ b/core/modules/responsive_image/src/Tests/ResponsiveImageFieldUiTest.php @@ -10,6 +10,7 @@ use Drupal\field_ui\Tests\FieldUiTestTrait; use Drupal\simpletest\WebTestBase; +use Drupal\responsive_image\Entity\ResponsiveImageStyle; /** * Tests the "Responsive Image" formatter settings form. @@ -65,7 +66,7 @@ function testResponsiveImageFormatterUI() { $this->assertText("Select a responsive image style.", 'The expected summary is displayed.'); // Create responsive image styles. - $responsive_image_style = entity_create('responsive_image_style', array( + $responsive_image_style = ResponsiveImageStyle::create(array( 'id' => 'style_one', 'label' => 'Style One', 'breakpoint_group' => 'responsive_image_test_module', diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php index a483044..58270a4 100644 --- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php +++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php @@ -18,6 +18,8 @@ /** * Represents entities as resources. * + * @see \Drupal\rest\Plugin\Deriver\EntityDeriver + * * @RestResource( * id = "entity", * label = @Translation("Entity"), @@ -28,8 +30,6 @@ * "https://www.drupal.org/link-relations/create" = "/entity/{entity_type}" * } * ) - * - * @see \Drupal\rest\Plugin\Deriver\EntityDeriver */ class EntityResource extends ResourceBase { @@ -112,9 +112,10 @@ public function post(EntityInterface $entity = NULL) { $entity->save(); $this->logger->notice('Created entity %type with ID %id.', array('%type' => $entity->getEntityTypeId(), '%id' => $entity->id())); - // 201 Created responses have an empty body. + // 201 Created responses return the newly created entity in the response + // body. $url = $entity->urlInfo('canonical', ['absolute' => TRUE])->toString(TRUE); - $response = new ResourceResponse(NULL, 201, ['Location' => $url->getGeneratedUrl()]); + $response = new ResourceResponse($entity, 201, ['Location' => $url->getGeneratedUrl()]); // Responses after creating an entity are not cacheable, so we add no // cacheability metadata here. return $response; diff --git a/core/modules/rest/src/Tests/CreateTest.php b/core/modules/rest/src/Tests/CreateTest.php index fc6037c..47673af 100644 --- a/core/modules/rest/src/Tests/CreateTest.php +++ b/core/modules/rest/src/Tests/CreateTest.php @@ -361,8 +361,14 @@ public function createAccountPerEntity($entity_type) { public function assertCreateEntityOverRestApi($entity_type, $serialized = NULL) { // Note: this will fail with PHP 5.6 when always_populate_raw_post_data is // set to something other than -1. See https://www.drupal.org/node/2456025. - $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType); + $response = $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, $this->defaultMimeType); $this->assertResponse(201); + + // Make sure that the response includes an entity in the body and check the + // UUID as an example. + $request = Json::decode($serialized); + $response = Json::decode($response); + $this->assertEqual($request['uuid'][0]['value'], $response['uuid'][0]['value'], 'Got new entity created as response after successful POST over Rest API'); } /** diff --git a/core/modules/rest/src/Tests/CsrfTest.php b/core/modules/rest/src/Tests/CsrfTest.php index af49a33..25beae1 100644 --- a/core/modules/rest/src/Tests/CsrfTest.php +++ b/core/modules/rest/src/Tests/CsrfTest.php @@ -54,7 +54,9 @@ protected function setUp() { // request. $serializer = $this->container->get('serializer'); $entity_values = $this->entityValues($this->testEntityType); - $entity = entity_create($this->testEntityType, $entity_values); + $entity = $this->container->get('entity_type.manager') + ->getStorage($this->testEntityType) + ->create($entity_values); $this->serialized = $serializer->serialize($entity, $this->defaultFormat); } diff --git a/core/modules/rest/src/Tests/RESTTestBase.php b/core/modules/rest/src/Tests/RESTTestBase.php index db7d123..e503216 100644 --- a/core/modules/rest/src/Tests/RESTTestBase.php +++ b/core/modules/rest/src/Tests/RESTTestBase.php @@ -93,6 +93,7 @@ protected function httpRequest($url, $method, $body = NULL, $mime_type = NULL) { $url = $this->buildUrl($url); + $curl_options = array(); switch ($method) { case 'GET': // Set query if there are additional GET parameters. @@ -105,6 +106,16 @@ protected function httpRequest($url, $method, $body = NULL, $mime_type = NULL) { ); break; + case 'HEAD': + $curl_options = array( + CURLOPT_HTTPGET => FALSE, + CURLOPT_CUSTOMREQUEST => 'HEAD', + CURLOPT_URL => $url, + CURLOPT_NOBODY => TRUE, + CURLOPT_HTTPHEADER => array('Accept: ' . $mime_type), + ); + break; + case 'POST': $curl_options = array( CURLOPT_HTTPGET => FALSE, @@ -183,7 +194,9 @@ protected function httpRequest($url, $method, $body = NULL, $mime_type = NULL) { * The new entity object. */ protected function entityCreate($entity_type) { - return entity_create($entity_type, $this->entityValues($entity_type)); + return $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create($this->entityValues($entity_type)); } /** diff --git a/core/modules/rest/src/Tests/ReadTest.php b/core/modules/rest/src/Tests/ReadTest.php index eb3f222..1cf1eac 100644 --- a/core/modules/rest/src/Tests/ReadTest.php +++ b/core/modules/rest/src/Tests/ReadTest.php @@ -94,7 +94,7 @@ public function testRead() { // application/hal+json, so it returns a 406. $this->assertResponse('406', 'HTTP response code is 406 when the resource does not define formats, because it falls back to the canonical, non-REST route.'); $this->assertEqual($response, Json::encode([ - 'message' => 'Not acceptable', + 'message' => 'Not acceptable format: hal_json', ])); } diff --git a/core/modules/rest/src/Tests/RestLinkManagerTest.php b/core/modules/rest/src/Tests/RestLinkManagerTest.php index 4adee7c..a8acb33 100644 --- a/core/modules/rest/src/Tests/RestLinkManagerTest.php +++ b/core/modules/rest/src/Tests/RestLinkManagerTest.php @@ -25,7 +25,6 @@ class RestLinkManagerTest extends KernelTestBase { */ protected function setUp() { parent::setUp(); - $this->installSchema('system', ['router']); \Drupal::service('router.builder')->rebuild(); } diff --git a/core/modules/rest/src/Tests/UpdateTest.php b/core/modules/rest/src/Tests/UpdateTest.php index ef18a74..f407c1e 100644 --- a/core/modules/rest/src/Tests/UpdateTest.php +++ b/core/modules/rest/src/Tests/UpdateTest.php @@ -50,7 +50,9 @@ public function testPatchUpdate() { 'value' => $this->randomString(), 'format' => 'plain_text', )); - $patch_entity = entity_create($entity_type, $patch_values); + $patch_entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create($patch_values); // We don't want to overwrite the UUID. $patch_entity->set('uuid', NULL); $serialized = $serializer->serialize($patch_entity, $this->defaultFormat, $context); diff --git a/core/modules/rest/src/Tests/Views/StyleSerializerTest.php b/core/modules/rest/src/Tests/Views/StyleSerializerTest.php index b590504..7e882a6 100644 --- a/core/modules/rest/src/Tests/Views/StyleSerializerTest.php +++ b/core/modules/rest/src/Tests/Views/StyleSerializerTest.php @@ -66,7 +66,7 @@ protected function setUp() { // Save some entity_test entities. for ($i = 1; $i <= 10; $i++) { - entity_create('entity_test', array('name' => 'test_' . $i, 'user_id' => $this->adminUser->id()))->save(); + EntityTest::create(array('name' => 'test_' . $i, 'user_id' => $this->adminUser->id()))->save(); } $this->enableViewsTestModule(); diff --git a/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php b/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php index 4b30b88..aa01d15 100644 --- a/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php +++ b/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php @@ -13,7 +13,6 @@ use Drupal\rest\Plugin\Type\ResourcePluginManager; use Drupal\rest\RequestHandler; use Drupal\rest\ResourceResponse; -use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; diff --git a/core/modules/search/src/Controller/SearchController.php b/core/modules/search/src/Controller/SearchController.php index 0893259..000ecf2 100644 --- a/core/modules/search/src/Controller/SearchController.php +++ b/core/modules/search/src/Controller/SearchController.php @@ -12,7 +12,6 @@ use Drupal\Core\Render\RendererInterface; use Drupal\search\SearchPageInterface; use Drupal\search\SearchPageRepositoryInterface; -use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -47,14 +46,12 @@ class SearchController extends ControllerBase { * * @param \Drupal\search\SearchPageRepositoryInterface $search_page_repository * The search page repository. - * @param \Psr\Log\LoggerInterface $logger - * A logger instance. * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer. */ - public function __construct(SearchPageRepositoryInterface $search_page_repository, LoggerInterface $logger, RendererInterface $renderer) { + public function __construct(SearchPageRepositoryInterface $search_page_repository, RendererInterface $renderer) { $this->searchPageRepository = $search_page_repository; - $this->logger = $logger; + $this->logger = $this->getLogger('search'); $this->renderer = $renderer; } @@ -64,7 +61,6 @@ public function __construct(SearchPageRepositoryInterface $search_page_repositor public static function create(ContainerInterface $container) { return new static( $container->get('search.search_page_repository'), - $container->get('logger.factory')->get('search'), $container->get('renderer') ); } diff --git a/core/modules/search/src/SearchPageAccessControlHandler.php b/core/modules/search/src/SearchPageAccessControlHandler.php index 3bd2a6e..36b952a 100644 --- a/core/modules/search/src/SearchPageAccessControlHandler.php +++ b/core/modules/search/src/SearchPageAccessControlHandler.php @@ -27,21 +27,21 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter /** @var $entity \Drupal\search\SearchPageInterface */ if (in_array($operation, array('delete', 'disable'))) { if ($entity->isDefaultSearch()) { - return AccessResult::forbidden()->cacheUntilEntityChanges($entity); + return AccessResult::forbidden()->addCacheableDependency($entity); } else { - return parent::checkAccess($entity, $operation, $account)->cacheUntilEntityChanges($entity); + return parent::checkAccess($entity, $operation, $account)->addCacheableDependency($entity); } } if ($operation == 'view') { if (!$entity->status()) { - return AccessResult::forbidden()->cacheUntilEntityChanges($entity); + return AccessResult::forbidden()->addCacheableDependency($entity); } $plugin = $entity->getPlugin(); if ($plugin instanceof AccessibleInterface) { - return $plugin->access($operation, $account, TRUE)->cacheUntilEntityChanges($entity); + return $plugin->access($operation, $account, TRUE)->addCacheableDependency($entity); } - return AccessResult::allowed()->cacheUntilEntityChanges($entity); + return AccessResult::allowed()->addCacheableDependency($entity); } return parent::checkAccess($entity, $operation, $account); } diff --git a/core/modules/search/src/Tests/Migrate/d6/MigrateSearchPageTest.php b/core/modules/search/src/Tests/Migrate/d6/MigrateSearchPageTest.php deleted file mode 100644 index 8f04775..0000000 --- a/core/modules/search/src/Tests/Migrate/d6/MigrateSearchPageTest.php +++ /dev/null @@ -1,72 +0,0 @@ -executeMigration('search_page'); - } - - /** - * Tests Drupal 6 search settings to Drupal 8 search page entity migration. - */ - public function testSearchPage() { - $id = 'node_search'; - /** @var \Drupal\search\Entity\SearchPage $search_page */ - $search_page = SearchPage::load($id); - $this->assertIdentical($id, $search_page->id()); - $configuration = $search_page->getPlugin()->getConfiguration(); - $this->assertIdentical($configuration['rankings'], array( - 'comments' => 5, - 'promote' => 0, - 'recent' => 0, - 'relevance' => 2, - 'sticky' => 8, - 'views' => 1, - )); - $this->assertIdentical('node', $search_page->getPath()); - - // Test that we can re-import using the EntitySearchPage destination. - Database::getConnection('default', 'migrate') - ->update('variable') - ->fields(array('value' => serialize(4))) - ->condition('name', 'node_rank_comments') - ->execute(); - - /** @var \Drupal\migrate\Entity\MigrationInterface $migration */ - $migration = \Drupal::entityManager() - ->getStorage('migration') - ->loadUnchanged('search_page'); - // Indicate we're rerunning a migration that's already run. - $migration->getIdMap()->prepareUpdate(); - $this->executeMigration($migration); - - $configuration = SearchPage::load($id)->getPlugin()->getConfiguration(); - $this->assertIdentical(4, $configuration['rankings']['comments']); - } - -} diff --git a/core/modules/search/src/Tests/Migrate/d6/MigrateSearchSettingsTest.php b/core/modules/search/src/Tests/Migrate/d6/MigrateSearchSettingsTest.php deleted file mode 100644 index 634a3a4..0000000 --- a/core/modules/search/src/Tests/Migrate/d6/MigrateSearchSettingsTest.php +++ /dev/null @@ -1,47 +0,0 @@ -executeMigration('d6_search_settings'); - } - - /** - * Tests migration of search variables to search.settings.yml. - */ - public function testSearchSettings() { - $config = $this->config('search.settings'); - $this->assertIdentical(3, $config->get('index.minimum_word_size')); - $this->assertIdentical(TRUE, $config->get('index.overlap_cjk')); - $this->assertIdentical(100, $config->get('index.cron_limit')); - $this->assertIdentical(TRUE, $config->get('logging')); - $this->assertConfigSchema(\Drupal::service('config.typed'), 'search.settings', $config->get()); - } - -} diff --git a/core/modules/search/src/Tests/Migrate/d7/MigrateSearchPageTest.php b/core/modules/search/src/Tests/Migrate/d7/MigrateSearchPageTest.php deleted file mode 100644 index 4781dbc..0000000 --- a/core/modules/search/src/Tests/Migrate/d7/MigrateSearchPageTest.php +++ /dev/null @@ -1,74 +0,0 @@ -executeMigration('search_page'); - } - - /** - * Tests Drupal 7 search ranking to Drupal 8 search page entity migration. - */ - public function testSearchPage() { - $id = 'node_search'; - /** @var \Drupal\search\Entity\SearchPage $search_page */ - $search_page = SearchPage::load($id); - $this->assertIdentical($id, $search_page->id()); - $configuration = $search_page->getPlugin()->getConfiguration(); - $expected_rankings = array( - 'comments' => 0, - 'promote' => 0, - 'relevance' => 2, - 'sticky' => 0, - 'views' => 0, - ); - $this->assertIdentical($expected_rankings, $configuration['rankings']); - $this->assertIdentical('node', $search_page->getPath()); - - // Test that we can re-import using the EntitySearchPage destination. - Database::getConnection('default', 'migrate') - ->update('variable') - ->fields(array('value' => serialize(4))) - ->condition('name', 'node_rank_comments') - ->execute(); - - /** @var \Drupal\migrate\Entity\MigrationInterface $migration */ - $migration = \Drupal::entityManager() - ->getStorage('migration') - ->loadUnchanged('search_page'); - // Indicate we're rerunning a migration that's already run. - $migration->getIdMap()->prepareUpdate(); - $this->executeMigration($migration); - - $configuration = SearchPage::load($id)->getPlugin()->getConfiguration(); - $this->assertIdentical(4, $configuration['rankings']['comments']); - } - -} diff --git a/core/modules/search/src/Tests/Migrate/d7/MigrateSearchSettingsTest.php b/core/modules/search/src/Tests/Migrate/d7/MigrateSearchSettingsTest.php deleted file mode 100644 index ac0e4c0..0000000 --- a/core/modules/search/src/Tests/Migrate/d7/MigrateSearchSettingsTest.php +++ /dev/null @@ -1,54 +0,0 @@ -executeMigration('d7_search_settings'); - } - - /** - * Tests the migration of Search's variables to configuration. - */ - public function testSearchSettings() { - $config = $this->config('search.settings'); - $this->assertIdentical('node_search', $config->get('default_page')); - $this->assertIdentical(4, $config->get('index.minimum_word_size')); - $this->assertTrue($config->get('index.overlap_cjk')); - $this->assertIdentical(100, $config->get('index.cron_limit')); - $this->assertIdentical(7, $config->get('and_or_limit')); - $this->assertIdentical(25, $config->get('index.tag_weights.h1')); - $this->assertIdentical(18, $config->get('index.tag_weights.h2')); - $this->assertIdentical(15, $config->get('index.tag_weights.h3')); - $this->assertIdentical(12, $config->get('index.tag_weights.h4')); - $this->assertIdentical(9, $config->get('index.tag_weights.h5')); - $this->assertIdentical(6, $config->get('index.tag_weights.h6')); - $this->assertIdentical(3, $config->get('index.tag_weights.u')); - $this->assertIdentical(3, $config->get('index.tag_weights.b')); - $this->assertIdentical(3, $config->get('index.tag_weights.i')); - $this->assertIdentical(3, $config->get('index.tag_weights.strong')); - $this->assertIdentical(3, $config->get('index.tag_weights.em')); - $this->assertIdentical(10, $config->get('index.tag_weights.a')); - $this->assertTrue($config->get('logging')); - } - -} diff --git a/core/modules/search/src/Tests/SearchBlockTest.php b/core/modules/search/src/Tests/SearchBlockTest.php index 5406502..2da3da7 100644 --- a/core/modules/search/src/Tests/SearchBlockTest.php +++ b/core/modules/search/src/Tests/SearchBlockTest.php @@ -78,7 +78,7 @@ public function testSearchFormBlock() { $this->assertEqual( $this->getUrl(), \Drupal::url('search.view_' . $entity_id, array(), array('query' => array('keys' => $terms['keys']), 'absolute' => TRUE)), - 'Submitted to correct url.' + 'Submitted to correct URL.' ); // Test an empty search via the block form, from the front page. @@ -92,7 +92,7 @@ public function testSearchFormBlock() { $this->assertEqual( $this->getUrl(), \Drupal::url('search.view_' . $entity_id, array(), array('query' => array('keys' => ''), 'absolute' => TRUE)), - 'Redirected to correct url.' + 'Redirected to correct URL.' ); // Test that after entering a too-short keyword in the form, you can then diff --git a/core/modules/search/src/Tests/SearchCommentTest.php b/core/modules/search/src/Tests/SearchCommentTest.php index d7f7255..faec089 100644 --- a/core/modules/search/src/Tests/SearchCommentTest.php +++ b/core/modules/search/src/Tests/SearchCommentTest.php @@ -11,6 +11,7 @@ use Drupal\comment\Tests\CommentTestTrait; use Drupal\field\Entity\FieldConfig; use Drupal\user\RoleInterface; +use Drupal\filter\Entity\FilterFormat; /** * Tests integration searching comments. @@ -59,7 +60,7 @@ class SearchCommentTest extends SearchTestBase { protected function setUp() { parent::setUp(); - $full_html_format = entity_create('filter_format', array( + $full_html_format = FilterFormat::create(array( 'format' => 'full_html', 'name' => 'Full HTML', 'weight' => 1, @@ -90,7 +91,7 @@ protected function setUp() { function testSearchResultsComment() { $node_storage = $this->container->get('entity.manager')->getStorage('node'); // Create basic_html format that escapes all HTML. - $basic_html_format = entity_create('filter_format', array( + $basic_html_format = FilterFormat::create(array( 'format' => 'basic_html', 'name' => 'Basic HTML', 'weight' => 1, diff --git a/core/modules/search/src/Tests/SearchRankingTest.php b/core/modules/search/src/Tests/SearchRankingTest.php index 4f12cdf..6c9f0e9 100644 --- a/core/modules/search/src/Tests/SearchRankingTest.php +++ b/core/modules/search/src/Tests/SearchRankingTest.php @@ -10,6 +10,7 @@ use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\comment\Tests\CommentTestTrait; use Drupal\Core\Url; +use Drupal\filter\Entity\FilterFormat; /** * Indexes content and tests ranking factors. @@ -203,7 +204,7 @@ public function testRankings() { * Test rankings of HTML tags. */ public function testHTMLRankings() { - $full_html_format = entity_create('filter_format', array( + $full_html_format = FilterFormat::create(array( 'format' => 'full_html', 'name' => 'Full HTML', )); diff --git a/core/modules/search/tests/modules/search_query_alter/search_query_alter.module b/core/modules/search/tests/modules/search_query_alter/search_query_alter.module index 57bdb16..aee8e6c 100644 --- a/core/modules/search/tests/modules/search_query_alter/search_query_alter.module +++ b/core/modules/search/tests/modules/search_query_alter/search_query_alter.module @@ -1,12 +1,12 @@ executeMigration('search_page'); + } + + /** + * Tests Drupal 6 search settings to Drupal 8 search page entity migration. + */ + public function testSearchPage() { + $id = 'node_search'; + /** @var \Drupal\search\Entity\SearchPage $search_page */ + $search_page = SearchPage::load($id); + $this->assertIdentical($id, $search_page->id()); + $configuration = $search_page->getPlugin()->getConfiguration(); + $this->assertIdentical($configuration['rankings'], array( + 'comments' => 5, + 'promote' => 0, + 'recent' => 0, + 'relevance' => 2, + 'sticky' => 8, + 'views' => 1, + )); + $this->assertIdentical('node', $search_page->getPath()); + + // Test that we can re-import using the EntitySearchPage destination. + Database::getConnection('default', 'migrate') + ->update('variable') + ->fields(array('value' => serialize(4))) + ->condition('name', 'node_rank_comments') + ->execute(); + + /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ + $migration = $this->getMigration('search_page'); + // Indicate we're rerunning a migration that's already run. + $migration->getIdMap()->prepareUpdate(); + $this->executeMigration($migration); + + $configuration = SearchPage::load($id)->getPlugin()->getConfiguration(); + $this->assertIdentical(4, $configuration['rankings']['comments']); + } + +} diff --git a/core/modules/search/tests/src/Kernel/Migrate/d6/MigrateSearchSettingsTest.php b/core/modules/search/tests/src/Kernel/Migrate/d6/MigrateSearchSettingsTest.php new file mode 100644 index 0000000..a37a89e --- /dev/null +++ b/core/modules/search/tests/src/Kernel/Migrate/d6/MigrateSearchSettingsTest.php @@ -0,0 +1,47 @@ +executeMigration('d6_search_settings'); + } + + /** + * Tests migration of search variables to search.settings.yml. + */ + public function testSearchSettings() { + $config = $this->config('search.settings'); + $this->assertIdentical(3, $config->get('index.minimum_word_size')); + $this->assertIdentical(TRUE, $config->get('index.overlap_cjk')); + $this->assertIdentical(100, $config->get('index.cron_limit')); + $this->assertIdentical(TRUE, $config->get('logging')); + $this->assertConfigSchema(\Drupal::service('config.typed'), 'search.settings', $config->get()); + } + +} diff --git a/core/modules/search/tests/src/Kernel/Migrate/d7/MigrateSearchPageTest.php b/core/modules/search/tests/src/Kernel/Migrate/d7/MigrateSearchPageTest.php new file mode 100644 index 0000000..44cb6ad --- /dev/null +++ b/core/modules/search/tests/src/Kernel/Migrate/d7/MigrateSearchPageTest.php @@ -0,0 +1,72 @@ +executeMigration('search_page'); + } + + /** + * Tests Drupal 7 search ranking to Drupal 8 search page entity migration. + */ + public function testSearchPage() { + $id = 'node_search'; + /** @var \Drupal\search\Entity\SearchPage $search_page */ + $search_page = SearchPage::load($id); + $this->assertIdentical($id, $search_page->id()); + $configuration = $search_page->getPlugin()->getConfiguration(); + $expected_rankings = array( + 'comments' => 0, + 'promote' => 0, + 'relevance' => 2, + 'sticky' => 0, + 'views' => 0, + ); + $this->assertIdentical($expected_rankings, $configuration['rankings']); + $this->assertIdentical('node', $search_page->getPath()); + + // Test that we can re-import using the EntitySearchPage destination. + Database::getConnection('default', 'migrate') + ->update('variable') + ->fields(array('value' => serialize(4))) + ->condition('name', 'node_rank_comments') + ->execute(); + + /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ + $migration = $this->getMigration('search_page'); + // Indicate we're rerunning a migration that's already run. + $migration->getIdMap()->prepareUpdate(); + $this->executeMigration($migration); + + $configuration = SearchPage::load($id)->getPlugin()->getConfiguration(); + $this->assertIdentical(4, $configuration['rankings']['comments']); + } + +} diff --git a/core/modules/search/tests/src/Kernel/Migrate/d7/MigrateSearchSettingsTest.php b/core/modules/search/tests/src/Kernel/Migrate/d7/MigrateSearchSettingsTest.php new file mode 100644 index 0000000..8373e05 --- /dev/null +++ b/core/modules/search/tests/src/Kernel/Migrate/d7/MigrateSearchSettingsTest.php @@ -0,0 +1,54 @@ +executeMigration('d7_search_settings'); + } + + /** + * Tests the migration of Search's variables to configuration. + */ + public function testSearchSettings() { + $config = $this->config('search.settings'); + $this->assertIdentical('node_search', $config->get('default_page')); + $this->assertIdentical(4, $config->get('index.minimum_word_size')); + $this->assertTrue($config->get('index.overlap_cjk')); + $this->assertIdentical(100, $config->get('index.cron_limit')); + $this->assertIdentical(7, $config->get('and_or_limit')); + $this->assertIdentical(25, $config->get('index.tag_weights.h1')); + $this->assertIdentical(18, $config->get('index.tag_weights.h2')); + $this->assertIdentical(15, $config->get('index.tag_weights.h3')); + $this->assertIdentical(12, $config->get('index.tag_weights.h4')); + $this->assertIdentical(9, $config->get('index.tag_weights.h5')); + $this->assertIdentical(6, $config->get('index.tag_weights.h6')); + $this->assertIdentical(3, $config->get('index.tag_weights.u')); + $this->assertIdentical(3, $config->get('index.tag_weights.b')); + $this->assertIdentical(3, $config->get('index.tag_weights.i')); + $this->assertIdentical(3, $config->get('index.tag_weights.strong')); + $this->assertIdentical(3, $config->get('index.tag_weights.em')); + $this->assertIdentical(10, $config->get('index.tag_weights.a')); + $this->assertTrue($config->get('logging')); + } + +} diff --git a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php index d007bd9..0fb0a0b 100644 --- a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php +++ b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php @@ -27,12 +27,18 @@ class EntityReferenceFieldItemNormalizer extends ComplexDataNormalizer { public function normalize($field_item, $format = NULL, array $context = []) { $values = parent::normalize($field_item, $format, $context); - // Add a 'url' value if there is a reference and a canonical URL. Hard code - // 'canonical' here as config entities override the default $rel parameter - // value to 'edit-form. /** @var \Drupal\Core\Entity\EntityInterface $entity */ - if (($entity = $field_item->get('entity')->getValue()) && ($url = $entity->url('canonical'))) { - $values['url'] = $url; + if ($entity = $field_item->get('entity')->getValue()) { + $values['target_type'] = $entity->getEntityTypeId(); + // Add the target entity UUID to the normalized output values. + $values['target_uuid'] = $entity->uuid(); + + // Add a 'url' value if there is a reference and a canonical URL. Hard + // code 'canonical' here as config entities override the default $rel + // parameter value to 'edit-form. + if ($url = $entity->url('canonical')) { + $values['url'] = $url; + } } return $values; diff --git a/core/modules/serialization/src/Tests/EntityResolverTest.php b/core/modules/serialization/src/Tests/EntityResolverTest.php index e362b8d..481afc3 100644 --- a/core/modules/serialization/src/Tests/EntityResolverTest.php +++ b/core/modules/serialization/src/Tests/EntityResolverTest.php @@ -8,6 +8,8 @@ use Drupal\Core\Url; use Drupal\entity_test\Entity\EntityTestMulRev; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests that entities references can be resolved. @@ -36,7 +38,7 @@ protected function setUp() { \Drupal::service('router.builder')->rebuild(); // Create the test field storage. - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'entity_type' => 'entity_test_mulrev', 'field_name' => 'field_test_entity_reference', 'type' => 'entity_reference', @@ -46,11 +48,11 @@ protected function setUp() { ))->save(); // Create the test field. - entity_create('field_config', array( + FieldConfig::create([ 'entity_type' => 'entity_test_mulrev', 'field_name' => 'field_test_entity_reference', 'bundle' => 'entity_test_mulrev', - ))->save(); + ])->save(); } /** diff --git a/core/modules/serialization/src/Tests/EntitySerializationTest.php b/core/modules/serialization/src/Tests/EntitySerializationTest.php index d9be353..abcac01 100644 --- a/core/modules/serialization/src/Tests/EntitySerializationTest.php +++ b/core/modules/serialization/src/Tests/EntitySerializationTest.php @@ -116,6 +116,8 @@ public function testNormalize() { 'user_id' => array( array( 'target_id' => $this->user->id(), + 'target_type' => $this->user->getEntityTypeId(), + 'target_uuid' => $this->user->uuid(), 'url' => $this->user->url(), ), ), @@ -190,7 +192,7 @@ public function testSerialize() { 'name' => '' . $this->values['name'] . '', 'type' => 'entity_test_mulrev', 'created' => '' . $this->entity->created->value . '', - 'user_id' => '' . $this->user->id() . '' . $this->user->url() . '', + 'user_id' => '' . $this->user->id() . '' . $this->user->getEntityTypeId() . '' . $this->user->uuid() . '' . $this->user->url() . '', 'revision_id' => '' . $this->entity->getRevisionId() . '', 'default_langcode' => '1', 'field_test_text' => '' . $this->values['field_test_text']['value'] . '' . $this->values['field_test_text']['format'] . '', diff --git a/core/modules/serialization/src/Tests/NormalizerTestBase.php b/core/modules/serialization/src/Tests/NormalizerTestBase.php index 50e3c87..42c4bcc 100644 --- a/core/modules/serialization/src/Tests/NormalizerTestBase.php +++ b/core/modules/serialization/src/Tests/NormalizerTestBase.php @@ -25,13 +25,12 @@ protected function setUp() { $this->installEntitySchema('entity_test_mulrev'); $this->installEntitySchema('user'); - $this->installSchema('system', array('url_alias', 'router')); $this->installConfig(array('field')); \Drupal::service('router.builder')->rebuild(); \Drupal::moduleHandler()->invoke('rest', 'install'); // Auto-create a field for testing. - FieldstorageConfig::create(array( + FieldStorageConfig::create(array( 'entity_type' => 'entity_test_mulrev', 'field_name' => 'field_test_text', 'type' => 'text', diff --git a/core/modules/serialization/tests/modules/entity_serialization_test/entity_serialization_test.module b/core/modules/serialization/tests/modules/entity_serialization_test/entity_serialization_test.module index a75e21c..26bd88c 100644 --- a/core/modules/serialization/tests/modules/entity_serialization_test/entity_serialization_test.module +++ b/core/modules/serialization/tests/modules/entity_serialization_test/entity_serialization_test.module @@ -18,6 +18,6 @@ function entity_serialization_test_entity_field_access_alter(array &$grants, arr // Override default access control from UserAccessControlHandler to allow // access to 'pass' field for the test user. if ($context['field_definition']->getName() == 'pass' && $context['account']->getUsername() == 'serialization_test_user') { - $grants[':default'] = AccessResult::allowed()->inheritCacheability($grants[':default'])->cacheUntilEntityChanges($context['items']->getEntity()); + $grants[':default'] = AccessResult::allowed()->inheritCacheability($grants[':default'])->addCacheableDependency($context['items']->getEntity()); } } diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php index a86b6f7..04cfa16 100644 --- a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php +++ b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php @@ -78,6 +78,12 @@ public function testNormalize() { $entity->url('canonical') ->willReturn($test_url) ->shouldBeCalled(); + $entity->uuid() + ->willReturn('080e3add-f9d5-41ac-9821-eea55b7b42fb') + ->shouldBeCalled(); + $entity->getEntityTypeId() + ->willReturn('test_type') + ->shouldBeCalled(); $entity_reference = $this->prophesize(TypedDataInterface::class); $entity_reference->getValue() @@ -92,6 +98,8 @@ public function testNormalize() { $expected = [ 'target_id' => ['value' => 'test'], + 'target_type' => 'test_type', + 'target_uuid' => '080e3add-f9d5-41ac-9821-eea55b7b42fb', 'url' => $test_url, ]; $this->assertSame($expected, $normalized); diff --git a/core/modules/shortcut/shortcut.install b/core/modules/shortcut/shortcut.install index 73986b8..8df408f 100644 --- a/core/modules/shortcut/shortcut.install +++ b/core/modules/shortcut/shortcut.install @@ -64,6 +64,6 @@ function shortcut_uninstall() { // Theme settings are not configuration entities and cannot depend on modules // so to unset a module-specific setting, we need to unset it with logic. if (\Drupal::service('theme_handler')->themeExists('seven')) { - \Drupal::configFactory()->getEditable('seven.settings')->clear('third_party_settings.shortcut.module_link')->save(TRUE); + \Drupal::configFactory()->getEditable('seven.settings')->clear('third_party_settings.shortcut')->save(TRUE); } } diff --git a/core/modules/shortcut/src/Plugin/migrate/destination/ShortcutSetUsers.php b/core/modules/shortcut/src/Plugin/migrate/destination/ShortcutSetUsers.php index 557fea3..8d57b42 100644 --- a/core/modules/shortcut/src/Plugin/migrate/destination/ShortcutSetUsers.php +++ b/core/modules/shortcut/src/Plugin/migrate/destination/ShortcutSetUsers.php @@ -9,7 +9,7 @@ use Drupal\shortcut\ShortcutSetStorageInterface; use Drupal\user\Entity\User; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; use Drupal\migrate\Plugin\migrate\destination\DestinationBase; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -38,7 +38,7 @@ class ShortcutSetUsers extends DestinationBase implements ContainerFactoryPlugin * The plugin_id for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. - * @param MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration. * @param \Drupal\shortcut\ShortcutSetStorageInterface $shortcut_set_storage * The shortcut_set entity storage handler. diff --git a/core/modules/shortcut/src/ShortcutAccessControlHandler.php b/core/modules/shortcut/src/ShortcutAccessControlHandler.php index f760937..cbfae00 100644 --- a/core/modules/shortcut/src/ShortcutAccessControlHandler.php +++ b/core/modules/shortcut/src/ShortcutAccessControlHandler.php @@ -61,7 +61,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter } // @todo Fix this bizarre code: how can a shortcut exist without a shortcut // set? The above if-test is unnecessary. See https://www.drupal.org/node/2339903. - return AccessResult::neutral()->cacheUntilEntityChanges($entity); + return AccessResult::neutral()->addCacheableDependency($entity); } /** diff --git a/core/modules/shortcut/src/ShortcutSetAccessControlHandler.php b/core/modules/shortcut/src/ShortcutSetAccessControlHandler.php index 3e9edc7..b859ff6 100644 --- a/core/modules/shortcut/src/ShortcutSetAccessControlHandler.php +++ b/core/modules/shortcut/src/ShortcutSetAccessControlHandler.php @@ -31,7 +31,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter if (!$account->hasPermission('access shortcuts')) { return AccessResult::neutral()->cachePerPermissions(); } - return AccessResult::allowedIf($account->hasPermission('customize shortcut links') && $entity == shortcut_current_displayed_set($account))->cachePerPermissions()->cacheUntilEntityChanges($entity); + return AccessResult::allowedIf($account->hasPermission('customize shortcut links') && $entity == shortcut_current_displayed_set($account))->cachePerPermissions()->addCacheableDependency($entity); case 'delete': return AccessResult::allowedIf($account->hasPermission('administer shortcuts') && $entity->id() != 'default')->cachePerPermissions(); diff --git a/core/modules/shortcut/src/Tests/Migrate/MigrateShortcutStubTest.php b/core/modules/shortcut/src/Tests/Migrate/MigrateShortcutStubTest.php deleted file mode 100644 index ba9a000..0000000 --- a/core/modules/shortcut/src/Tests/Migrate/MigrateShortcutStubTest.php +++ /dev/null @@ -1,44 +0,0 @@ -installEntitySchema('shortcut'); - // Make sure the 'default' shortcut_set is installed. - $this->installConfig(['shortcut']); - } - - /** - * Tests creation of shortcut stubs. - */ - public function testStub() { - $this->performStubTest('shortcut'); - } - -} diff --git a/core/modules/shortcut/src/Tests/Migrate/d7/MigrateShortcutSetTest.php b/core/modules/shortcut/src/Tests/Migrate/d7/MigrateShortcutSetTest.php deleted file mode 100644 index ff2cd8f..0000000 --- a/core/modules/shortcut/src/Tests/Migrate/d7/MigrateShortcutSetTest.php +++ /dev/null @@ -1,78 +0,0 @@ -installSchema('system', array('router')); - $this->installEntitySchema('shortcut'); - $this->installEntitySchema('menu_link_content'); - \Drupal::service('router.builder')->rebuild(); - $this->executeMigration('d7_shortcut_set'); - $this->executeMigration('menu'); - $this->executeMigration('menu_links'); - $this->executeMigration('d7_shortcut'); - } - - /** - * Test the shortcut set migration. - */ - public function testShortcutSetMigration() { - $this->assertEntity('default', 'Default', 2); - $this->assertEntity('shortcut_set_2', 'Alternative shortcut set', 2); - } - - /** - * Asserts various aspects of a shortcut set entity. - * - * @param string $id - * The expected shortcut set ID. - * @param string $label - * The expected shortcut set label. - * @param int $expected_size - * The number of shortcuts expected to be in the set. - */ - protected function assertEntity($id, $label, $expected_size) { - $shortcut_set = ShortcutSet::load($id); - $this->assertTrue($shortcut_set instanceof ShortcutSetInterface); - /** @var \Drupal\shortcut\ShortcutSetInterface $shortcut_set */ - $this->assertIdentical($id, $shortcut_set->id()); - $this->assertIdentical($label, $shortcut_set->label()); - - // Check the number of shortcuts in the set. - $shortcuts = $shortcut_set->getShortcuts(); - $this->assertIdentical(count($shortcuts), $expected_size); - } - -} diff --git a/core/modules/shortcut/src/Tests/Migrate/d7/MigrateShortcutSetUsersTest.php b/core/modules/shortcut/src/Tests/Migrate/d7/MigrateShortcutSetUsersTest.php deleted file mode 100644 index ed63ac0..0000000 --- a/core/modules/shortcut/src/Tests/Migrate/d7/MigrateShortcutSetUsersTest.php +++ /dev/null @@ -1,62 +0,0 @@ -installSchema('system', array('router')); - $this->installEntitySchema('shortcut'); - $this->installEntitySchema('menu_link_content'); - $this->installSchema('shortcut', ['shortcut_set_users']); - \Drupal::service('router.builder')->rebuild(); - $this->executeMigration('d7_user_role'); - $this->executeMigration('d7_user'); - $this->executeMigration('d7_shortcut_set'); - $this->executeMigration('menu'); - $this->executeMigration('menu_links'); - $this->executeMigration('d7_shortcut'); - $this->executeMigration('d7_shortcut_set_users'); - } - - /** - * Test the shortcut set migration. - */ - public function testShortcutSetUsersMigration() { - // Check if migrated user has correct migrated shortcut set assigned. - $account = User::load(2); - $shortcut_set = shortcut_current_displayed_set($account); - /** @var \Drupal\shortcut\ShortcutSetInterface $shortcut_set */ - $this->assertIdentical('shortcut_set_2', $shortcut_set->id()); - } - -} diff --git a/core/modules/shortcut/src/Tests/Migrate/d7/MigrateShortcutTest.php b/core/modules/shortcut/src/Tests/Migrate/d7/MigrateShortcutTest.php deleted file mode 100644 index 0d333fe..0000000 --- a/core/modules/shortcut/src/Tests/Migrate/d7/MigrateShortcutTest.php +++ /dev/null @@ -1,80 +0,0 @@ -installSchema('system', array('router')); - $this->installEntitySchema('shortcut'); - $this->installEntitySchema('menu_link_content'); - \Drupal::service('router.builder')->rebuild(); - $this->executeMigration('d7_shortcut_set'); - $this->executeMigration('menu'); - $this->executeMigration('menu_links'); - $this->executeMigration('d7_shortcut'); - } - - /** - * Asserts various aspects of a shortcut entity. - * - * @param int $id - * The shortcut ID. - * @param string $title - * The expected title of the shortcut. - * @param int $weight - * The expected weight of the shortcut. - * @param string $url - * The expected URL of the shortcut. - */ - protected function assertEntity($id, $title, $weight, $url) { - $shortcut = Shortcut::load($id); - $this->assertTrue($shortcut instanceof ShortcutInterface); - /** @var \Drupal\shortcut\ShortcutInterface $shortcut */ - $this->assertIdentical($title, $shortcut->getTitle()); - $this->assertIdentical($weight, $shortcut->getWeight()); - $this->assertIdentical($url, $shortcut->getUrl()->toString()); - } - - /** - * Test the shortcut migration. - */ - public function testShortcutMigration() { - // Check if the 4 shortcuts were migrated correctly. - $this->assertEntity(1, 'Add content', '-20', '/node/add'); - $this->assertEntity(2, 'Find content', '-19', '/admin/content'); - $this->assertEntity(3, 'Help', '-49', '/admin/help'); - $this->assertEntity(4, 'People', '-50', '/admin/people'); - } - -} diff --git a/core/modules/shortcut/tests/src/Kernel/Migrate/MigrateShortcutStubTest.php b/core/modules/shortcut/tests/src/Kernel/Migrate/MigrateShortcutStubTest.php new file mode 100644 index 0000000..526a273 --- /dev/null +++ b/core/modules/shortcut/tests/src/Kernel/Migrate/MigrateShortcutStubTest.php @@ -0,0 +1,44 @@ +installEntitySchema('shortcut'); + // Make sure the 'default' shortcut_set is installed. + $this->installConfig(['shortcut']); + } + + /** + * Tests creation of shortcut stubs. + */ + public function testStub() { + $this->performStubTest('shortcut'); + } + +} diff --git a/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetTest.php b/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetTest.php new file mode 100644 index 0000000..0e0510b --- /dev/null +++ b/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetTest.php @@ -0,0 +1,77 @@ +installEntitySchema('shortcut'); + $this->installEntitySchema('menu_link_content'); + \Drupal::service('router.builder')->rebuild(); + $this->executeMigration('d7_shortcut_set'); + $this->executeMigration('menu'); + $this->executeMigration('menu_links'); + $this->executeMigration('d7_shortcut'); + } + + /** + * Test the shortcut set migration. + */ + public function testShortcutSetMigration() { + $this->assertEntity('default', 'Default', 2); + $this->assertEntity('shortcut_set_2', 'Alternative shortcut set', 2); + } + + /** + * Asserts various aspects of a shortcut set entity. + * + * @param string $id + * The expected shortcut set ID. + * @param string $label + * The expected shortcut set label. + * @param int $expected_size + * The number of shortcuts expected to be in the set. + */ + protected function assertEntity($id, $label, $expected_size) { + $shortcut_set = ShortcutSet::load($id); + $this->assertTrue($shortcut_set instanceof ShortcutSetInterface); + /** @var \Drupal\shortcut\ShortcutSetInterface $shortcut_set */ + $this->assertIdentical($id, $shortcut_set->id()); + $this->assertIdentical($label, $shortcut_set->label()); + + // Check the number of shortcuts in the set. + $shortcuts = $shortcut_set->getShortcuts(); + $this->assertIdentical(count($shortcuts), $expected_size); + } + +} diff --git a/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetUsersTest.php b/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetUsersTest.php new file mode 100644 index 0000000..76f4944 --- /dev/null +++ b/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutSetUsersTest.php @@ -0,0 +1,61 @@ +installEntitySchema('shortcut'); + $this->installEntitySchema('menu_link_content'); + $this->installSchema('shortcut', ['shortcut_set_users']); + \Drupal::service('router.builder')->rebuild(); + $this->executeMigration('d7_user_role'); + $this->executeMigration('d7_user'); + $this->executeMigration('d7_shortcut_set'); + $this->executeMigration('menu'); + $this->executeMigration('menu_links'); + $this->executeMigration('d7_shortcut'); + $this->executeMigration('d7_shortcut_set_users'); + } + + /** + * Test the shortcut set migration. + */ + public function testShortcutSetUsersMigration() { + // Check if migrated user has correct migrated shortcut set assigned. + $account = User::load(2); + $shortcut_set = shortcut_current_displayed_set($account); + /** @var \Drupal\shortcut\ShortcutSetInterface $shortcut_set */ + $this->assertIdentical('shortcut_set_2', $shortcut_set->id()); + } + +} diff --git a/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutTest.php b/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutTest.php new file mode 100644 index 0000000..45bb9d1 --- /dev/null +++ b/core/modules/shortcut/tests/src/Kernel/Migrate/d7/MigrateShortcutTest.php @@ -0,0 +1,79 @@ +installEntitySchema('shortcut'); + $this->installEntitySchema('menu_link_content'); + \Drupal::service('router.builder')->rebuild(); + $this->executeMigration('d7_shortcut_set'); + $this->executeMigration('menu'); + $this->executeMigration('menu_links'); + $this->executeMigration('d7_shortcut'); + } + + /** + * Asserts various aspects of a shortcut entity. + * + * @param int $id + * The shortcut ID. + * @param string $title + * The expected title of the shortcut. + * @param int $weight + * The expected weight of the shortcut. + * @param string $url + * The expected URL of the shortcut. + */ + protected function assertEntity($id, $title, $weight, $url) { + $shortcut = Shortcut::load($id); + $this->assertTrue($shortcut instanceof ShortcutInterface); + /** @var \Drupal\shortcut\ShortcutInterface $shortcut */ + $this->assertIdentical($title, $shortcut->getTitle()); + $this->assertIdentical($weight, $shortcut->getWeight()); + $this->assertIdentical($url, $shortcut->getUrl()->toString()); + } + + /** + * Test the shortcut migration. + */ + public function testShortcutMigration() { + // Check if the 4 shortcuts were migrated correctly. + $this->assertEntity(1, 'Add content', '-20', '/node/add'); + $this->assertEntity(2, 'Find content', '-19', '/admin/content'); + $this->assertEntity(3, 'Help', '-49', '/admin/help'); + $this->assertEntity(4, 'People', '-50', '/admin/people'); + } + +} diff --git a/core/modules/shortcut/tests/src/Kernel/ShortcutSevenIntegrationTest.php b/core/modules/shortcut/tests/src/Kernel/ShortcutSevenIntegrationTest.php new file mode 100644 index 0000000..f027427 --- /dev/null +++ b/core/modules/shortcut/tests/src/Kernel/ShortcutSevenIntegrationTest.php @@ -0,0 +1,36 @@ +install(['seven']); + $this->assertNull($this->config('seven.settings')->get('third_party_settings.shortcut'), 'There are no shortcut settings in seven.settings.'); + + \Drupal::service('module_installer')->install(['shortcut']); + $this->assertTrue($this->config('seven.settings')->get('third_party_settings.shortcut.module_link'), 'The shortcut module_link setting is in seven.settings.'); + + \Drupal::service('module_installer')->uninstall(['shortcut']); + $this->assertNull($this->config('seven.settings')->get('third_party_settings.shortcut'), 'There are no shortcut settings in seven.settings.'); + } + +} diff --git a/core/modules/simpletest/files/php-1.txt b/core/modules/simpletest/files/php-1.txt index 52788b6..c641a69 100644 --- a/core/modules/simpletest/files/php-1.txt +++ b/core/modules/simpletest/files/php-1.txt @@ -1,3 +1,4 @@ diff --git a/core/modules/simpletest/files/php-2.php b/core/modules/simpletest/files/php-2.php index 615a8d7..29738aa 100644 --- a/core/modules/simpletest/files/php-2.php +++ b/core/modules/simpletest/files/php-2.php @@ -1,2 +1,3 @@ cURL library is not available.', array(':curl_url' => 'http://php.net/manual/curl.setup.php')); - } - - $requirements['php_domdocument'] = array( - 'title' => t('PHP DOMDocument class'), - 'value' => $has_domdocument ? t('Enabled') : t('Not found'), - ); - if (!$has_domdocument) { - $requirements['php_domdocument']['severity'] = REQUIREMENT_ERROR; - $requirements['php_domdocument']['description'] = t('The testing framework requires the DOMDocument class to be available. Check the configure command at the PHP info page.', array(':link-phpinfo' => \Drupal::url('system.php'))); + $requirements['curl']['description'] = t('The testing framework could not be installed because the PHP cURL library is not available.'); } // SimpleTest currently needs 2 cURL options which are incompatible with @@ -49,7 +39,7 @@ function simpletest_requirements($phase) { ); if ($open_basedir) { $requirements['php_open_basedir']['severity'] = REQUIREMENT_ERROR; - $requirements['php_open_basedir']['description'] = t('The testing framework requires the PHP open_basedir restriction to be disabled. Check your webserver configuration or contact your web host.', array(':open_basedir-url' => 'http://php.net/manual/ini.core.php#ini.open-basedir')); + $requirements['php_open_basedir']['description'] = t('The testing framework requires the PHP open_basedir restriction to be disabled. Check your webserver configuration or contact your web host.'); } // Check the current memory limit. If it is set too low, SimpleTest will fail diff --git a/core/modules/simpletest/simpletest.js b/core/modules/simpletest/simpletest.js index dfe5fba..ba54cbf 100644 --- a/core/modules/simpletest/simpletest.js +++ b/core/modules/simpletest/simpletest.js @@ -3,7 +3,7 @@ * Simpletest behaviors. */ -(function ($) { +(function ($, Drupal, drupalSettings) { 'use strict'; @@ -127,4 +127,4 @@ } }; -})(jQuery); +})(jQuery, Drupal, drupalSettings); diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index f5b85a5..4dc7544 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -1,5 +1,10 @@ save(); $this->assertNotNull($account->id(), SafeMarkup::format('User created with name %name and pass %pass', array('%name' => $edit['name'], '%pass' => $edit['pass']))); @@ -620,7 +621,7 @@ protected function drupalCreateRole(array $permissions, $rid = NULL, $name = NUL // Create new role. /* @var \Drupal\user\RoleInterface $role */ - $role = entity_create('user_role', array( + $role = Role::create(array( 'id' => $rid, 'label' => $name, )); diff --git a/core/modules/simpletest/src/InstallerTestBase.php b/core/modules/simpletest/src/InstallerTestBase.php index 8115840..adc5463 100644 --- a/core/modules/simpletest/src/InstallerTestBase.php +++ b/core/modules/simpletest/src/InstallerTestBase.php @@ -118,7 +118,7 @@ protected function setUp() { ->set('app.root', DRUPAL_ROOT); \Drupal::setContainer($this->container); - $this->drupalGet($GLOBALS['base_url'] . '/core/install.php'); + $this->visitInstaller(); // Select language. $this->setUpLanguage(); @@ -165,6 +165,13 @@ protected function setUp() { } /** + * Visits the interactive installer. + */ + protected function visitInstaller() { + $this->drupalGet($GLOBALS['base_url'] . '/core/install.php'); + } + + /** * Installer step: Select language. */ protected function setUpLanguage() { diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 3460e73..75c9955 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -435,10 +435,18 @@ protected function installSchema($module, $tables) { if (!$this->container->get('module_handler')->moduleExists($module)) { throw new \RuntimeException("'$module' module is not enabled"); } + $tables = (array) $tables; foreach ($tables as $table) { $schema = drupal_get_module_schema($module, $table); if (empty($schema)) { + // BC layer to avoid some contrib tests to fail. + // @todo Remove the BC layer before 8.1.x release. + // @see https://www.drupal.org/node/2670360 + // @see https://www.drupal.org/node/2670454 + if ($module == 'system') { + continue; + } throw new \RuntimeException("Unknown '$table' table schema in '$module' module."); } $this->container->get('database')->schema()->createTable($table, $schema); @@ -602,9 +610,7 @@ protected function registerStreamWrapper($scheme, $class, $type = StreamWrapperI protected function render(array &$elements) { // Use the bare HTML page renderer to render our links. $renderer = $this->container->get('bare_html_page_renderer'); - $response = $renderer->renderBarePage( - $elements, '', $this->container->get('theme.manager')->getActiveTheme()->getName() - ); + $response = $renderer->renderBarePage($elements, '', 'maintenance_page'); // Glean the content from the response object. $content = $response->getContent(); diff --git a/core/modules/simpletest/src/RouteProvider.php b/core/modules/simpletest/src/RouteProvider.php index acff99a..0784d72 100644 --- a/core/modules/simpletest/src/RouteProvider.php +++ b/core/modules/simpletest/src/RouteProvider.php @@ -13,6 +13,9 @@ /** * Rebuilds the router when the provider is instantiated. + * + * @todo Move this outside of simpletest namespace to the Drupal\Tests, see + * https://www.drupal.org/node/2672762 */ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProviderInterface { diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php index 1cdc231..517afb8 100644 --- a/core/modules/simpletest/src/TestBase.php +++ b/core/modules/simpletest/src/TestBase.php @@ -314,7 +314,7 @@ * HTTP authentication method (specified as a CURLAUTH_* constant). * * @var int - * @see http://php.net/manual/en/function.curl-setopt.php + * @see http://php.net/manual/function.curl-setopt.php */ protected $httpAuthMethod = CURLAUTH_BASIC; diff --git a/core/modules/simpletest/src/TestDiscovery.php b/core/modules/simpletest/src/TestDiscovery.php index 3538934..fab8833 100644 --- a/core/modules/simpletest/src/TestDiscovery.php +++ b/core/modules/simpletest/src/TestDiscovery.php @@ -11,7 +11,6 @@ use Doctrine\Common\Reflection\StaticReflectionParser; use Drupal\Component\Annotation\Reflection\MockFileFinder; use Drupal\Component\Utility\NestedArray; -use Drupal\Component\Utility\Unicode; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ExtensionDiscovery; use Drupal\Core\Extension\ModuleHandlerInterface; diff --git a/core/modules/simpletest/src/TestServiceProvider.php b/core/modules/simpletest/src/TestServiceProvider.php index 5a1a398..ba65c37 100644 --- a/core/modules/simpletest/src/TestServiceProvider.php +++ b/core/modules/simpletest/src/TestServiceProvider.php @@ -33,15 +33,25 @@ function register(ContainerBuilder $container) { */ public function alter(ContainerBuilder $container) { if (static::$currentTest instanceof KernelTestBase) { + static::addRouteProvider($container); + } + } + + /** + * Add the on demand rebuild route provider service. + * + * @param \Drupal\Core\DependencyInjection\ContainerBuilder $container + */ + public static function addRouteProvider(ContainerBuilder $container) { + foreach (['router.route_provider' => 'RouteProvider'] as $original_id => $class) { // While $container->get() does a recursive resolve, getDefinition() does // not, so do it ourselves. - foreach (['router.route_provider' => 'RouteProvider'] as $original_id => $class) { - for ($id = $original_id; $container->hasAlias($id); $id = (string) $container->getAlias($id)); - $definition = $container->getDefinition($id); - $definition->clearTag('needs_destruction'); - $container->setDefinition("simpletest.$original_id", $definition); - $container->setDefinition($id, new Definition('Drupal\simpletest\\' . $class)); - } + for ($id = $original_id; $container->hasAlias($id); $id = (string) $container->getAlias($id)); + $definition = $container->getDefinition($id); + $definition->clearTag('needs_destruction'); + $container->setDefinition("simpletest.$original_id", $definition); + $container->setDefinition($id, new Definition('Drupal\simpletest\\' . $class)); } } + } diff --git a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php index 2187ccc..5bcd5d3 100644 --- a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php +++ b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php @@ -8,7 +8,10 @@ namespace Drupal\simpletest\Tests; use Drupal\Core\Database\Database; +use Drupal\field\Entity\FieldConfig; use Drupal\simpletest\KernelTestBase; +use Drupal\field\Entity\FieldStorageConfig; +use Drupal\Core\Entity\Entity\EntityViewDisplay; /** * Tests KernelTestBase functionality. @@ -272,21 +275,21 @@ function testEnableModulesFixedList() { $this->enableModules(array('field_test')); // Create a field. - entity_create('entity_view_display', array( + $display = EntityViewDisplay::create(array( 'targetEntityType' => 'entity_test', 'bundle' => 'entity_test', 'mode' => 'default', )); - $field_storage = entity_create('field_storage_config', array( + $field_storage = FieldStorageConfig::create(array( 'field_name' => 'test_field', 'entity_type' => 'entity_test', 'type' => 'test_field' )); $field_storage->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_storage' => $field_storage, 'bundle' => 'entity_test', - ))->save(); + ])->save(); } /** diff --git a/core/modules/simpletest/src/Tests/Migrate/d6/MigrateSimpletestConfigsTest.php b/core/modules/simpletest/src/Tests/Migrate/d6/MigrateSimpletestConfigsTest.php deleted file mode 100644 index ad66d9f..0000000 --- a/core/modules/simpletest/src/Tests/Migrate/d6/MigrateSimpletestConfigsTest.php +++ /dev/null @@ -1,51 +0,0 @@ -installConfig(['simpletest']); - $this->executeMigration('d6_simpletest_settings'); - } - - /** - * Tests migration of simpletest variables to simpletest.settings.yml. - */ - public function testSimpletestSettings() { - $config = $this->config('simpletest.settings'); - $this->assertIdentical(TRUE, $config->get('clear_results')); - $this->assertIdentical(CURLAUTH_BASIC, $config->get('httpauth.method')); - // NULL in the dump means defaults which is empty string. Same as omitting - // them. - $this->assertIdentical('', $config->get('httpauth.password')); - $this->assertIdentical('', $config->get('httpauth.username')); - $this->assertIdentical(TRUE, $config->get('verbose')); - $this->assertConfigSchema(\Drupal::service('config.typed'), 'simpletest.settings', $config->get()); - } - -} diff --git a/core/modules/simpletest/src/Tests/Migrate/d7/MigrateSimpletestSettingsTest.php b/core/modules/simpletest/src/Tests/Migrate/d7/MigrateSimpletestSettingsTest.php deleted file mode 100644 index 0528cc1..0000000 --- a/core/modules/simpletest/src/Tests/Migrate/d7/MigrateSimpletestSettingsTest.php +++ /dev/null @@ -1,42 +0,0 @@ -installConfig(static::$modules); - $this->executeMigration('d7_simpletest_settings'); - } - - /** - * Tests migration of SimpleTest settings to configuration. - */ - public function testMigration() { - $config = \Drupal::config('simpletest.settings')->get(); - $this->assertTrue($config['clear_results']); - $this->assertIdentical(CURLAUTH_BASIC, $config['httpauth']['method']); - $this->assertIdentical('testbot', $config['httpauth']['username']); - $this->assertIdentical('foobaz', $config['httpauth']['password']); - $this->assertTrue($config['verbose']); - } - -} diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php index a3bfd4c..e9b6376 100644 --- a/core/modules/simpletest/src/WebTestBase.php +++ b/core/modules/simpletest/src/WebTestBase.php @@ -1179,7 +1179,7 @@ protected function curlInitialize() { * @param $curl_options * An associative array of cURL options to set, where the keys are constants * defined by the cURL library. For a list of valid options, see - * http://www.php.net/manual/function.curl-setopt.php + * http://php.net/manual/function.curl-setopt.php * @param $redirect * FALSE if this is an initial request, TRUE if this request is the result * of a redirect. diff --git a/core/modules/simpletest/tests/fixtures/simpletest_phpunit_run_command_test.php b/core/modules/simpletest/tests/fixtures/simpletest_phpunit_run_command_test.php index c86a9e6..dd47547 100644 --- a/core/modules/simpletest/tests/fixtures/simpletest_phpunit_run_command_test.php +++ b/core/modules/simpletest/tests/fixtures/simpletest_phpunit_run_command_test.php @@ -1,5 +1,10 @@ $id)); + $entity = EntityTest::create(array('id' => $id)); $entity->save(); } diff --git a/core/modules/simpletest/tests/src/Kernel/Migrate/d6/MigrateSimpletestConfigsTest.php b/core/modules/simpletest/tests/src/Kernel/Migrate/d6/MigrateSimpletestConfigsTest.php new file mode 100644 index 0000000..99c66ed --- /dev/null +++ b/core/modules/simpletest/tests/src/Kernel/Migrate/d6/MigrateSimpletestConfigsTest.php @@ -0,0 +1,51 @@ +installConfig(['simpletest']); + $this->executeMigration('d6_simpletest_settings'); + } + + /** + * Tests migration of simpletest variables to simpletest.settings.yml. + */ + public function testSimpletestSettings() { + $config = $this->config('simpletest.settings'); + $this->assertIdentical(TRUE, $config->get('clear_results')); + $this->assertIdentical(CURLAUTH_BASIC, $config->get('httpauth.method')); + // NULL in the dump means defaults which is empty string. Same as omitting + // them. + $this->assertIdentical('', $config->get('httpauth.password')); + $this->assertIdentical('', $config->get('httpauth.username')); + $this->assertIdentical(TRUE, $config->get('verbose')); + $this->assertConfigSchema(\Drupal::service('config.typed'), 'simpletest.settings', $config->get()); + } + +} diff --git a/core/modules/simpletest/tests/src/Kernel/Migrate/d7/MigrateSimpletestSettingsTest.php b/core/modules/simpletest/tests/src/Kernel/Migrate/d7/MigrateSimpletestSettingsTest.php new file mode 100644 index 0000000..ae62ef4 --- /dev/null +++ b/core/modules/simpletest/tests/src/Kernel/Migrate/d7/MigrateSimpletestSettingsTest.php @@ -0,0 +1,42 @@ +installConfig(static::$modules); + $this->executeMigration('d7_simpletest_settings'); + } + + /** + * Tests migration of SimpleTest settings to configuration. + */ + public function testMigration() { + $config = \Drupal::config('simpletest.settings')->get(); + $this->assertTrue($config['clear_results']); + $this->assertIdentical(CURLAUTH_BASIC, $config['httpauth']['method']); + $this->assertIdentical('testbot', $config['httpauth']['username']); + $this->assertIdentical('foobaz', $config['httpauth']['password']); + $this->assertTrue($config['verbose']); + } + +} diff --git a/core/modules/simpletest/tests/src/Unit/TestInfoParsingTest.php b/core/modules/simpletest/tests/src/Unit/TestInfoParsingTest.php index fda12f1..b96ea79 100644 --- a/core/modules/simpletest/tests/src/Unit/TestInfoParsingTest.php +++ b/core/modules/simpletest/tests/src/Unit/TestInfoParsingTest.php @@ -5,7 +5,7 @@ * Contains \Drupal\Tests\simpletest\Unit\TestInfoParsingTest. */ -namespace Drupal\Tests\simpletest\Unit; +namespace Drupal\Tests\simpletest\Unit { use Composer\Autoload\ClassLoader; use Drupal\Core\Extension\Extension; @@ -87,18 +87,18 @@ public function infoParserProvider() { $tests[] = [ // Expected result. [ - 'name' => 'Drupal\field\Tests\BulkDeleteTest', - 'group' => 'field', - 'description' => 'Bulk delete storages and fields, and clean up afterwards.', + 'name' => 'Drupal\simpletest\Tests\ExampleSimpleTest', + 'group' => 'simpletest', + 'description' => 'Tests the Simpletest UI internal browser.', 'type' => 'Simpletest', ], // Classname. - 'Drupal\field\Tests\BulkDeleteTest', + 'Drupal\simpletest\Tests\ExampleSimpleTest', // Doc block. "/** - * Bulk delete storages and fields, and clean up afterwards. + * Tests the Simpletest UI internal browser. * - * @group field + * @group simpletest */ ", ]; @@ -107,18 +107,19 @@ public function infoParserProvider() { $tests[] = [ // Expected result. [ - 'name' => 'Drupal\field\Tests\BulkDeleteTest', - 'group' => 'field', - 'description' => 'Bulk delete storages and fields, and clean up afterwards.', - 'type' => 'Simpletest' + 'name' => 'Drupal\simpletest\Tests\ExampleSimpleTest', + 'group' => 'simpletest', + 'description' => 'Tests the Simpletest UI internal browser.', + 'type' => 'Simpletest', ], // Classname. - 'Drupal\field\Tests\BulkDeleteTest', + 'Drupal\simpletest\Tests\ExampleSimpleTest', // Doc block. "/** - * Bulk delete storages and fields, and clean up afterwards. + * Tests the Simpletest UI internal browser. * - * @group field + * @group simpletest + */ */ ", ]; @@ -128,18 +129,18 @@ public function infoParserProvider() { $tests[] = [ // Expected result. [ - 'name' => 'Drupal\field\Tests\BulkDeleteTest', - 'group' => 'field', - 'description' => 'Bulk delete storages and fields, and clean up afterwards. * @', - 'type' => 'Simpletest' + 'name' => 'Drupal\simpletest\Tests\ExampleSimpleTest', + 'group' => 'simpletest', + 'description' => 'Tests the Simpletest UI internal browser. * @', + 'type' => 'Simpletest', ], // Classname. - 'Drupal\field\Tests\BulkDeleteTest', + 'Drupal\simpletest\Tests\ExampleSimpleTest', // Doc block. "/** - * Bulk delete storages and fields, and clean up afterwards. * @ + * Tests the Simpletest UI internal browser. * @ * - * @group field + * @group simpletest */ ", ]; @@ -148,19 +149,19 @@ public function infoParserProvider() { $tests[] = [ // Expected result. [ - 'name' => 'Drupal\field\Tests\BulkDeleteTest', + 'name' => 'Drupal\simpletest\Tests\ExampleSimpleTest', 'group' => 'Test', - 'description' => 'Bulk delete storages and fields, and clean up afterwards.', - 'type' => 'Simpletest' + 'description' => 'Tests the Simpletest UI internal browser.', + 'type' => 'Simpletest', ], // Classname. - 'Drupal\field\Tests\BulkDeleteTest', + 'Drupal\simpletest\Tests\ExampleSimpleTest', // Doc block. "/** - * Bulk delete storages and fields, and clean up afterwards. + * Tests the Simpletest UI internal browser. * * @group Test - * @group field + * @group simpletest */ ", ]; @@ -169,20 +170,20 @@ public function infoParserProvider() { $tests[] = [ // Expected result. [ - 'name' => 'Drupal\field\Tests\BulkDeleteTest', - 'group' => 'field', - 'description' => 'Bulk delete storages and fields, and clean up afterwards.', + 'name' => 'Drupal\simpletest\Tests\ExampleSimpleTest', + 'description' => 'Tests the Simpletest UI internal browser.', + 'type' => 'Simpletest', 'requires' => ['module' => ['test']], - 'type' => 'Simpletest' + 'group' => 'simpletest', ], // Classname. - 'Drupal\field\Tests\BulkDeleteTest', + 'Drupal\simpletest\Tests\ExampleSimpleTest', // Doc block. "/** - * Bulk delete storages and fields, and clean up afterwards. + * Tests the Simpletest UI internal browser. * * @dependencies test - * @group field + * @group simpletest */ ", ]; @@ -191,20 +192,20 @@ public function infoParserProvider() { $tests[] = [ // Expected result. [ - 'name' => 'Drupal\field\Tests\BulkDeleteTest', - 'group' => 'field', - 'description' => 'Bulk delete storages and fields, and clean up afterwards.', + 'name' => 'Drupal\simpletest\Tests\ExampleSimpleTest', + 'description' => 'Tests the Simpletest UI internal browser.', + 'type' => 'Simpletest', 'requires' => ['module' => ['test', 'test1', 'test2']], - 'type' => 'Simpletest' + 'group' => 'simpletest', ], // Classname. - 'Drupal\field\Tests\BulkDeleteTest', + 'Drupal\simpletest\Tests\ExampleSimpleTest', // Doc block. "/** - * Bulk delete storages and fields, and clean up afterwards. + * Tests the Simpletest UI internal browser. * - * @dependencies test, test1,test2 - * @group field + * @dependencies test, test1, test2 + * @group simpletest */ ", ]; @@ -213,18 +214,19 @@ public function infoParserProvider() { $tests[] = [ // Expected result. [ - 'name' => 'Drupal\field\Tests\BulkDeleteTest', - 'group' => 'field', - 'description' => 'Bulk delete storages and fields, and clean up afterwards. And the summary line continues and there is no gap to the annotation.', - 'type' => 'Simpletest' + 'name' => 'Drupal\simpletest\Tests\ExampleSimpleTest', + 'description' => 'Tests the Simpletest UI internal browser. And the summary line continues an there is no gap to the annotation.', + 'type' => 'Simpletest', + 'group' => 'simpletest', ], // Classname. - 'Drupal\field\Tests\BulkDeleteTest', + 'Drupal\simpletest\Tests\ExampleSimpleTest', // Doc block. "/** - * Bulk delete storages and fields, and clean up afterwards. And the summary - * line continues and there is no gap to the annotation. - * @group field + * Tests the Simpletest UI internal browser. And the summary line continues an + * there is no gap to the annotation. + * + * @group simpletest */ ", ]; @@ -234,10 +236,10 @@ public function infoParserProvider() { /** * @covers ::getTestInfo * @expectedException \Drupal\simpletest\Exception\MissingGroupException - * @expectedExceptionMessage Missing @group annotation in Drupal\field\Tests\BulkDeleteTest + * @expectedExceptionMessage Missing @group annotation in Drupal\KernelTests\field\BulkDeleteTest */ public function testTestInfoParserMissingGroup() { - $classname = 'Drupal\field\Tests\BulkDeleteTest'; + $classname = 'Drupal\KernelTests\field\BulkDeleteTest'; $doc_comment = <<executeMigration('d6_statistics_settings'); - } - - /** - * Tests migration of statistics variables to statistics.settings.yml. - */ - public function testStatisticsSettings() { - $config = $this->config('statistics.settings'); - $this->assertIdentical(FALSE, $config->get('access_log.enabled')); - $this->assertIdentical(259200, $config->get('access_log.max_lifetime')); - $this->assertIdentical(0, $config->get('count_content_views')); - $this->assertConfigSchema(\Drupal::service('config.typed'), 'statistics.settings', $config->get()); - } - -} diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module index 61d58e2..5079e43 100644 --- a/core/modules/statistics/statistics.module +++ b/core/modules/statistics/statistics.module @@ -48,20 +48,20 @@ function statistics_node_view(array &$build, EntityInterface $node, EntityViewDi /** * Implements hook_node_links_alter(). */ -function statistics_node_links_alter(array &$node_links, NodeInterface $entity, array &$context) { +function statistics_node_links_alter(array &$links, NodeInterface $entity, array &$context) { if ($context['view_mode'] != 'rss') { - $node_links['#cache']['contexts'][] = 'user.permissions'; + $links['#cache']['contexts'][] = 'user.permissions'; if (\Drupal::currentUser()->hasPermission('view post access counter')) { $statistics = statistics_get($entity->id()); if ($statistics) { - $links['statistics_counter']['title'] = \Drupal::translation()->formatPlural($statistics['totalcount'], '1 view', '@count views'); - $node_links['statistics'] = array( + $statistics_links['statistics_counter']['title'] = \Drupal::translation()->formatPlural($statistics['totalcount'], '1 view', '@count views'); + $links['statistics'] = array( '#theme' => 'links__node__statistics', - '#links' => $links, + '#links' => $statistics_links, '#attributes' => array('class' => array('links', 'inline')), ); } - $node_links['#cache']['max-age'] = \Drupal::config('statistics.settings')->get('display_max_age'); + $links['#cache']['max-age'] = \Drupal::config('statistics.settings')->get('display_max_age'); } } } diff --git a/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateStatisticsConfigsTest.php b/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateStatisticsConfigsTest.php new file mode 100644 index 0000000..dd90898 --- /dev/null +++ b/core/modules/statistics/tests/src/Kernel/Migrate/d6/MigrateStatisticsConfigsTest.php @@ -0,0 +1,46 @@ +executeMigration('d6_statistics_settings'); + } + + /** + * Tests migration of statistics variables to statistics.settings.yml. + */ + public function testStatisticsSettings() { + $config = $this->config('statistics.settings'); + $this->assertIdentical(FALSE, $config->get('access_log.enabled')); + $this->assertIdentical(259200, $config->get('access_log.max_lifetime')); + $this->assertIdentical(0, $config->get('count_content_views')); + $this->assertConfigSchema(\Drupal::service('config.typed'), 'statistics.settings', $config->get()); + } + +} diff --git a/core/modules/syslog/src/Tests/Migrate/d6/MigrateSyslogConfigsTest.php b/core/modules/syslog/src/Tests/Migrate/d6/MigrateSyslogConfigsTest.php deleted file mode 100644 index 686bce0..0000000 --- a/core/modules/syslog/src/Tests/Migrate/d6/MigrateSyslogConfigsTest.php +++ /dev/null @@ -1,45 +0,0 @@ -executeMigration('d6_syslog_settings'); - } - - /** - * Tests migration of syslog variables to syslog.settings.yml. - */ - public function testSyslogSettings() { - $config = $this->config('syslog.settings'); - $this->assertIdentical('drupal', $config->get('identity')); - $this->assertIdentical('128', $config->get('facility')); - $this->assertConfigSchema(\Drupal::service('config.typed'), 'syslog.settings', $config->get()); - } - -} diff --git a/core/modules/syslog/src/Tests/Migrate/d7/MigrateSyslogConfigsTest.php b/core/modules/syslog/src/Tests/Migrate/d7/MigrateSyslogConfigsTest.php deleted file mode 100644 index f7ae205..0000000 --- a/core/modules/syslog/src/Tests/Migrate/d7/MigrateSyslogConfigsTest.php +++ /dev/null @@ -1,49 +0,0 @@ -installConfig(static::$modules); - $this->executeMigration('d7_syslog_settings'); - } - - /** - * Tests migration of syslog variables to syslog.settings.yml. - */ - public function testSyslogSettings() { - $config = $this->config('syslog.settings'); - // 8 == LOG_USER - $this->assertIdentical('8', $config->get('facility')); - $this->assertIdentical('!base_url|!timestamp|!type|!ip|!request_uri|!referer|!uid|!link|!message', $config->get('format')); - $this->assertIdentical('drupal', $config->get('identity')); - } - -} diff --git a/core/modules/syslog/syslog.module b/core/modules/syslog/syslog.module index e74d993..68fe614 100644 --- a/core/modules/syslog/syslog.module +++ b/core/modules/syslog/syslog.module @@ -17,7 +17,7 @@ function syslog_help($route_name, RouteMatchInterface $route_match) { case 'help.page.syslog': $output = ''; $output .= '

                          ' . t('About') . '

                          '; - $output .= '

                          ' . t('The Syslog module logs events by sending messages to the logging facility of your web server\'s operating system. Syslog is an operating system administrative logging tool that provides valuable information for use in system management and security auditing. Most suited to medium and large sites, Syslog provides filtering tools that allow messages to be routed by type and severity. For more information, see the online documentation for the Syslog module, as well as PHP\'s documentation pages for the openlog and syslog functions.', array(':syslog' => 'https://www.drupal.org/documentation/modules/syslog', ':php_openlog' => 'http://www.php.net/manual/function.openlog.php', ':php_syslog' => 'http://www.php.net/manual/function.syslog.php')) . '

                          '; + $output .= '

                          ' . t('The Syslog module logs events by sending messages to the logging facility of your web server\'s operating system. Syslog is an operating system administrative logging tool that provides valuable information for use in system management and security auditing. Most suited to medium and large sites, Syslog provides filtering tools that allow messages to be routed by type and severity. For more information, see the online documentation for the Syslog module, as well as PHP\'s documentation pages for the openlog and syslog functions.', array(':syslog' => 'https://www.drupal.org/documentation/modules/syslog')) . '

                          '; $output .= '

                          ' . t('Uses') . '

                          '; $output .= '
                          '; $output .= '
                          ' . t('Logging for UNIX, Linux, and Mac OS X') . '
                          '; diff --git a/core/modules/syslog/tests/src/Kernel/Migrate/d6/MigrateSyslogConfigsTest.php b/core/modules/syslog/tests/src/Kernel/Migrate/d6/MigrateSyslogConfigsTest.php new file mode 100644 index 0000000..25a145d --- /dev/null +++ b/core/modules/syslog/tests/src/Kernel/Migrate/d6/MigrateSyslogConfigsTest.php @@ -0,0 +1,45 @@ +executeMigration('d6_syslog_settings'); + } + + /** + * Tests migration of syslog variables to syslog.settings.yml. + */ + public function testSyslogSettings() { + $config = $this->config('syslog.settings'); + $this->assertIdentical('drupal', $config->get('identity')); + $this->assertIdentical('128', $config->get('facility')); + $this->assertConfigSchema(\Drupal::service('config.typed'), 'syslog.settings', $config->get()); + } + +} diff --git a/core/modules/syslog/tests/src/Kernel/Migrate/d7/MigrateSyslogConfigsTest.php b/core/modules/syslog/tests/src/Kernel/Migrate/d7/MigrateSyslogConfigsTest.php new file mode 100644 index 0000000..f4830cd --- /dev/null +++ b/core/modules/syslog/tests/src/Kernel/Migrate/d7/MigrateSyslogConfigsTest.php @@ -0,0 +1,49 @@ +installConfig(static::$modules); + $this->executeMigration('d7_syslog_settings'); + } + + /** + * Tests migration of syslog variables to syslog.settings.yml. + */ + public function testSyslogSettings() { + $config = $this->config('syslog.settings'); + // 8 == LOG_USER + $this->assertIdentical('8', $config->get('facility')); + $this->assertIdentical('!base_url|!timestamp|!type|!ip|!request_uri|!referer|!uid|!link|!message', $config->get('format')); + $this->assertIdentical('drupal', $config->get('identity')); + } + +} diff --git a/core/modules/system/src/Controller/ThemeController.php b/core/modules/system/src/Controller/ThemeController.php index 7d64b5f..b6ac1dc 100644 --- a/core/modules/system/src/Controller/ThemeController.php +++ b/core/modules/system/src/Controller/ThemeController.php @@ -12,7 +12,6 @@ use Drupal\Core\Config\UnmetDependenciesException; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Extension\ThemeHandlerInterface; -use Drupal\Core\Routing\RouteBuilderInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; diff --git a/core/modules/system/src/DateFormatAccessControlHandler.php b/core/modules/system/src/DateFormatAccessControlHandler.php index e817f70..daea873 100644 --- a/core/modules/system/src/DateFormatAccessControlHandler.php +++ b/core/modules/system/src/DateFormatAccessControlHandler.php @@ -30,10 +30,10 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter // Locked date formats cannot be updated or deleted. elseif (in_array($operation, array('update', 'delete'))) { if ($entity->isLocked()) { - return AccessResult::forbidden()->cacheUntilEntityChanges($entity); + return AccessResult::forbidden()->addCacheableDependency($entity); } else { - return parent::checkAccess($entity, $operation, $account)->cacheUntilEntityChanges($entity); + return parent::checkAccess($entity, $operation, $account)->addCacheableDependency($entity); } } diff --git a/core/modules/system/src/Form/CronForm.php b/core/modules/system/src/Form/CronForm.php index 81e44c4..ca50f4c 100644 --- a/core/modules/system/src/Form/CronForm.php +++ b/core/modules/system/src/Form/CronForm.php @@ -15,7 +15,6 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\State\StateInterface; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpFoundation\RedirectResponse; /** * Configure cron settings for this site. @@ -127,7 +126,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { public function submitForm(array &$form, FormStateInterface $form_state) { // Run cron manually from Cron form. if ($this->cron->run()) { - drupal_set_message(t('Cron run successfully.')); + drupal_set_message(t('Cron ran successfully.')); } else { drupal_set_message(t('Cron run failed.'), 'error'); diff --git a/core/modules/system/src/Form/DateFormatFormBase.php b/core/modules/system/src/Form/DateFormatFormBase.php index 252fba4..d5868b5 100644 --- a/core/modules/system/src/Form/DateFormatFormBase.php +++ b/core/modules/system/src/Form/DateFormatFormBase.php @@ -104,7 +104,7 @@ public function form(array $form, FormStateInterface $form_state) { '#type' => 'textfield', '#title' => t('Format string'), '#maxlength' => 100, - '#description' => $this->t('A user-defined date format. See the PHP manual for available options.', array(':url' => 'http://php.net/manual/function.date.php')), + '#description' => $this->t('A user-defined date format. See the PHP manual for available options.'), '#required' => TRUE, '#attributes' => [ 'data-drupal-date-formatter' => 'source', diff --git a/core/modules/system/src/Form/ModulesListConfirmForm.php b/core/modules/system/src/Form/ModulesListConfirmForm.php index dea5207..1fc6868 100644 --- a/core/modules/system/src/Form/ModulesListConfirmForm.php +++ b/core/modules/system/src/Form/ModulesListConfirmForm.php @@ -124,16 +124,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { return $this->redirect('system.modules_list'); } - $items = array(); - // Display a list of required modules that have to be installed as well but - // were not manually selected. - foreach ($this->modules['dependencies'] as $module => $dependencies) { - $items[] = $this->formatPlural(count($dependencies), 'You must enable the @required module to install @module.', 'You must enable the @required modules to install @module.', array( - '@module' => $this->modules['install'][$module], - '@required' => implode(', ', $dependencies), - )); - } - + $items = $this->buildMessageList(); $form['message'] = array( '#theme' => 'item_list', '#items' => $items, @@ -143,6 +134,31 @@ public function buildForm(array $form, FormStateInterface $form_state) { } /** + * Builds the message list for the confirmation form. + * + * @return \Drupal\Component\Render\MarkupInterface[] + * Array of markup for the list of messages on the form. + * + * @see \Drupal\system\Form\ModulesListForm::buildModuleList() + */ + protected function buildMessageList() { + $items = []; + if (!empty($this->modules['dependencies'])) { + // Display a list of required modules that have to be installed as well + // but were not manually selected. + foreach ($this->modules['dependencies'] as $module => $dependencies) { + $items[] = $this->formatPlural(count($dependencies), 'You must enable the @required module to install @module.', 'You must enable the @required modules to install @module.', [ + '@module' => $this->modules['install'][$module], + // It is safe to implode this because module names are not translated + // markup and so will not be double-escaped. + '@required' => implode(', ', $dependencies), + ]); + } + } + return $items; + } + + /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { diff --git a/core/modules/system/src/Form/ModulesListExperimentalConfirmForm.php b/core/modules/system/src/Form/ModulesListExperimentalConfirmForm.php new file mode 100644 index 0000000..db83264 --- /dev/null +++ b/core/modules/system/src/Form/ModulesListExperimentalConfirmForm.php @@ -0,0 +1,42 @@ +t('Are you sure you wish to enable experimental modules?'); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'system_modules_experimental_confirm_form'; + } + + /** + * {@inheritdoc} + */ + protected function buildMessageList() { + drupal_set_message($this->t('Experimental modules are provided for testing purposes only. Use at your own risk.', [':url' => 'https://www.drupal.org/core/experimental']), 'warning'); + + $items = parent::buildMessageList(); + // Add the list of experimental modules after any other messages. + $items[] = $this->t('The following modules are experimental: @modules', ['@modules' => implode(', ', array_values($this->modules['experimental']))]); + + return $items; + } + +} diff --git a/core/modules/system/src/Form/ModulesListForm.php b/core/modules/system/src/Form/ModulesListForm.php index e8a6cea..de67829 100644 --- a/core/modules/system/src/Form/ModulesListForm.php +++ b/core/modules/system/src/Form/ModulesListForm.php @@ -368,6 +368,7 @@ protected function buildModuleList(FormStateInterface $form_state) { $modules = array( 'install' => array(), 'dependencies' => array(), + 'experimental' => [], ); // Required modules have to be installed. @@ -380,10 +381,14 @@ protected function buildModuleList(FormStateInterface $form_state) { } // First, build a list of all modules that were selected. - foreach ($packages as $items) { + foreach ($packages as $package => $items) { foreach ($items as $name => $checkbox) { if ($checkbox['enable'] && !$this->moduleHandler->moduleExists($name)) { $modules['install'][$name] = $data[$name]->info['name']; + // Identify experimental modules. + if ($package == 'Core (Experimental)') { + $modules['experimental'][$name] = $data[$name]->info['name']; + } } } } @@ -394,6 +399,11 @@ protected function buildModuleList(FormStateInterface $form_state) { if (!isset($modules['install'][$dependency]) && !$this->moduleHandler->moduleExists($dependency)) { $modules['dependencies'][$module][$dependency] = $data[$dependency]->info['name']; $modules['install'][$dependency] = $data[$dependency]->info['name']; + + // Identify experimental modules. + if ($data[$dependency]->info['package'] == 'Core (Experimental)') { + $modules['experimental'][$dependency] = $data[$dependency]->info['name']; + } } } } @@ -423,16 +433,16 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // Retrieve a list of modules to install and their dependencies. $modules = $this->buildModuleList($form_state); - // Check if we have to install any dependencies. If there is one or more - // dependencies that are not installed yet, redirect to the confirmation - // form. - if (!empty($modules['dependencies']) || !empty($modules['missing'])) { + // Redirect to a confirmation form if needed. + if (!empty($modules['experimental']) || !empty($modules['dependencies'])) { + + $route_name = !empty($modules['experimental']) ? 'system.modules_list_experimental_confirm' : 'system.modules_list_confirm'; // Write the list of changed module states into a key value store. $account = $this->currentUser()->id(); $this->keyValueExpirable->setWithExpire($account, $modules, 60); // Redirect to the confirmation form. - $form_state->setRedirect('system.modules_list_confirm'); + $form_state->setRedirect($route_name); // We can exit here because at least one modules has dependencies // which we have to prompt the user for in a confirmation form. diff --git a/core/modules/system/src/MenuAccessControlHandler.php b/core/modules/system/src/MenuAccessControlHandler.php index 789577b..4ae9e5b 100644 --- a/core/modules/system/src/MenuAccessControlHandler.php +++ b/core/modules/system/src/MenuAccessControlHandler.php @@ -29,10 +29,10 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter // Locked menus could not be deleted. elseif ($operation == 'delete') { if ($entity->isLocked()) { - return AccessResult::forbidden()->cacheUntilEntityChanges($entity); + return AccessResult::forbidden()->addCacheableDependency($entity); } else { - return parent::checkAccess($entity, $operation, $account)->cacheUntilEntityChanges($entity); + return parent::checkAccess($entity, $operation, $account)->addCacheableDependency($entity); } } diff --git a/core/modules/system/src/Plugin/Block/SystemPoweredByBlock.php b/core/modules/system/src/Plugin/Block/SystemPoweredByBlock.php index 25ea051..a84c3fc 100644 --- a/core/modules/system/src/Plugin/Block/SystemPoweredByBlock.php +++ b/core/modules/system/src/Plugin/Block/SystemPoweredByBlock.php @@ -22,6 +22,13 @@ class SystemPoweredByBlock extends BlockBase { /** * {@inheritdoc} */ + public function defaultConfiguration() { + return ['label_display' => FALSE]; + } + + /** + * {@inheritdoc} + */ public function build() { return array('#markup' => '' . $this->t('Powered by Drupal', array(':poweredby' => 'https://www.drupal.org')) . ''); } diff --git a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php index f9cfb53..3314623 100644 --- a/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php +++ b/core/modules/system/src/Plugin/ImageToolkit/GDToolkit.php @@ -53,7 +53,7 @@ class GDToolkit extends ImageToolkitBase { * * @see \Drupal\system\Plugin\ImageToolkit\GDToolkit::parseFile() * @see \Drupal\system\Plugin\ImageToolkit\GDToolkit::setResource() - * @see http://php.net/manual/en/function.getimagesize.php + * @see http://php.net/manual/function.getimagesize.php */ protected $preLoadInfo = NULL; @@ -378,7 +378,7 @@ public function getRequirements() { // Check for filter and rotate support. if (!function_exists('imagefilter') || !function_exists('imagerotate')) { $requirements['version']['severity'] = REQUIREMENT_WARNING; - $requirements['version']['description'] = t('The GD Library for PHP is enabled, but was compiled without support for functions used by the rotate and desaturate effects. It was probably compiled using the official GD libraries from http://www.libgd.org instead of the GD library bundled with PHP. You should recompile PHP --with-gd using the bundled GD library. See the PHP manual.', array(':url' => 'http://www.php.net/manual/book.image.php')); + $requirements['version']['description'] = t('The GD Library for PHP is enabled, but was compiled without support for functions used by the rotate and desaturate effects. It was probably compiled using the official GD libraries from http://www.libgd.org instead of the GD library bundled with PHP. You should recompile PHP --with-gd using the bundled GD library. See the PHP manual.'); } return $requirements; diff --git a/core/modules/system/src/Tests/Action/ActionUnitTest.php b/core/modules/system/src/Tests/Action/ActionUnitTest.php index 7bc57b5..6de447f 100644 --- a/core/modules/system/src/Tests/Action/ActionUnitTest.php +++ b/core/modules/system/src/Tests/Action/ActionUnitTest.php @@ -9,6 +9,7 @@ use Drupal\simpletest\KernelTestBase; use Drupal\Core\Action\ActionInterface; +use Drupal\system\Entity\Action; use Drupal\user\RoleInterface; /** @@ -80,7 +81,7 @@ public function testOperations() { */ public function testDependencies() { // Create a new action that depends on a user role. - $action = entity_create('action', array( + $action = Action::create(array( 'id' => 'user_add_role_action.' . RoleInterface::ANONYMOUS_ID, 'type' => 'user', 'label' => t('Add the anonymous role to the selected users'), diff --git a/core/modules/system/src/Tests/Ajax/AjaxInGroupTest.php b/core/modules/system/src/Tests/Ajax/AjaxInGroupTest.php index 9732d28..144f20e 100644 --- a/core/modules/system/src/Tests/Ajax/AjaxInGroupTest.php +++ b/core/modules/system/src/Tests/Ajax/AjaxInGroupTest.php @@ -7,8 +7,6 @@ namespace Drupal\system\Tests\Ajax; -use Drupal\Core\Ajax\DataCommand; - /** * Tests that form elements in groups work correctly with AJAX. * diff --git a/core/modules/system/src/Tests/Ajax/AjaxTestBase.php b/core/modules/system/src/Tests/Ajax/AjaxTestBase.php index a4c7bfc..c9edeb5 100644 --- a/core/modules/system/src/Tests/Ajax/AjaxTestBase.php +++ b/core/modules/system/src/Tests/Ajax/AjaxTestBase.php @@ -56,7 +56,7 @@ protected function assertCommand($haystack, $needle, $message) { // If the command has additional data that we're not testing for, do not // consider that a failure. Also, == instead of ===, because we don't // require the key/value pairs to be in any particular order - // (http://www.php.net/manual/language.operators.array.php). + // (http://php.net/manual/language.operators.array.php). if (array_intersect_key($command, $needle) == $needle) { $found = TRUE; break; diff --git a/core/modules/system/src/Tests/Ajax/FrameworkTest.php b/core/modules/system/src/Tests/Ajax/FrameworkTest.php index f6dfd6b..68ed9dd 100644 --- a/core/modules/system/src/Tests/Ajax/FrameworkTest.php +++ b/core/modules/system/src/Tests/Ajax/FrameworkTest.php @@ -198,7 +198,7 @@ public function testCurrentPathChange() { * Tests that overridden CSS files are not added during lazy load. */ public function testLazyLoadOverriddenCSS() { - // The test theme overrides system.module.css without an implementation, + // The test theme overrides js.module.css without an implementation, // thereby removing it. \Drupal::service('theme_handler')->install(array('test_theme')); $this->config('system.theme') @@ -214,6 +214,6 @@ public function testLazyLoadOverriddenCSS() { // information about the file; we only really care about whether it appears // in a LINK or STYLE tag, for which Drupal always adds a query string for // cache control. - $this->assertNoText('system.module.css?', 'Ajax lazy loading does not add overridden CSS files.'); + $this->assertNoText('js.module.css?', 'Ajax lazy loading does not add overridden CSS files.'); } } diff --git a/core/modules/system/src/Tests/Ajax/MultiFormTest.php b/core/modules/system/src/Tests/Ajax/MultiFormTest.php index f87100d..3d16bca 100644 --- a/core/modules/system/src/Tests/Ajax/MultiFormTest.php +++ b/core/modules/system/src/Tests/Ajax/MultiFormTest.php @@ -8,6 +8,8 @@ namespace Drupal\system\Tests\Ajax; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\field\Entity\FieldConfig; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests that AJAX-enabled forms work when multiple instances of the same form @@ -31,17 +33,17 @@ protected function setUp() { // Create a multi-valued field for 'page' nodes to use for Ajax testing. $field_name = 'field_ajax_test'; - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'entity_type' => 'node', 'field_name' => $field_name, 'type' => 'text', 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_name' => $field_name, 'entity_type' => 'node', 'bundle' => 'page', - ))->save(); + ])->save(); entity_get_form_display('node', 'page', 'default') ->setComponent($field_name, array('type' => 'text_textfield')) ->save(); diff --git a/core/modules/system/src/Tests/Block/SystemMenuBlockTest.php b/core/modules/system/src/Tests/Block/SystemMenuBlockTest.php index e10a6af..ad65b8d 100644 --- a/core/modules/system/src/Tests/Block/SystemMenuBlockTest.php +++ b/core/modules/system/src/Tests/Block/SystemMenuBlockTest.php @@ -6,6 +6,8 @@ namespace Drupal\system\Tests\Block; +use Drupal\system\Entity\Menu; +use Drupal\block\Entity\Block; use Drupal\Core\Render\Element; use Drupal\simpletest\KernelTestBase; use Drupal\system\Tests\Routing\MockRouteProvider; @@ -85,7 +87,6 @@ protected function setUp() { parent::setUp(); $this->installSchema('system', 'sequences'); $this->installEntitySchema('user'); - $this->installSchema('system', array('router')); $this->installEntitySchema('menu_link_content'); $account = User::create([ @@ -118,7 +119,7 @@ protected function setUp() { $menu_name = 'mock'; $label = $this->randomMachineName(16); - $this->menu = entity_create('menu', array( + $this->menu = Menu::create(array( 'id' => $menu_name, 'label' => $label, 'description' => 'Description text', @@ -155,7 +156,7 @@ protected function setUp() { */ public function testSystemMenuBlockConfigDependencies() { - $block = entity_create('block', array( + $block = Block::create(array( 'plugin' => 'system_menu_block:' . $this->menu->id(), 'region' => 'footer', 'id' => 'machinename', diff --git a/core/modules/system/src/Tests/Cache/SessionExistsCacheContextTest.php b/core/modules/system/src/Tests/Cache/SessionExistsCacheContextTest.php new file mode 100644 index 0000000..3041dd1 --- /dev/null +++ b/core/modules/system/src/Tests/Cache/SessionExistsCacheContextTest.php @@ -0,0 +1,71 @@ +dumpHeaders = TRUE; + + // 1. No session (anonymous). + $this->assertSessionCookieOnClient(FALSE); + $this->drupalGet(Url::fromRoute('')); + $this->assertSessionCookieOnClient(FALSE); + $this->assertRaw('Session does not exist!'); + $this->assertRaw('[session.exists]=0'); + + // 2. Session (authenticated). + $this->assertSessionCookieOnClient(FALSE); + $this->drupalLogin($this->rootUser); + $this->assertSessionCookieOnClient(TRUE); + $this->assertRaw('Session exists!'); + $this->assertRaw('[session.exists]=1'); + $this->drupalLogout(); + $this->assertSessionCookieOnClient(FALSE); + $this->assertRaw('Session does not exist!'); + $this->assertRaw('[session.exists]=0'); + + // 3. Session (anonymous). + $this->assertSessionCookieOnClient(FALSE); + $this->drupalGet(Url::fromRoute('', [], ['query' => ['trigger_session' => 1]])); + $this->assertSessionCookieOnClient(TRUE); + $this->assertRaw('Session does not exist!'); + $this->assertRaw('[session.exists]=0'); + $this->drupalGet(Url::fromRoute('')); + $this->assertSessionCookieOnClient(TRUE); + $this->assertRaw('Session exists!'); + $this->assertRaw('[session.exists]=1'); + } + + /** + * Asserts whether a session cookie is present on the client or not. + */ + function assertSessionCookieOnClient($expected_present) { + $non_deleted_cookies = array_filter($this->cookies, function ($item) { return $item['value'] !== 'deleted'; }); + $this->assertEqual($expected_present, isset($non_deleted_cookies[$this->getSessionName()]), 'Session cookie exists.'); + } + +} diff --git a/core/modules/system/src/Tests/Common/AddFeedTest.php b/core/modules/system/src/Tests/Common/AddFeedTest.php index 0620051..2e8b4ce 100644 --- a/core/modules/system/src/Tests/Common/AddFeedTest.php +++ b/core/modules/system/src/Tests/Common/AddFeedTest.php @@ -63,9 +63,7 @@ function testBasicFeedAddNoTitle() { // Use the bare HTML page renderer to render our links. $renderer = $this->container->get('bare_html_page_renderer'); - $response = $renderer->renderBarePage( - $build, '', $this->container->get('theme.manager')->getActiveTheme()->getName() - ); + $response = $renderer->renderBarePage($build, '', 'maintenance_page'); // Glean the content from the response object. $this->setRawContent($response->getContent()); // Assert that the content contains the RSS links we specified. diff --git a/core/modules/system/src/Tests/Common/AttachedAssetsTest.php b/core/modules/system/src/Tests/Common/AttachedAssetsTest.php index 3ab2d31..99c8829 100644 --- a/core/modules/system/src/Tests/Common/AttachedAssetsTest.php +++ b/core/modules/system/src/Tests/Common/AttachedAssetsTest.php @@ -51,7 +51,6 @@ class AttachedAssetsTest extends KernelTestBase { */ protected function setUp() { parent::setUp(); - $this->installSchema('system', array('router')); $this->container->get('router.builder')->rebuild(); $this->assetResolver = $this->container->get('asset.resolver'); diff --git a/core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php b/core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php index b3b363a..67787db 100644 --- a/core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php +++ b/core/modules/system/src/Tests/Common/EarlyRenderingControllerTest.php @@ -7,7 +7,6 @@ namespace Drupal\system\Tests\Common; -use Drupal\Core\Render\Element; use Drupal\Core\Url; use Drupal\simpletest\WebTestBase; diff --git a/core/modules/system/src/Tests/Common/PageRenderTest.php b/core/modules/system/src/Tests/Common/PageRenderTest.php index 1b4c387..f7a093d 100644 --- a/core/modules/system/src/Tests/Common/PageRenderTest.php +++ b/core/modules/system/src/Tests/Common/PageRenderTest.php @@ -21,7 +21,6 @@ class PageRenderTest extends KernelTestBase { */ function testHookPageAttachmentsExceptions() { $this->enableModules(['common_test', 'system']); - $this->installSchema('system', 'router'); \Drupal::service('router.builder')->rebuild(); $this->assertPageRenderHookExceptions('common_test', 'hook_page_attachments'); @@ -32,7 +31,6 @@ function testHookPageAttachmentsExceptions() { */ function testHookPageAlter() { $this->enableModules(['common_test', 'system']); - $this->installSchema('system', 'router'); \Drupal::service('router.builder')->rebuild(); $this->assertPageRenderHookExceptions('common_test', 'hook_page_attachments_alter'); diff --git a/core/modules/system/src/Tests/Common/RenderElementTypesTest.php b/core/modules/system/src/Tests/Common/RenderElementTypesTest.php index b71b9c1..fdbdd5b 100644 --- a/core/modules/system/src/Tests/Common/RenderElementTypesTest.php +++ b/core/modules/system/src/Tests/Common/RenderElementTypesTest.php @@ -28,7 +28,6 @@ class RenderElementTypesTest extends KernelTestBase { protected function setUp() { parent::setUp(); $this->installConfig(array('system')); - $this->installSchema('system', array('router')); \Drupal::service('router.builder')->rebuild(); } diff --git a/core/modules/system/src/Tests/Common/RenderTest.php b/core/modules/system/src/Tests/Common/RenderTest.php index 98de2b5..03676e7 100644 --- a/core/modules/system/src/Tests/Common/RenderTest.php +++ b/core/modules/system/src/Tests/Common/RenderTest.php @@ -7,7 +7,6 @@ namespace Drupal\system\Tests\Common; -use Drupal\Core\Render\Element; use Drupal\simpletest\KernelTestBase; /** @@ -58,7 +57,7 @@ public function testProcessAttached() { $build['#attached']['drupal_process_states'][] = []; $renderer = $this->container->get('bare_html_page_renderer'); try { - $renderer->renderBarePage($build, '', $this->container->get('theme.manager')->getActiveTheme()->getName()); + $renderer->renderBarePage($build, '', 'maintenance_page'); $this->fail("Invalid #attachment 'drupal_process_states' allowed"); } catch (\LogicException $e) { diff --git a/core/modules/system/src/Tests/Condition/ConditionFormTest.php b/core/modules/system/src/Tests/Condition/ConditionFormTest.php index cfbdb74..231806f 100644 --- a/core/modules/system/src/Tests/Condition/ConditionFormTest.php +++ b/core/modules/system/src/Tests/Condition/ConditionFormTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Condition; +use Drupal\node\Entity\Node; use Drupal\simpletest\WebTestBase; /** @@ -28,8 +29,13 @@ class ConditionFormTest extends WebTestBase { function testConfigForm() { $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Page')); $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article')); - $article = entity_create('node', array('type' => 'article', 'title' => $this->randomMachineName())); + + $article = Node::create([ + 'type' => 'article', + 'title' => $this->randomMachineName(), + ]); $article->save(); + $this->drupalGet('condition_test'); $this->assertField('bundles[article]', 'There is an article bundle selector.'); $this->assertField('bundles[page]', 'There is a page bundle selector.'); diff --git a/core/modules/system/src/Tests/Condition/CurrentThemeConditionTest.php b/core/modules/system/src/Tests/Condition/CurrentThemeConditionTest.php index 3139ecc..bb80e8b 100644 --- a/core/modules/system/src/Tests/Condition/CurrentThemeConditionTest.php +++ b/core/modules/system/src/Tests/Condition/CurrentThemeConditionTest.php @@ -23,14 +23,6 @@ class CurrentThemeConditionTest extends KernelTestBase { public static $modules = array('system', 'theme_test'); /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - $this->installSchema('system', array('router')); - } - - /** * Tests the current theme condition. */ public function testCurrentTheme() { @@ -52,7 +44,7 @@ public function testCurrentTheme() { $this->assertTrue($condition_negated->execute()); // Set the expected theme to be used. - $this->config('system.theme')->set('default', 'test_theme')->save(); + \Drupal::service('theme_handler')->setDefault('test_theme'); \Drupal::theme()->resetActiveTheme(); $this->assertTrue($condition->execute()); diff --git a/core/modules/system/src/Tests/Database/AlterTest.php b/core/modules/system/src/Tests/Database/AlterTest.php deleted file mode 100644 index 991ae85..0000000 --- a/core/modules/system/src/Tests/Database/AlterTest.php +++ /dev/null @@ -1,155 +0,0 @@ -addField('test', 'name'); - $query->addField('test', 'age', 'age'); - $query->addTag('database_test_alter_add_range'); - - $result = $query->execute()->fetchAll(); - - $this->assertEqual(count($result), 2, 'Returned the correct number of rows.'); - } - - /** - * Tests that we can alter the joins on a query. - */ - function testAlterWithJoin() { - $query = db_select('test_task'); - $tid_field = $query->addField('test_task', 'tid'); - $task_field = $query->addField('test_task', 'task'); - $query->orderBy($task_field); - $query->addTag('database_test_alter_add_join'); - - $result = $query->execute(); - - $records = $result->fetchAll(); - - $this->assertEqual(count($records), 2, 'Returned the correct number of rows.'); - - $this->assertEqual($records[0]->name, 'George', 'Correct data retrieved.'); - $this->assertEqual($records[0]->$tid_field, 4, 'Correct data retrieved.'); - $this->assertEqual($records[0]->$task_field, 'sing', 'Correct data retrieved.'); - $this->assertEqual($records[1]->name, 'George', 'Correct data retrieved.'); - $this->assertEqual($records[1]->$tid_field, 5, 'Correct data retrieved.'); - $this->assertEqual($records[1]->$task_field, 'sleep', 'Correct data retrieved.'); - } - - /** - * Tests that we can alter a query's conditionals. - */ - function testAlterChangeConditional() { - $query = db_select('test_task'); - $tid_field = $query->addField('test_task', 'tid'); - $pid_field = $query->addField('test_task', 'pid'); - $task_field = $query->addField('test_task', 'task'); - $people_alias = $query->join('test', 'people', "test_task.pid = people.id"); - $name_field = $query->addField($people_alias, 'name', 'name'); - $query->condition('test_task.tid', '1'); - $query->orderBy($tid_field); - $query->addTag('database_test_alter_change_conditional'); - - $result = $query->execute(); - - $records = $result->fetchAll(); - - $this->assertEqual(count($records), 1, 'Returned the correct number of rows.'); - $this->assertEqual($records[0]->$name_field, 'John', 'Correct data retrieved.'); - $this->assertEqual($records[0]->$tid_field, 2, 'Correct data retrieved.'); - $this->assertEqual($records[0]->$pid_field, 1, 'Correct data retrieved.'); - $this->assertEqual($records[0]->$task_field, 'sleep', 'Correct data retrieved.'); - } - - /** - * Tests that we can alter the fields of a query. - */ - function testAlterChangeFields() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $query->orderBy('name'); - $query->addTag('database_test_alter_change_fields'); - - $record = $query->execute()->fetch(); - $this->assertEqual($record->$name_field, 'George', 'Correct data retrieved.'); - $this->assertFalse(isset($record->$age_field), 'Age field not found, as intended.'); - } - - /** - * Tests that we can alter expressions in the query. - */ - function testAlterExpression() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addExpression("age*2", 'double_age'); - $query->condition('age', 27); - $query->addTag('database_test_alter_change_expressions'); - $result = $query->execute(); - - // Ensure that we got the right record. - $record = $result->fetch(); - - $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.'); - $this->assertEqual($record->$age_field, 27*3, 'Fetched age expression is correct.'); - } - - /** - * Tests that we can remove a range() value from a query. - * - * This also tests hook_query_TAG_alter(). - */ - function testAlterRemoveRange() { - $query = db_select('test'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - $query->range(0, 2); - $query->addTag('database_test_alter_remove_range'); - - $num_records = count($query->execute()->fetchAll()); - - $this->assertEqual($num_records, 4, 'Returned the correct number of rows.'); - } - - /** - * Tests that we can do basic alters on subqueries. - */ - function testSimpleAlterSubquery() { - // Create a sub-query with an alter tag. - $subquery = db_select('test', 'p'); - $subquery->addField('p', 'name'); - $subquery->addField('p', 'id'); - // Pick out George. - $subquery->condition('age', 27); - $subquery->addExpression("age*2", 'double_age'); - // This query alter should change it to age * 3. - $subquery->addTag('database_test_alter_change_expressions'); - - // Create a main query and join to sub-query. - $query = db_select('test_task', 'tt'); - $query->join($subquery, 'pq', 'pq.id = tt.pid'); - $age_field = $query->addField('pq', 'double_age'); - $name_field = $query->addField('pq', 'name'); - - $record = $query->execute()->fetch(); - $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.'); - $this->assertEqual($record->$age_field, 27*3, 'Fetched age expression is correct.'); - } -} diff --git a/core/modules/system/src/Tests/Database/BasicSyntaxTest.php b/core/modules/system/src/Tests/Database/BasicSyntaxTest.php deleted file mode 100644 index 3d4b68d..0000000 --- a/core/modules/system/src/Tests/Database/BasicSyntaxTest.php +++ /dev/null @@ -1,141 +0,0 @@ - 'This', - ':a2' => ' ', - ':a3' => 'is', - ':a4' => ' a ', - ':a5' => 'test.', - )); - $this->assertIdentical($result->fetchField(), 'This is a test.', 'Basic CONCAT works.'); - } - - /** - * Tests string concatenation with field values. - */ - function testConcatFields() { - $result = db_query('SELECT CONCAT(:a1, CONCAT(name, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age', array( - ':a1' => 'The age of ', - ':a2' => ' is ', - ':a3' => '.', - ':age' => 25, - )); - $this->assertIdentical($result->fetchField(), 'The age of John is 25.', 'Field CONCAT works.'); - } - - /** - * Tests string concatenation with separator. - */ - function testConcatWsLiterals() { - $result = db_query("SELECT CONCAT_WS(', ', :a1, NULL, :a2, :a3, :a4)", array( - ':a1' => 'Hello', - ':a2' => NULL, - ':a3' => '', - ':a4' => 'world.', - )); - $this->assertIdentical($result->fetchField(), 'Hello, , world.'); - } - - /** - * Tests string concatenation with separator, with field values. - */ - function testConcatWsFields() { - $result = db_query("SELECT CONCAT_WS('-', :a1, name, :a2, age) FROM {test} WHERE age = :age", array( - ':a1' => 'name', - ':a2' => 'age', - ':age' => 25, - )); - $this->assertIdentical($result->fetchField(), 'name-John-age-25'); - } - - /** - * Tests escaping of LIKE wildcards. - */ - function testLikeEscape() { - db_insert('test') - ->fields(array( - 'name' => 'Ring_', - )) - ->execute(); - - // Match both "Ringo" and "Ring_". - $num_matches = db_select('test', 't') - ->condition('name', 'Ring_', 'LIKE') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertIdentical($num_matches, '2', 'Found 2 records.'); - // Match only "Ring_" using a LIKE expression with no wildcards. - $num_matches = db_select('test', 't') - ->condition('name', db_like('Ring_'), 'LIKE') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertIdentical($num_matches, '1', 'Found 1 record.'); - } - - /** - * Tests a LIKE query containing a backslash. - */ - function testLikeBackslash() { - db_insert('test') - ->fields(array('name')) - ->values(array( - 'name' => 'abcde\f', - )) - ->values(array( - 'name' => 'abc%\_', - )) - ->execute(); - - // Match both rows using a LIKE expression with two wildcards and a verbatim - // backslash. - $num_matches = db_select('test', 't') - ->condition('name', 'abc%\\\\_', 'LIKE') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertIdentical($num_matches, '2', 'Found 2 records.'); - // Match only the former using a LIKE expression with no wildcards. - $num_matches = db_select('test', 't') - ->condition('name', db_like('abc%\_'), 'LIKE') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertIdentical($num_matches, '1', 'Found 1 record.'); - } - - /** - * Tests \Drupal\Core\Database\Connection::getFullQualifiedTableName(). - */ - public function testGetFullQualifiedTableName() { - $database = \Drupal::database(); - $num_matches = $database->select($database->getFullQualifiedTableName('test'), 't') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertIdentical($num_matches, '4', 'Found 4 records.'); - } - -} diff --git a/core/modules/system/src/Tests/Database/CaseSensitivityTest.php b/core/modules/system/src/Tests/Database/CaseSensitivityTest.php deleted file mode 100644 index 6dcf743..0000000 --- a/core/modules/system/src/Tests/Database/CaseSensitivityTest.php +++ /dev/null @@ -1,35 +0,0 @@ -fetchField(); - - db_insert('test') - ->fields(array( - 'name' => 'john', // <- A record already exists with name 'John'. - 'age' => 2, - 'job' => 'Baby', - )) - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - $this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'john'))->fetchField(); - $this->assertIdentical($saved_age, '2', 'Can retrieve after inserting.'); - } -} diff --git a/core/modules/system/src/Tests/Database/ConnectionTest.php b/core/modules/system/src/Tests/Database/ConnectionTest.php deleted file mode 100644 index 1948be1..0000000 --- a/core/modules/system/src/Tests/Database/ConnectionTest.php +++ /dev/null @@ -1,180 +0,0 @@ -assertNotNull($db1, 'default connection is a real connection object.'); - $this->assertNotNull($db2, 'replica connection is a real connection object.'); - $this->assertNotIdentical($db1, $db2, 'Each target refers to a different connection.'); - - // Try to open those targets another time, that should return the same objects. - $db1b = Database::getConnection('default', 'default'); - $db2b = Database::getConnection('replica', 'default'); - $this->assertIdentical($db1, $db1b, 'A second call to getConnection() returns the same object.'); - $this->assertIdentical($db2, $db2b, 'A second call to getConnection() returns the same object.'); - - // Try to open an unknown target. - $unknown_target = $this->randomMachineName(); - $db3 = Database::getConnection($unknown_target, 'default'); - $this->assertNotNull($db3, 'Opening an unknown target returns a real connection object.'); - $this->assertIdentical($db1, $db3, 'An unknown target opens the default connection.'); - - // Try to open that unknown target another time, that should return the same object. - $db3b = Database::getConnection($unknown_target, 'default'); - $this->assertIdentical($db3, $db3b, 'A second call to getConnection() returns the same object.'); - } - - /** - * Tests that connections return appropriate connection objects. - */ - function testConnectionRoutingOverride() { - // Clone the primary credentials to a replica connection. - // Note this will result in two independent connection objects that happen - // to point to the same place. - $connection_info = Database::getConnectionInfo('default'); - Database::addConnectionInfo('default', 'replica', $connection_info['default']); - - Database::ignoreTarget('default', 'replica'); - - $db1 = Database::getConnection('default', 'default'); - $db2 = Database::getConnection('replica', 'default'); - - $this->assertIdentical($db1, $db2, 'Both targets refer to the same connection.'); - } - - /** - * Tests the closing of a database connection. - */ - function testConnectionClosing() { - // Open the default target so we have an object to compare. - $db1 = Database::getConnection('default', 'default'); - - // Try to close the default connection, then open a new one. - Database::closeConnection('default', 'default'); - $db2 = Database::getConnection('default', 'default'); - - // Opening a connection after closing it should yield an object different than the original. - $this->assertNotIdentical($db1, $db2, 'Opening the default connection after it is closed returns a new object.'); - } - - /** - * Tests the connection options of the active database. - */ - function testConnectionOptions() { - $connection_info = Database::getConnectionInfo('default'); - - // Be sure we're connected to the default database. - $db = Database::getConnection('default', 'default'); - $connectionOptions = $db->getConnectionOptions(); - - // In the MySQL driver, the port can be different, so check individual - // options. - $this->assertEqual($connection_info['default']['driver'], $connectionOptions['driver'], 'The default connection info driver matches the current connection options driver.'); - $this->assertEqual($connection_info['default']['database'], $connectionOptions['database'], 'The default connection info database matches the current connection options database.'); - - // Set up identical replica and confirm connection options are identical. - Database::addConnectionInfo('default', 'replica', $connection_info['default']); - $db2 = Database::getConnection('replica', 'default'); - $connectionOptions2 = $db2->getConnectionOptions(); - - // Get a fresh copy of the default connection options. - $connectionOptions = $db->getConnectionOptions(); - $this->assertIdentical($connectionOptions, $connectionOptions2, 'The default and replica connection options are identical.'); - - // Set up a new connection with different connection info. - $test = $connection_info['default']; - $test['database'] .= 'test'; - Database::addConnectionInfo('test', 'default', $test); - $connection_info = Database::getConnectionInfo('test'); - - // Get a fresh copy of the default connection options. - $connectionOptions = $db->getConnectionOptions(); - $this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], 'The test connection info database does not match the current connection options database.'); - } - - /** - * Ensure that you cannot execute multiple statements on phpversion() > 5.5.21 or > 5.6.5. - */ - public function testMultipleStatementsForNewPhp() { - // This just tests mysql, as other PDO integrations don't allow disabling - // multiple statements. - if (Database::getConnection()->databaseType() !== 'mysql' || !defined('\PDO::MYSQL_ATTR_MULTI_STATEMENTS')) { - return; - } - - $db = Database::getConnection('default', 'default'); - // Disable the protection at the PHP level. - try { - $db->query('SELECT * FROM {test}; SELECT * FROM {test_people}', - [], - [ 'allow_delimiter_in_query' => TRUE ] - ); - $this->fail('No PDO exception thrown for multiple statements.'); - } - catch (DatabaseExceptionWrapper $e) { - $this->pass('PDO exception thrown for multiple statements.'); - } - } - - /** - * Ensure that you cannot execute multiple statements. - */ - public function testMultipleStatements() { - - $db = Database::getConnection('default', 'default'); - try { - $db->query('SELECT * FROM {test}; SELECT * FROM {test_people}'); - $this->fail('No exception thrown for multiple statements.'); - } - catch (\InvalidArgumentException $e) { - $this->pass('Exception thrown for multiple statements.'); - } - } - - /** - * Test the escapeTable(), escapeField() and escapeAlias() methods with all possible reserved words in PostgreSQL. - */ - public function testPostgresqlReservedWords() { - if (Database::getConnection()->databaseType() !== 'pgsql') { - return; - } - - $db = Database::getConnection('default', 'default'); - $stmt = $db->query("SELECT word FROM pg_get_keywords() WHERE catcode IN ('R', 'T')"); - $stmt->execute(); - foreach ($stmt->fetchAllAssoc('word') as $word => $row) { - $expected = '"' . $word . '"'; - $this->assertIdentical($db->escapeTable($word), $expected, format_string('The reserved word %word was correctly escaped when used as a table name.', array('%word' => $word))); - $this->assertIdentical($db->escapeField($word), $expected, format_string('The reserved word %word was correctly escaped when used as a column name.', array('%word' => $word))); - $this->assertIdentical($db->escapeAlias($word), $expected, format_string('The reserved word %word was correctly escaped when used as an alias.', array('%word' => $word))); - } - } - -} diff --git a/core/modules/system/src/Tests/Database/ConnectionUnitTest.php b/core/modules/system/src/Tests/Database/ConnectionUnitTest.php deleted file mode 100644 index d1916b3..0000000 --- a/core/modules/system/src/Tests/Database/ConnectionUnitTest.php +++ /dev/null @@ -1,248 +0,0 @@ -key = 'default'; - $this->originalTarget = 'default'; - $this->target = 'DatabaseConnectionUnitTest'; - - // Determine whether the database driver is MySQL. If it is not, the test - // methods will not be executed. - // @todo Make this test driver-agnostic, or find a proper way to skip it. - // See https://www.drupal.org/node/1273478. - $connection_info = Database::getConnectionInfo('default'); - $this->skipTest = (bool) ($connection_info['default']['driver'] != 'mysql'); - if ($this->skipTest) { - // Insert an assertion to prevent Simpletest from interpreting the test - // as failure. - $this->pass('This test is only compatible with MySQL.'); - } - - // Create an additional connection to monitor the connections being opened - // and closed in this test. - // @see TestBase::changeDatabasePrefix() - Database::addConnectionInfo('default', 'monitor', $connection_info['default']); - $this->monitor = Database::getConnection('monitor'); - } - - /** - * Adds a new database connection info to Database. - */ - protected function addConnection() { - // Add a new target to the connection, by cloning the current connection. - $connection_info = Database::getConnectionInfo($this->key); - Database::addConnectionInfo($this->key, $this->target, $connection_info[$this->originalTarget]); - - // Verify that the new target exists. - $info = Database::getConnectionInfo($this->key); - // Note: Custom assertion message to not expose database credentials. - $this->assertIdentical($info[$this->target], $connection_info[$this->key], 'New connection info found.'); - } - - /** - * Returns the connection ID of the current test connection. - * - * @return int - */ - protected function getConnectionID() { - return (int) Database::getConnection($this->target, $this->key)->query('SELECT CONNECTION_ID()')->fetchField(); - } - - /** - * Asserts that a connection ID exists. - * - * @param int $id - * The connection ID to verify. - */ - protected function assertConnection($id) { - $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0); - return $this->assertTrue(isset($list[$id]), format_string('Connection ID @id found.', array('@id' => $id))); - } - - /** - * Asserts that a connection ID does not exist. - * - * @param int $id - * The connection ID to verify. - */ - protected function assertNoConnection($id) { - $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0); - return $this->assertFalse(isset($list[$id]), format_string('Connection ID @id not found.', array('@id' => $id))); - } - - /** - * Tests Database::closeConnection() without query. - * - * @todo getConnectionID() executes a query. - */ - function testOpenClose() { - if ($this->skipTest) { - return; - } - // Add and open a new connection. - $this->addConnection(); - $id = $this->getConnectionID(); - Database::getConnection($this->target, $this->key); - - // Verify that there is a new connection. - $this->assertConnection($id); - - // Close the connection. - Database::closeConnection($this->target, $this->key); - // Wait 20ms to give the database engine sufficient time to react. - usleep(20000); - - // Verify that we are back to the original connection count. - $this->assertNoConnection($id); - } - - /** - * Tests Database::closeConnection() with a query. - */ - function testOpenQueryClose() { - if ($this->skipTest) { - return; - } - // Add and open a new connection. - $this->addConnection(); - $id = $this->getConnectionID(); - Database::getConnection($this->target, $this->key); - - // Verify that there is a new connection. - $this->assertConnection($id); - - // Execute a query. - Database::getConnection($this->target, $this->key)->query('SHOW TABLES'); - - // Close the connection. - Database::closeConnection($this->target, $this->key); - // Wait 20ms to give the database engine sufficient time to react. - usleep(20000); - - // Verify that we are back to the original connection count. - $this->assertNoConnection($id); - } - - /** - * Tests Database::closeConnection() with a query and custom prefetch method. - */ - function testOpenQueryPrefetchClose() { - if ($this->skipTest) { - return; - } - // Add and open a new connection. - $this->addConnection(); - $id = $this->getConnectionID(); - Database::getConnection($this->target, $this->key); - - // Verify that there is a new connection. - $this->assertConnection($id); - - // Execute a query. - Database::getConnection($this->target, $this->key)->query('SHOW TABLES')->fetchCol(); - - // Close the connection. - Database::closeConnection($this->target, $this->key); - // Wait 20ms to give the database engine sufficient time to react. - usleep(20000); - - // Verify that we are back to the original connection count. - $this->assertNoConnection($id); - } - - /** - * Tests Database::closeConnection() with a select query. - */ - function testOpenSelectQueryClose() { - if ($this->skipTest) { - return; - } - // Add and open a new connection. - $this->addConnection(); - $id = $this->getConnectionID(); - Database::getConnection($this->target, $this->key); - - // Verify that there is a new connection. - $this->assertConnection($id); - - // Create a table. - $name = 'foo'; - Database::getConnection($this->target, $this->key)->schema()->createTable($name, array( - 'fields' => array( - 'name' => array( - 'type' => 'varchar', - 'length' => 255, - ), - ), - )); - - // Execute a query. - Database::getConnection($this->target, $this->key)->select('foo', 'f') - ->fields('f', array('name')) - ->execute() - ->fetchAll(); - - // Drop the table. - Database::getConnection($this->target, $this->key)->schema()->dropTable($name); - - // Close the connection. - Database::closeConnection($this->target, $this->key); - // Wait 20ms to give the database engine sufficient time to react. - usleep(20000); - - // Verify that we are back to the original connection count. - $this->assertNoConnection($id); - } - - /** - * Tests pdo options override. - */ - public function testConnectionOpen() { - $connection = Database::getConnection('default'); - $reflection = new \ReflectionObject($connection); - $connection_property = $reflection->getProperty('connection'); - $connection_property->setAccessible(TRUE); - $error_mode = $connection_property->getValue($connection) - ->getAttribute(\PDO::ATTR_ERRMODE); - $this->assertEqual($error_mode, \PDO::ERRMODE_EXCEPTION, 'Ensure the default error mode is set to exception.'); - - $connection = Database::getConnectionInfo('default'); - $connection['default']['pdo'][\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_SILENT; - Database::addConnectionInfo('test', 'default', $connection['default']); - $connection = Database::getConnection('default', 'test'); - - $reflection = new \ReflectionObject($connection); - $connection_property = $reflection->getProperty('connection'); - $connection_property->setAccessible(TRUE); - $error_mode = $connection_property->getValue($connection) - ->getAttribute(\PDO::ATTR_ERRMODE); - $this->assertEqual($error_mode, \PDO::ERRMODE_SILENT, 'Ensure PDO connection options can be overridden.'); - - Database::removeConnection('test'); - } - -} diff --git a/core/modules/system/src/Tests/Database/DatabaseExceptionWrapperTest.php b/core/modules/system/src/Tests/Database/DatabaseExceptionWrapperTest.php deleted file mode 100644 index d534e5a..0000000 --- a/core/modules/system/src/Tests/Database/DatabaseExceptionWrapperTest.php +++ /dev/null @@ -1,65 +0,0 @@ -prepare('bananas'); - - // MySQL only validates the syntax upon trying to execute a query. - // @throws \Drupal\Core\Database\DatabaseExceptionWrapper - $connection->query($query); - - $this->fail('Expected PDOException or DatabaseExceptionWrapper, none was thrown.'); - } - catch (\PDOException $e) { - $this->pass('Expected PDOException was thrown.'); - } - catch (DatabaseExceptionWrapper $e) { - $this->pass('Expected DatabaseExceptionWrapper was thrown.'); - } - catch (\Exception $e) { - $this->fail("Thrown exception is not a PDOException:\n" . (string) $e); - } - } - - /** - * Tests the expected database exception thrown for inexistent tables. - */ - public function testQueryThrowsDatabaseExceptionWrapperException() { - $connection = Database::getConnection(); - try { - $connection->query('SELECT * FROM {does_not_exist}'); - $this->fail('Expected PDOException, none was thrown.'); - } - catch (DatabaseExceptionWrapper $e) { - $this->pass('Expected DatabaseExceptionWrapper was thrown.'); - } - catch (\Exception $e) { - $this->fail("Thrown exception is not a DatabaseExceptionWrapper:\n" . (string) $e); - } - } - -} diff --git a/core/modules/system/src/Tests/Database/DatabaseTestBase.php b/core/modules/system/src/Tests/Database/DatabaseTestBase.php deleted file mode 100644 index a6b7611..0000000 --- a/core/modules/system/src/Tests/Database/DatabaseTestBase.php +++ /dev/null @@ -1,151 +0,0 @@ -installSchema('database_test', array( - 'test', - 'test_people', - 'test_people_copy', - 'test_one_blob', - 'test_two_blobs', - 'test_task', - 'test_null', - 'test_serialized', - 'test_special_columns', - 'TEST_UPPERCASE', - )); - self::addSampleData(); - } - - /** - * Sets up tables for NULL handling. - */ - function ensureSampleDataNull() { - db_insert('test_null') - ->fields(array('name', 'age')) - ->values(array( - 'name' => 'Kermit', - 'age' => 25, - )) - ->values(array( - 'name' => 'Fozzie', - 'age' => NULL, - )) - ->values(array( - 'name' => 'Gonzo', - 'age' => 27, - )) - ->execute(); - } - - /** - * Sets up our sample data. - */ - static function addSampleData() { - // We need the IDs, so we can't use a multi-insert here. - $john = db_insert('test') - ->fields(array( - 'name' => 'John', - 'age' => 25, - 'job' => 'Singer', - )) - ->execute(); - - $george = db_insert('test') - ->fields(array( - 'name' => 'George', - 'age' => 27, - 'job' => 'Singer', - )) - ->execute(); - - db_insert('test') - ->fields(array( - 'name' => 'Ringo', - 'age' => 28, - 'job' => 'Drummer', - )) - ->execute(); - - $paul = db_insert('test') - ->fields(array( - 'name' => 'Paul', - 'age' => 26, - 'job' => 'Songwriter', - )) - ->execute(); - - db_insert('test_people') - ->fields(array( - 'name' => 'Meredith', - 'age' => 30, - 'job' => 'Speaker', - )) - ->execute(); - - db_insert('test_task') - ->fields(array('pid', 'task', 'priority')) - ->values(array( - 'pid' => $john, - 'task' => 'eat', - 'priority' => 3, - )) - ->values(array( - 'pid' => $john, - 'task' => 'sleep', - 'priority' => 4, - )) - ->values(array( - 'pid' => $john, - 'task' => 'code', - 'priority' => 1, - )) - ->values(array( - 'pid' => $george, - 'task' => 'sing', - 'priority' => 2, - )) - ->values(array( - 'pid' => $george, - 'task' => 'sleep', - 'priority' => 2, - )) - ->values(array( - 'pid' => $paul, - 'task' => 'found new band', - 'priority' => 1, - )) - ->values(array( - 'pid' => $paul, - 'task' => 'perform at superbowl', - 'priority' => 3, - )) - ->execute(); - - db_insert('test_special_columns') - ->fields(array( - 'id' => 1, - 'offset' => 'Offset value 1', - )) - ->execute(); - } -} diff --git a/core/modules/system/src/Tests/Database/DatabaseWebTestBase.php b/core/modules/system/src/Tests/Database/DatabaseWebTestBase.php index 690a2a6..db80acc 100644 --- a/core/modules/system/src/Tests/Database/DatabaseWebTestBase.php +++ b/core/modules/system/src/Tests/Database/DatabaseWebTestBase.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Database; +use Drupal\KernelTests\Core\Database\DatabaseTestBase; use Drupal\simpletest\WebTestBase; /** diff --git a/core/modules/system/src/Tests/Database/DeleteTruncateTest.php b/core/modules/system/src/Tests/Database/DeleteTruncateTest.php deleted file mode 100644 index 56f53cb..0000000 --- a/core/modules/system/src/Tests/Database/DeleteTruncateTest.php +++ /dev/null @@ -1,88 +0,0 @@ -fetchField(); - $pid_to_delete = db_query("SELECT * FROM {test_task} WHERE task = 'sleep'")->fetchField(); - - $subquery = db_select('test', 't') - ->fields('t', array('id')) - ->condition('t.id', array($pid_to_delete), 'IN'); - $delete = db_delete('test_task') - ->condition('task', 'sleep') - ->condition('pid', $subquery, 'IN'); - - $num_deleted = $delete->execute(); - $this->assertEqual($num_deleted, 1, 'Deleted 1 record.'); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.'); - } - - /** - * Confirms that we can delete a single record successfully. - */ - function testSimpleDelete() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - - $num_deleted = db_delete('test') - ->condition('id', 1) - ->execute(); - $this->assertIdentical($num_deleted, 1, 'Deleted 1 record.'); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.'); - } - - /** - * Confirms that we can truncate a whole table successfully. - */ - function testTruncate() { - $num_records_before = db_query("SELECT COUNT(*) FROM {test}")->fetchField(); - $this->assertTrue($num_records_before > 0, 'The table is not empty.'); - - db_truncate('test')->execute(); - - $num_records_after = db_query("SELECT COUNT(*) FROM {test}")->fetchField(); - $this->assertEqual(0, $num_records_after, 'Truncate really deletes everything.'); - } - - /** - * Confirms that we can delete a single special column name record successfully. - */ - function testSpecialColumnDelete() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_special_columns}')->fetchField(); - - $num_deleted = db_delete('test_special_columns') - ->condition('id', 1) - ->execute(); - $this->assertIdentical($num_deleted, 1, 'Deleted 1 special column record.'); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_special_columns}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.'); - } -} diff --git a/core/modules/system/src/Tests/Database/FetchTest.php b/core/modules/system/src/Tests/Database/FetchTest.php deleted file mode 100644 index 0cb8dfc..0000000 --- a/core/modules/system/src/Tests/Database/FetchTest.php +++ /dev/null @@ -1,150 +0,0 @@ - 25)); - $this->assertTrue($result instanceof StatementInterface, 'Result set is a Drupal statement object.'); - foreach ($result as $record) { - $records[] = $record; - $this->assertTrue(is_object($record), 'Record is an object.'); - $this->assertIdentical($record->name, 'John', '25 year old is John.'); - } - - $this->assertIdentical(count($records), 1, 'There is only one record.'); - } - - /** - * Confirms that we can fetch a record to an object explicitly. - */ - function testQueryFetchObject() { - $records = array(); - $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => \PDO::FETCH_OBJ)); - foreach ($result as $record) { - $records[] = $record; - $this->assertTrue(is_object($record), 'Record is an object.'); - $this->assertIdentical($record->name, 'John', '25 year old is John.'); - } - - $this->assertIdentical(count($records), 1, 'There is only one record.'); - } - - /** - * Confirms that we can fetch a record to an associative array explicitly. - */ - function testQueryFetchArray() { - $records = array(); - $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => \PDO::FETCH_ASSOC)); - foreach ($result as $record) { - $records[] = $record; - if ($this->assertTrue(is_array($record), 'Record is an array.')) { - $this->assertIdentical($record['name'], 'John', 'Record can be accessed associatively.'); - } - } - - $this->assertIdentical(count($records), 1, 'There is only one record.'); - } - - /** - * Confirms that we can fetch a record into a new instance of a custom class. - * - * @see \Drupal\system\Tests\Database\FakeRecord - */ - function testQueryFetchClass() { - $records = array(); - $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => 'Drupal\system\Tests\Database\FakeRecord')); - foreach ($result as $record) { - $records[] = $record; - if ($this->assertTrue($record instanceof FakeRecord, 'Record is an object of class FakeRecord.')) { - $this->assertIdentical($record->name, 'John', '25 year old is John.'); - } - } - - $this->assertIdentical(count($records), 1, 'There is only one record.'); - } - - /** - * Confirms that we can fetch a record into an indexed array explicitly. - */ - function testQueryFetchNum() { - $records = array(); - $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => \PDO::FETCH_NUM)); - foreach ($result as $record) { - $records[] = $record; - if ($this->assertTrue(is_array($record), 'Record is an array.')) { - $this->assertIdentical($record[0], 'John', 'Record can be accessed numerically.'); - } - } - - $this->assertIdentical(count($records), 1, 'There is only one record'); - } - - /** - * Confirms that we can fetch a record into a doubly-keyed array explicitly. - */ - function testQueryFetchBoth() { - $records = array(); - $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => \PDO::FETCH_BOTH)); - foreach ($result as $record) { - $records[] = $record; - if ($this->assertTrue(is_array($record), 'Record is an array.')) { - $this->assertIdentical($record[0], 'John', 'Record can be accessed numerically.'); - $this->assertIdentical($record['name'], 'John', 'Record can be accessed associatively.'); - } - } - - $this->assertIdentical(count($records), 1, 'There is only one record.'); - } - - /** - * Confirms that we can fetch an entire column of a result set at once. - */ - function testQueryFetchCol() { - $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25)); - $column = $result->fetchCol(); - $this->assertIdentical(count($column), 3, 'fetchCol() returns the right number of records.'); - - $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25)); - $i = 0; - foreach ($result as $record) { - $this->assertIdentical($record->name, $column[$i++], 'Column matches direct access.'); - } - } - - /** - * Tests that rowCount() throws exception on SELECT query. - */ - public function testRowCount() { - $result = db_query('SELECT name FROM {test}'); - try { - $result->rowCount(); - $exception = FALSE; - } - catch (RowCountException $e) { - $exception = TRUE; - } - $this->assertTrue($exception, 'Exception was thrown'); - } - -} diff --git a/core/modules/system/src/Tests/Database/InsertDefaultsTest.php b/core/modules/system/src/Tests/Database/InsertDefaultsTest.php deleted file mode 100644 index 980b6b3..0000000 --- a/core/modules/system/src/Tests/Database/InsertDefaultsTest.php +++ /dev/null @@ -1,64 +0,0 @@ -useDefaults(array('job')); - $id = $query->execute(); - - $schema = drupal_get_module_schema('database_test', 'test'); - - $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField(); - $this->assertEqual($job, $schema['fields']['job']['default'], 'Default field value is set.'); - } - - /** - * Tests that no action will be preformed if no fields are specified. - */ - function testDefaultEmptyInsert() { - $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - - try { - db_insert('test')->execute(); - // This is only executed if no exception has been thrown. - $this->fail('Expected exception NoFieldsException has not been thrown.'); - } catch (NoFieldsException $e) { - $this->pass('Expected exception NoFieldsException has been thrown.'); - } - - $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - $this->assertIdentical($num_records_before, $num_records_after, 'Do nothing as no fields are specified.'); - } - - /** - * Tests that we can insert fields with values and defaults in the same query. - */ - function testDefaultInsertWithFields() { - $query = db_insert('test') - ->fields(array('name' => 'Bob')) - ->useDefaults(array('job')); - $id = $query->execute(); - - $schema = drupal_get_module_schema('database_test', 'test'); - - $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField(); - $this->assertEqual($job, $schema['fields']['job']['default'], 'Default field value is set.'); - } -} diff --git a/core/modules/system/src/Tests/Database/InsertLobTest.php b/core/modules/system/src/Tests/Database/InsertLobTest.php deleted file mode 100644 index 774c89a..0000000 --- a/core/modules/system/src/Tests/Database/InsertLobTest.php +++ /dev/null @@ -1,43 +0,0 @@ -assertTrue(strlen($data) === 15, 'Test data contains a NULL.'); - $id = db_insert('test_one_blob') - ->fields(array('blob1' => $data)) - ->execute(); - $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc(); - $this->assertTrue($r['blob1'] === $data, format_string('Can insert a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r)))); - } - - /** - * Tests that we can insert multiple blob fields in the same query. - */ - function testInsertMultipleBlob() { - $id = db_insert('test_two_blobs') - ->fields(array( - 'blob1' => 'This is', - 'blob2' => 'a test', - )) - ->execute(); - $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc(); - $this->assertTrue($r['blob1'] === 'This is' && $r['blob2'] === 'a test', 'Can insert multiple blobs per row.'); - } -} diff --git a/core/modules/system/src/Tests/Database/InsertTest.php b/core/modules/system/src/Tests/Database/InsertTest.php deleted file mode 100644 index f021ff5..0000000 --- a/core/modules/system/src/Tests/Database/InsertTest.php +++ /dev/null @@ -1,198 +0,0 @@ -fetchField(); - - $query = db_insert('test'); - $query->fields(array( - 'name' => 'Yoko', - 'age' => '29', - )); - $query->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - $this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Yoko'))->fetchField(); - $this->assertIdentical($saved_age, '29', 'Can retrieve after inserting.'); - } - - /** - * Tests that we can insert multiple records in one query object. - */ - function testMultiInsert() { - $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - - $query = db_insert('test'); - $query->fields(array( - 'name' => 'Larry', - 'age' => '30', - )); - - // We should be able to specify values in any order if named. - $query->values(array( - 'age' => '31', - 'name' => 'Curly', - )); - - // We should be able to say "use the field order". - // This is not the recommended mechanism for most cases, but it should work. - $query->values(array('Moe', '32')); - $query->execute(); - - $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - $this->assertIdentical($num_records_before + 3, $num_records_after, 'Record inserts correctly.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField(); - $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField(); - $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField(); - $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.'); - } - - /** - * Tests that an insert object can be reused with new data after it executes. - */ - function testRepeatedInsert() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - - $query = db_insert('test'); - - $query->fields(array( - 'name' => 'Larry', - 'age' => '30', - )); - $query->execute(); // This should run the insert, but leave the fields intact. - - // We should be able to specify values in any order if named. - $query->values(array( - 'age' => '31', - 'name' => 'Curly', - )); - $query->execute(); - - // We should be able to say "use the field order". - $query->values(array('Moe', '32')); - $query->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); - $this->assertIdentical((int) $num_records_before + 3, (int) $num_records_after, 'Record inserts correctly.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField(); - $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField(); - $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField(); - $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.'); - } - - /** - * Tests that we can specify fields without values and specify values later. - */ - function testInsertFieldOnlyDefinition() { - // This is useful for importers, when we want to create a query and define - // its fields once, then loop over a multi-insert execution. - db_insert('test') - ->fields(array('name', 'age')) - ->values(array('Larry', '30')) - ->values(array('Curly', '31')) - ->values(array('Moe', '32')) - ->execute(); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField(); - $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField(); - $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField(); - $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.'); - } - - /** - * Tests that inserts return the proper auto-increment ID. - */ - function testInsertLastInsertID() { - $id = db_insert('test') - ->fields(array( - 'name' => 'Larry', - 'age' => '30', - )) - ->execute(); - - $this->assertIdentical($id, '5', 'Auto-increment ID returned successfully.'); - } - - /** - * Tests that the INSERT INTO ... SELECT (fields) ... syntax works. - */ - function testInsertSelectFields() { - $query = db_select('test_people', 'tp'); - // The query builder will always append expressions after fields. - // Add the expression first to test that the insert fields are correctly - // re-ordered. - $query->addExpression('tp.age', 'age'); - $query - ->fields('tp', array('name', 'job')) - ->condition('tp.name', 'Meredith'); - - // The resulting query should be equivalent to: - // INSERT INTO test (age, name, job) - // SELECT tp.age AS age, tp.name AS name, tp.job AS job - // FROM test_people tp - // WHERE tp.name = 'Meredith' - db_insert('test') - ->from($query) - ->execute(); - - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Meredith'))->fetchField(); - $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.'); - } - - /** - * Tests that the INSERT INTO ... SELECT * ... syntax works. - */ - function testInsertSelectAll() { - $query = db_select('test_people', 'tp') - ->fields('tp') - ->condition('tp.name', 'Meredith'); - - // The resulting query should be equivalent to: - // INSERT INTO test_people_copy - // SELECT * - // FROM test_people tp - // WHERE tp.name = 'Meredith' - db_insert('test_people_copy') - ->from($query) - ->execute(); - - $saved_age = db_query('SELECT age FROM {test_people_copy} WHERE name = :name', array(':name' => 'Meredith'))->fetchField(); - $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.'); - } - - /** - * Tests that we can INSERT INTO a special named column. - */ - function testSpecialColumnInsert() { - $id = db_insert('test_special_columns') - ->fields(array( - 'id' => 2, - 'offset' => 'Offset value 2', - )) - ->execute(); - $saved_value = db_query('SELECT "offset" FROM {test_special_columns} WHERE id = :id', array(':id' => 2))->fetchField(); - $this->assertIdentical($saved_value, 'Offset value 2', 'Can retrieve special column name value after inserting.'); - } -} diff --git a/core/modules/system/src/Tests/Database/InvalidDataTest.php b/core/modules/system/src/Tests/Database/InvalidDataTest.php deleted file mode 100644 index 8fcfb23..0000000 --- a/core/modules/system/src/Tests/Database/InvalidDataTest.php +++ /dev/null @@ -1,73 +0,0 @@ -fields(array('name', 'age', 'job')) - ->values(array( - 'name' => 'Elvis', - 'age' => 63, - 'job' => 'Singer', - ))->values(array( - 'name' => 'John', // <-- Duplicate value on unique field. - 'age' => 17, - 'job' => 'Consultant', - )) - ->values(array( - 'name' => 'Frank', - 'age' => 75, - 'job' => 'Singer', - )) - ->execute(); - $this->fail('Insert succeeded when it should not have.'); - } - catch (IntegrityConstraintViolationException $e) { - // Check if the first record was inserted. - $name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 63))->fetchField(); - - if ($name == 'Elvis') { - if (!Database::getConnection()->supportsTransactions()) { - // This is an expected fail. - // Database engines that don't support transactions can leave partial - // inserts in place when an error occurs. This is the case for MySQL - // when running on a MyISAM table. - $this->pass("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions"); - } - else { - $this->fail('The whole transaction is rolled back when a duplicate key insert occurs.'); - } - } - else { - $this->pass('The whole transaction is rolled back when a duplicate key insert occurs.'); - } - - // Ensure the other values were not inserted. - $record = db_select('test') - ->fields('test', array('name', 'age')) - ->condition('age', array(17, 75), 'IN') - ->execute()->fetchObject(); - - $this->assertFalse($record, 'The rest of the insert aborted as expected.'); - } - } - -} diff --git a/core/modules/system/src/Tests/Database/LargeQueryTest.php b/core/modules/system/src/Tests/Database/LargeQueryTest.php deleted file mode 100644 index fab1b8f..0000000 --- a/core/modules/system/src/Tests/Database/LargeQueryTest.php +++ /dev/null @@ -1,58 +0,0 @@ -fetchField(); - if (Environment::checkMemoryLimit($max_allowed_packet + (16 * 1024 * 1024))) { - $long_name = str_repeat('a', $max_allowed_packet + 1); - try { - db_query('SELECT name FROM {test} WHERE name = :name', array(':name' => $long_name)); - $this->fail("An exception should be thrown for queries larger than 'max_allowed_packet'"); - } catch (DatabaseException $e) { - // Close and re-open the connection. Otherwise we will run into error - // 2006 "MySQL server had gone away" afterwards. - Database::closeConnection(); - Database::getConnection(); - $this->assertEqual($e->getPrevious()->errorInfo[1], 1153, "Got a packet bigger than 'max_allowed_packet' bytes exception thrown."); - // Use strlen() to count the bytes exactly, not the unicode chars. - $this->assertTrue(strlen($e->getMessage()) <= $max_allowed_packet, "'max_allowed_packet' exception message truncated."); - } - } - else { - $this->verbose('The configured max_allowed_packet exceeds the php memory limit. Therefore the test is skipped.'); - } - } - else { - $this->verbose('The test requires MySQL. Therefore the test is skipped.'); - } - } - -} diff --git a/core/modules/system/src/Tests/Database/LoggingTest.php b/core/modules/system/src/Tests/Database/LoggingTest.php deleted file mode 100644 index f17f6c5..0000000 --- a/core/modules/system/src/Tests/Database/LoggingTest.php +++ /dev/null @@ -1,133 +0,0 @@ - :age', array(':age' => 25))->fetchCol(); - db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol(); - - // Trigger a call that does not have file in the backtrace. - call_user_func_array('db_query', array('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo')))->fetchCol(); - - $queries = Database::getLog('testing', 'default'); - - $this->assertEqual(count($queries), 3, 'Correct number of queries recorded.'); - - foreach ($queries as $query) { - $this->assertEqual($query['caller']['function'], __FUNCTION__, 'Correct function in query log.'); - } - } - - /** - * Tests that we can run two logs in parallel. - */ - function testEnableMultiLogging() { - Database::startLog('testing1'); - - db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); - - Database::startLog('testing2'); - - db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol(); - - $queries1 = Database::getLog('testing1'); - $queries2 = Database::getLog('testing2'); - - $this->assertEqual(count($queries1), 2, 'Correct number of queries recorded for log 1.'); - $this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for log 2.'); - } - - /** - * Tests logging queries against multiple targets on the same connection. - */ - function testEnableTargetLogging() { - // Clone the primary credentials to a replica connection and to another fake - // connection. - $connection_info = Database::getConnectionInfo('default'); - Database::addConnectionInfo('default', 'replica', $connection_info['default']); - - Database::startLog('testing1'); - - db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); - - db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'replica'));//->fetchCol(); - - $queries1 = Database::getLog('testing1'); - - $this->assertEqual(count($queries1), 2, 'Recorded queries from all targets.'); - $this->assertEqual($queries1[0]['target'], 'default', 'First query used default target.'); - $this->assertEqual($queries1[1]['target'], 'replica', 'Second query used replica target.'); - } - - /** - * Tests that logs to separate targets use the same connection properly. - * - * This test is identical to the one above, except that it doesn't create - * a fake target so the query should fall back to running on the default - * target. - */ - function testEnableTargetLoggingNoTarget() { - Database::startLog('testing1'); - - db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); - - // We use "fake" here as a target because any non-existent target will do. - // However, because all of the tests in this class share a single page - // request there is likely to be a target of "replica" from one of the other - // unit tests, so we use a target here that we know with absolute certainty - // does not exist. - db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'fake'))->fetchCol(); - - $queries1 = Database::getLog('testing1'); - - $this->assertEqual(count($queries1), 2, 'Recorded queries from all targets.'); - $this->assertEqual($queries1[0]['target'], 'default', 'First query used default target.'); - $this->assertEqual($queries1[1]['target'], 'default', 'Second query used default target as fallback.'); - } - - /** - * Tests that we can log queries separately on different connections. - */ - function testEnableMultiConnectionLogging() { - // Clone the primary credentials to a fake connection. - // That both connections point to the same physical database is irrelevant. - $connection_info = Database::getConnectionInfo('default'); - Database::addConnectionInfo('test2', 'default', $connection_info['default']); - - Database::startLog('testing1'); - Database::startLog('testing1', 'test2'); - - db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); - - $old_key = db_set_active('test2'); - - db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'replica'))->fetchCol(); - - db_set_active($old_key); - - $queries1 = Database::getLog('testing1'); - $queries2 = Database::getLog('testing1', 'test2'); - - $this->assertEqual(count($queries1), 1, 'Correct number of queries recorded for first connection.'); - $this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for second connection.'); - } -} diff --git a/core/modules/system/src/Tests/Database/MergeTest.php b/core/modules/system/src/Tests/Database/MergeTest.php deleted file mode 100644 index c4e83bb..0000000 --- a/core/modules/system/src/Tests/Database/MergeTest.php +++ /dev/null @@ -1,237 +0,0 @@ -fetchField(); - - $result = db_merge('test_people') - ->key('job', 'Presenter') - ->fields(array( - 'age' => 31, - 'name' => 'Tiffany', - )) - ->execute(); - - $this->assertEqual($result, Merge::STATUS_INSERT, 'Insert status returned.'); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before + 1, $num_records_after, 'Merge inserted properly.'); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch(); - $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.'); - $this->assertEqual($person->age, 31, 'Age set correctly.'); - $this->assertEqual($person->job, 'Presenter', 'Job set correctly.'); - } - - /** - * Confirms that we can merge-update a record successfully. - */ - function testMergeUpdate() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - $result = db_merge('test_people') - ->key('job', 'Speaker') - ->fields(array( - 'age' => 31, - 'name' => 'Tiffany', - )) - ->execute(); - - $this->assertEqual($result, Merge::STATUS_UPDATE, 'Update status returned.'); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.'); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.'); - $this->assertEqual($person->age, 31, 'Age set correctly.'); - $this->assertEqual($person->job, 'Speaker', 'Job set correctly.'); - } - - /** - * Confirms that we can merge-update a record successfully. - * - * This test varies from the previous test because it manually defines which - * fields are inserted, and which fields are updated. - */ - function testMergeUpdateExcept() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - db_merge('test_people') - ->key('job', 'Speaker') - ->insertFields(array('age' => 31)) - ->updateFields(array('name' => 'Tiffany')) - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.'); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.'); - $this->assertEqual($person->age, 30, 'Age skipped correctly.'); - $this->assertEqual($person->job, 'Speaker', 'Job set correctly.'); - } - - /** - * Confirms that we can merge-update a record, with alternate replacement. - */ - function testMergeUpdateExplicit() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - db_merge('test_people') - ->key('job', 'Speaker') - ->insertFields(array( - 'age' => 31, - 'name' => 'Tiffany', - )) - ->updateFields(array( - 'name' => 'Joe', - )) - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.'); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->name, 'Joe', 'Name set correctly.'); - $this->assertEqual($person->age, 30, 'Age skipped correctly.'); - $this->assertEqual($person->job, 'Speaker', 'Job set correctly.'); - } - - /** - * Confirms that we can merge-update a record successfully, with expressions. - */ - function testMergeUpdateExpression() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - $age_before = db_query('SELECT age FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetchField(); - - // This is a very contrived example, as I have no idea why you'd want to - // change age this way, but that's beside the point. - // Note that we are also double-setting age here, once as a literal and - // once as an expression. This test will only pass if the expression wins, - // which is what is supposed to happen. - db_merge('test_people') - ->key('job', 'Speaker') - ->fields(array('name' => 'Tiffany')) - ->insertFields(array('age' => 31)) - ->expression('age', 'age + :age', array(':age' => 4)) - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.'); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.'); - $this->assertEqual($person->age, $age_before + 4, 'Age updated correctly.'); - $this->assertEqual($person->job, 'Speaker', 'Job set correctly.'); - } - - /** - * Tests that we can merge-insert without any update fields. - */ - function testMergeInsertWithoutUpdate() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - db_merge('test_people') - ->key('job', 'Presenter') - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before + 1, $num_records_after, 'Merge inserted properly.'); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch(); - $this->assertEqual($person->name, '', 'Name set correctly.'); - $this->assertEqual($person->age, 0, 'Age set correctly.'); - $this->assertEqual($person->job, 'Presenter', 'Job set correctly.'); - } - - /** - * Confirms that we can merge-update without any update fields. - */ - function testMergeUpdateWithoutUpdate() { - $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - db_merge('test_people') - ->key('job', 'Speaker') - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after, 'Merge skipped properly.'); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->name, 'Meredith', 'Name skipped correctly.'); - $this->assertEqual($person->age, 30, 'Age skipped correctly.'); - $this->assertEqual($person->job, 'Speaker', 'Job skipped correctly.'); - - db_merge('test_people') - ->key('job', 'Speaker') - ->insertFields(array('age' => 31)) - ->execute(); - - $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before, $num_records_after, 'Merge skipped properly.'); - - $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->name, 'Meredith', 'Name skipped correctly.'); - $this->assertEqual($person->age, 30, 'Age skipped correctly.'); - $this->assertEqual($person->job, 'Speaker', 'Job skipped correctly.'); - } - - /** - * Tests that an invalid merge query throws an exception. - */ - function testInvalidMerge() { - try { - // This query will fail because there is no key field specified. - // Normally it would throw an exception but we are suppressing it with - // the throw_exception option. - $options['throw_exception'] = FALSE; - db_merge('test_people', $options) - ->fields(array( - 'age' => 31, - 'name' => 'Tiffany', - )) - ->execute(); - $this->pass('$options[\'throw_exception\'] is FALSE, no InvalidMergeQueryException thrown.'); - } - catch (InvalidMergeQueryException $e) { - $this->fail('$options[\'throw_exception\'] is FALSE, but InvalidMergeQueryException thrown for invalid query.'); - return; - } - - try { - // This query will fail because there is no key field specified. - db_merge('test_people') - ->fields(array( - 'age' => 31, - 'name' => 'Tiffany', - )) - ->execute(); - } - catch (InvalidMergeQueryException $e) { - $this->pass('InvalidMergeQueryException thrown for invalid query.'); - return; - } - $this->fail('No InvalidMergeQueryException thrown'); - } -} diff --git a/core/modules/system/src/Tests/Database/NextIdTest.php b/core/modules/system/src/Tests/Database/NextIdTest.php deleted file mode 100644 index cabbe16..0000000 --- a/core/modules/system/src/Tests/Database/NextIdTest.php +++ /dev/null @@ -1,43 +0,0 @@ -installSchema('system', 'sequences'); - } - - /** - * Tests that the sequences API works. - */ - function testDbNextId() { - $first = db_next_id(); - $second = db_next_id(); - // We can test for exact increase in here because we know there is no - // other process operating on these tables -- normally we could only - // expect $second > $first. - $this->assertEqual($first + 1, $second, 'The second call from a sequence provides a number increased by one.'); - $result = db_next_id(1000); - $this->assertEqual($result, 1001, 'Sequence provides a larger number than the existing ID.'); - } -} diff --git a/core/modules/system/src/Tests/Database/QueryTest.php b/core/modules/system/src/Tests/Database/QueryTest.php deleted file mode 100644 index dd613c5..0000000 --- a/core/modules/system/src/Tests/Database/QueryTest.php +++ /dev/null @@ -1,153 +0,0 @@ - array(25, 26, 27)))->fetchAll(); - $this->assertEqual(count($names), 3, 'Correct number of names returned'); - - $names = db_query('SELECT name FROM {test} WHERE age IN ( :ages[] ) ORDER BY age', array(':ages[]' => array(25)))->fetchAll(); - $this->assertEqual(count($names), 1, 'Correct number of names returned'); - } - - /** - * Tests that we can not pass a scalar value when an array is expected. - */ - function testScalarSubstitution() { - try { - $names = db_query('SELECT name FROM {test} WHERE age IN ( :ages[] ) ORDER BY age', array(':ages[]' => 25))->fetchAll(); - $this->fail('Array placeholder with scalar argument should result in an exception.'); - } - catch (\InvalidArgumentException $e) { - $this->pass('Array placeholder with scalar argument should result in an exception.'); - } - - } - - /** - * Tests SQL injection via database query array arguments. - */ - public function testArrayArgumentsSQLInjection() { - // Attempt SQL injection and verify that it does not work. - $condition = array( - "1 ;INSERT INTO {test} (name) VALUES ('test12345678'); -- " => '', - '1' => '', - ); - try { - db_query("SELECT * FROM {test} WHERE name = :name", array(':name' => $condition))->fetchObject(); - $this->fail('SQL injection attempt via array arguments should result in a database exception.'); - } - catch (\InvalidArgumentException $e) { - $this->pass('SQL injection attempt via array arguments should result in a database exception.'); - } - - // Test that the insert query that was used in the SQL injection attempt did - // not result in a row being inserted in the database. - $result = db_select('test') - ->condition('name', 'test12345678') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertFalse($result, 'SQL injection attempt did not result in a row being inserted in the database table.'); - } - - /** - * Tests SQL injection via condition operator. - */ - public function testConditionOperatorArgumentsSQLInjection() { - $injection = "IS NOT NULL) ;INSERT INTO {test} (name) VALUES ('test12345678'); -- "; - - // Convert errors to exceptions for testing purposes below. - set_error_handler(function ($severity, $message, $filename, $lineno) { - throw new \ErrorException($message, 0, $severity, $filename, $lineno); - }); - try { - $result = db_select('test', 't') - ->fields('t') - ->condition('name', 1, $injection) - ->execute(); - $this->fail('Should not be able to attempt SQL injection via condition operator.'); - } - catch (\ErrorException $e) { - $this->pass('SQL injection attempt via condition arguments should result in a database exception.'); - } - - // Test that the insert query that was used in the SQL injection attempt did - // not result in a row being inserted in the database. - $result = db_select('test') - ->condition('name', 'test12345678') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertFalse($result, 'SQL injection attempt did not result in a row being inserted in the database table.'); - - // Attempt SQLi via union query with no unsafe characters. - $this->enableModules(['user']); - $this->installEntitySchema('user'); - db_insert('test') - ->fields(['name' => '123456']) - ->execute(); - $injection = "= 1 UNION ALL SELECT password FROM user WHERE uid ="; - - try { - $result = db_select('test', 't') - ->fields('t', array('name', 'name')) - ->condition('name', 1, $injection) - ->execute(); - $this->fail('Should not be able to attempt SQL injection via operator.'); - } - catch (\ErrorException $e) { - $this->pass('SQL injection attempt via condition arguments should result in a database exception.'); - } - - // Attempt SQLi via union query - uppercase tablename. - db_insert('TEST_UPPERCASE') - ->fields(['name' => 'secrets']) - ->execute(); - $injection = "IS NOT NULL) UNION ALL SELECT name FROM {TEST_UPPERCASE} -- "; - - try { - $result = db_select('test', 't') - ->fields('t', array('name')) - ->condition('name', 1, $injection) - ->execute(); - $this->fail('Should not be able to attempt SQL injection via operator.'); - } - catch (\ErrorException $e) { - $this->pass('SQL injection attempt via condition arguments should result in a database exception.'); - } - restore_error_handler(); - } - - /** - * Tests numeric query parameter expansion in expressions. - * - * @see \Drupal\Core\Database\Driver\sqlite\Statement::getStatement() - * @see http://bugs.php.net/bug.php?id=45259 - */ - public function testNumericExpressionSubstitution() { - $count = db_query('SELECT COUNT(*) >= 3 FROM {test}')->fetchField(); - $this->assertEqual((bool) $count, TRUE); - - $count = db_query('SELECT COUNT(*) >= :count FROM {test}', array( - ':count' => 3, - ))->fetchField(); - $this->assertEqual((bool) $count, TRUE); - } - -} diff --git a/core/modules/system/src/Tests/Database/RangeQueryTest.php b/core/modules/system/src/Tests/Database/RangeQueryTest.php deleted file mode 100644 index 13b10c1..0000000 --- a/core/modules/system/src/Tests/Database/RangeQueryTest.php +++ /dev/null @@ -1,37 +0,0 @@ -fetchAll(); - $this->assertEqual(count($range_rows), 3, 'Range query work and return correct number of rows.'); - - // Test if return target data. - $raw_rows = db_query('SELECT name FROM {test} ORDER BY name')->fetchAll(); - $raw_rows = array_slice($raw_rows, 1, 3); - $this->assertEqual($range_rows, $raw_rows); - } -} diff --git a/core/modules/system/src/Tests/Database/RegressionTest.php b/core/modules/system/src/Tests/Database/RegressionTest.php deleted file mode 100644 index c6dbc06..0000000 --- a/core/modules/system/src/Tests/Database/RegressionTest.php +++ /dev/null @@ -1,64 +0,0 @@ -fields(array( - 'name' => $this->randomMachineName(), - 'age' => 20, - 'job' => $job, - ))->execute(); - - $from_database = db_query('SELECT job FROM {test} WHERE job = :job', array(':job' => $job))->fetchField(); - $this->assertIdentical($job, $from_database, 'The database handles UTF-8 characters cleanly.'); - } - - /** - * Tests the db_table_exists() function. - */ - function testDBTableExists() { - $this->assertIdentical(TRUE, db_table_exists('test'), 'Returns true for existent table.'); - $this->assertIdentical(FALSE, db_table_exists('nosuchtable'), 'Returns false for nonexistent table.'); - } - - /** - * Tests the db_field_exists() function. - */ - function testDBFieldExists() { - $this->assertIdentical(TRUE, db_field_exists('test', 'name'), 'Returns true for existent column.'); - $this->assertIdentical(FALSE, db_field_exists('test', 'nosuchcolumn'), 'Returns false for nonexistent column.'); - } - - /** - * Tests the db_index_exists() function. - */ - function testDBIndexExists() { - $this->assertIdentical(TRUE, db_index_exists('test', 'ages'), 'Returns true for existent index.'); - $this->assertIdentical(FALSE, db_index_exists('test', 'nosuchindex'), 'Returns false for nonexistent index.'); - } -} diff --git a/core/modules/system/src/Tests/Database/SchemaTest.php b/core/modules/system/src/Tests/Database/SchemaTest.php deleted file mode 100644 index 716a6a1..0000000 --- a/core/modules/system/src/Tests/Database/SchemaTest.php +++ /dev/null @@ -1,796 +0,0 @@ - 'Schema table description may contain "quotes" and could be long—very long indeed.', - 'fields' => array( - 'id' => array( - 'type' => 'int', - 'default' => NULL, - ), - 'test_field' => array( - 'type' => 'int', - 'not null' => TRUE, - 'description' => 'Schema table description may contain "quotes" and could be long—very long indeed. There could be "multiple quoted regions".', - ), - 'test_field_string' => array( - 'type' => 'varchar', - 'length' => 20, - 'not null' => TRUE, - 'default' => "'\"funky default'\"", - 'description' => 'Schema column description for string.', - ), - 'test_field_string_ascii' => array( - 'type' => 'varchar_ascii', - 'length' => 255, - 'description' => 'Schema column description for ASCII string.', - ), - ), - ); - db_create_table('test_table', $table_specification); - - // Assert that the table exists. - $this->assertTrue(db_table_exists('test_table'), 'The table exists.'); - - // Assert that the table comment has been set. - $this->checkSchemaComment($table_specification['description'], 'test_table'); - - // Assert that the column comment has been set. - $this->checkSchemaComment($table_specification['fields']['test_field']['description'], 'test_table', 'test_field'); - - if (Database::getConnection()->databaseType() == 'mysql') { - // Make sure that varchar fields have the correct collation. - $columns = db_query('SHOW FULL COLUMNS FROM {test_table}'); - foreach ($columns as $column) { - if ($column->Field == 'test_field_string') { - $string_check = ($column->Collation == 'utf8mb4_general_ci'); - } - if ($column->Field == 'test_field_string_ascii') { - $string_ascii_check = ($column->Collation == 'ascii_general_ci'); - } - } - $this->assertTrue(!empty($string_check), 'string field has the right collation.'); - $this->assertTrue(!empty($string_ascii_check), 'ASCII string field has the right collation.'); - } - - // An insert without a value for the column 'test_table' should fail. - $this->assertFalse($this->tryInsert(), 'Insert without a default failed.'); - - // Add a default value to the column. - db_field_set_default('test_table', 'test_field', 0); - // The insert should now succeed. - $this->assertTrue($this->tryInsert(), 'Insert with a default succeeded.'); - - // Remove the default. - db_field_set_no_default('test_table', 'test_field'); - // The insert should fail again. - $this->assertFalse($this->tryInsert(), 'Insert without a default failed.'); - - // Test for fake index and test for the boolean result of indexExists(). - $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field'); - $this->assertIdentical($index_exists, FALSE, 'Fake index does not exists'); - // Add index. - db_add_index('test_table', 'test_field', array('test_field'), $table_specification); - // Test for created index and test for the boolean result of indexExists(). - $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field'); - $this->assertIdentical($index_exists, TRUE, 'Index created.'); - - // Rename the table. - db_rename_table('test_table', 'test_table2'); - - // Index should be renamed. - $index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field'); - $this->assertTrue($index_exists, 'Index was renamed.'); - - // We need the default so that we can insert after the rename. - db_field_set_default('test_table2', 'test_field', 0); - $this->assertFalse($this->tryInsert(), 'Insert into the old table failed.'); - $this->assertTrue($this->tryInsert('test_table2'), 'Insert into the new table succeeded.'); - - // We should have successfully inserted exactly two rows. - $count = db_query('SELECT COUNT(*) FROM {test_table2}')->fetchField(); - $this->assertEqual($count, 2, 'Two fields were successfully inserted.'); - - // Try to drop the table. - db_drop_table('test_table2'); - $this->assertFalse(db_table_exists('test_table2'), 'The dropped table does not exist.'); - - // Recreate the table. - db_create_table('test_table', $table_specification); - db_field_set_default('test_table', 'test_field', 0); - db_add_field('test_table', 'test_serial', array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Added column description.')); - - // Assert that the column comment has been set. - $this->checkSchemaComment('Added column description.', 'test_table', 'test_serial'); - - // Change the new field to a serial column. - db_change_field('test_table', 'test_serial', 'test_serial', array('type' => 'serial', 'not null' => TRUE, 'description' => 'Changed column description.'), array('primary key' => array('test_serial'))); - - // Assert that the column comment has been set. - $this->checkSchemaComment('Changed column description.', 'test_table', 'test_serial'); - - $this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.'); - $max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField(); - $this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.'); - $max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField(); - $this->assertTrue($max2 > $max1, 'The serial is monotone.'); - - $count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField(); - $this->assertEqual($count, 2, 'There were two rows.'); - - // Test renaming of keys and constraints. - db_drop_table('test_table'); - $table_specification = array( - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'not null' => TRUE, - ), - 'test_field' => array( - 'type' => 'int', - 'default' => 0, - ), - ), - 'primary key' => array('id'), - 'unique keys' => array( - 'test_field' => array('test_field'), - ), - ); - db_create_table('test_table', $table_specification); - - // Tests for indexes are Database specific. - $db_type = Database::getConnection()->databaseType(); - - // Test for existing primary and unique keys. - switch ($db_type) { - case 'pgsql': - $primary_key_exists = Database::getConnection()->schema()->constraintExists('test_table', '__pkey'); - $unique_key_exists = Database::getConnection()->schema()->constraintExists('test_table', 'test_field' . '__key'); - break; - case 'sqlite': - // SQLite does not create a standalone index for primary keys. - $primary_key_exists = TRUE; - $unique_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field'); - break; - default: - $primary_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'PRIMARY'); - $unique_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field'); - break; - } - $this->assertIdentical($primary_key_exists, TRUE, 'Primary key created.'); - $this->assertIdentical($unique_key_exists, TRUE, 'Unique key created.'); - - db_rename_table('test_table', 'test_table2'); - - // Test for renamed primary and unique keys. - switch ($db_type) { - case 'pgsql': - $renamed_primary_key_exists = Database::getConnection()->schema()->constraintExists('test_table2', '__pkey'); - $renamed_unique_key_exists = Database::getConnection()->schema()->constraintExists('test_table2', 'test_field' . '__key'); - break; - case 'sqlite': - // SQLite does not create a standalone index for primary keys. - $renamed_primary_key_exists = TRUE; - $renamed_unique_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field'); - break; - default: - $renamed_primary_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'PRIMARY'); - $renamed_unique_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field'); - break; - } - $this->assertIdentical($renamed_primary_key_exists, TRUE, 'Primary key was renamed.'); - $this->assertIdentical($renamed_unique_key_exists, TRUE, 'Unique key was renamed.'); - - // For PostgreSQL check in addition that sequence was renamed. - if ($db_type == 'pgsql') { - // Get information about new table. - $info = Database::getConnection()->schema()->queryTableInformation('test_table2'); - $sequence_name = Database::getConnection()->schema()->prefixNonTable('test_table2', 'id', 'seq'); - $this->assertEqual($sequence_name, current($info->sequences), 'Sequence was renamed.'); - } - - // Use database specific data type and ensure that table is created. - $table_specification = array( - 'description' => 'Schema table description.', - 'fields' => array( - 'timestamp' => array( - 'mysql_type' => 'timestamp', - 'pgsql_type' => 'timestamp', - 'sqlite_type' => 'datetime', - 'not null' => FALSE, - 'default' => NULL, - ), - ), - ); - try { - db_create_table('test_timestamp', $table_specification); - } - catch (\Exception $e) {} - $this->assertTrue(db_table_exists('test_timestamp'), 'Table with database specific datatype was created.'); - } - - /** - * Tests that indexes on string fields are limited to 191 characters on MySQL. - * - * @see \Drupal\Core\Database\Driver\mysql\Schema::getNormalizedIndexes() - */ - function testIndexLength() { - if (Database::getConnection()->databaseType() != 'mysql') { - return; - } - $table_specification = array( - 'fields' => array( - 'id' => array( - 'type' => 'int', - 'default' => NULL, - ), - 'test_field_text' => array( - 'type' => 'text', - 'not null' => TRUE, - ), - 'test_field_string_long' => array( - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - ), - 'test_field_string_ascii_long' => array( - 'type' => 'varchar_ascii', - 'length' => 255, - ), - 'test_field_string_short' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - ), - ), - 'indexes' => array( - 'test_regular' => array( - 'test_field_text', - 'test_field_string_long', - 'test_field_string_ascii_long', - 'test_field_string_short', - ), - 'test_length' => array( - array('test_field_text', 128), - array('test_field_string_long', 128), - array('test_field_string_ascii_long', 128), - array('test_field_string_short', 128), - ), - 'test_mixed' => array( - array('test_field_text', 200), - 'test_field_string_long', - array('test_field_string_ascii_long', 200), - 'test_field_string_short', - ), - ), - ); - db_create_table('test_table_index_length', $table_specification); - - $schema_object = Database::getConnection()->schema(); - - // Ensure expected exception thrown when adding index with missing info. - $expected_exception_message = "MySQL needs the 'test_field_text' field specification in order to normalize the 'test_regular' index"; - $missing_field_spec = $table_specification; - unset($missing_field_spec['fields']['test_field_text']); - try { - $schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $missing_field_spec); - $this->fail('SchemaException not thrown when adding index with missing information.'); - } - catch (SchemaException $e) { - $this->assertEqual($expected_exception_message, $e->getMessage()); - } - - // Add a separate index. - $schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification); - $table_specification_with_new_index = $table_specification; - $table_specification_with_new_index['indexes']['test_separate'] = [['test_field_text', 200]]; - - // Ensure that the exceptions of addIndex are thrown as expected. - - try { - $schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification); - $this->fail('\Drupal\Core\Database\SchemaObjectExistsException exception missed.'); - } - catch (SchemaObjectExistsException $e) { - $this->pass('\Drupal\Core\Database\SchemaObjectExistsException thrown when index already exists.'); - } - - try { - $schema_object->addIndex('test_table_non_existing', 'test_separate', [['test_field_text', 200]], $table_specification); - $this->fail('\Drupal\Core\Database\SchemaObjectDoesNotExistException exception missed.'); - } - catch (SchemaObjectDoesNotExistException $e) { - $this->pass('\Drupal\Core\Database\SchemaObjectDoesNotExistException thrown when index already exists.'); - } - - // Get index information. - $results = db_query('SHOW INDEX FROM {test_table_index_length}'); - $expected_lengths = array( - 'test_regular' => array( - 'test_field_text' => 191, - 'test_field_string_long' => 191, - 'test_field_string_ascii_long' => NULL, - 'test_field_string_short' => NULL, - ), - 'test_length' => array( - 'test_field_text' => 128, - 'test_field_string_long' => 128, - 'test_field_string_ascii_long' => 128, - 'test_field_string_short' => NULL, - ), - 'test_mixed' => array( - 'test_field_text' => 191, - 'test_field_string_long' => 191, - 'test_field_string_ascii_long' => 200, - 'test_field_string_short' => NULL, - ), - 'test_separate' => array( - 'test_field_text' => 191, - ), - ); - - // Count the number of columns defined in the indexes. - $column_count = 0; - foreach ($table_specification_with_new_index['indexes'] as $index) { - foreach ($index as $field) { - $column_count++; - } - } - $test_count = 0; - foreach ($results as $result) { - $this->assertEqual($result->Sub_part, $expected_lengths[$result->Key_name][$result->Column_name], 'Index length matches expected value.'); - $test_count++; - } - $this->assertEqual($test_count, $column_count, 'Number of tests matches expected value.'); - } - - /** - * Tests inserting data into an existing table. - * - * @param $table - * The database table to insert data into. - * - * @return - * TRUE if the insert succeeded, FALSE otherwise. - */ - function tryInsert($table = 'test_table') { - try { - db_insert($table) - ->fields(array('id' => mt_rand(10, 20))) - ->execute(); - return TRUE; - } - catch (\Exception $e) { - return FALSE; - } - } - - /** - * Checks that a table or column comment matches a given description. - * - * @param $description - * The asserted description. - * @param $table - * The table to test. - * @param $column - * Optional column to test. - */ - function checkSchemaComment($description, $table, $column = NULL) { - if (method_exists(Database::getConnection()->schema(), 'getComment')) { - $comment = Database::getConnection()->schema()->getComment($table, $column); - // The schema comment truncation for mysql is different. - if (Database::getConnection()->databaseType() == 'mysql') { - $max_length = $column ? 255 : 60; - $description = Unicode::truncate($description, $max_length, TRUE, TRUE); - } - $this->assertEqual($comment, $description, 'The comment matches the schema description.'); - } - } - - /** - * Tests creating unsigned columns and data integrity thereof. - */ - function testUnsignedColumns() { - // First create the table with just a serial column. - $table_name = 'unsigned_table'; - $table_spec = array( - 'fields' => array('serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE)), - 'primary key' => array('serial_column'), - ); - db_create_table($table_name, $table_spec); - - // Now set up columns for the other types. - $types = array('int', 'float', 'numeric'); - foreach ($types as $type) { - $column_spec = array('type' => $type, 'unsigned'=> TRUE); - if ($type == 'numeric') { - $column_spec += array('precision' => 10, 'scale' => 0); - } - $column_name = $type . '_column'; - $table_spec['fields'][$column_name] = $column_spec; - db_add_field($table_name, $column_name, $column_spec); - } - - // Finally, check each column and try to insert invalid values into them. - foreach ($table_spec['fields'] as $column_name => $column_spec) { - $this->assertTrue(db_field_exists($table_name, $column_name), format_string('Unsigned @type column was created.', array('@type' => $column_spec['type']))); - $this->assertFalse($this->tryUnsignedInsert($table_name, $column_name), format_string('Unsigned @type column rejected a negative value.', array('@type' => $column_spec['type']))); - } - } - - /** - * Tries to insert a negative value into columns defined as unsigned. - * - * @param $table_name - * The table to insert. - * @param $column_name - * The column to insert. - * - * @return - * TRUE if the insert succeeded, FALSE otherwise. - */ - function tryUnsignedInsert($table_name, $column_name) { - try { - db_insert($table_name) - ->fields(array($column_name => -1)) - ->execute(); - return TRUE; - } - catch (\Exception $e) { - return FALSE; - } - } - - /** - * Tests adding columns to an existing table. - */ - function testSchemaAddField() { - // Test varchar types. - foreach (array(1, 32, 128, 256, 512) as $length) { - $base_field_spec = array( - 'type' => 'varchar', - 'length' => $length, - ); - $variations = array( - array('not null' => FALSE), - array('not null' => FALSE, 'default' => '7'), - array('not null' => FALSE, 'default' => substr('"thing"', 0, $length)), - array('not null' => FALSE, 'default' => substr("\"'hing", 0, $length)), - array('not null' => TRUE, 'initial' => 'd'), - array('not null' => FALSE, 'default' => NULL), - array('not null' => TRUE, 'initial' => 'd', 'default' => '7'), - ); - - foreach ($variations as $variation) { - $field_spec = $variation + $base_field_spec; - $this->assertFieldAdditionRemoval($field_spec); - } - } - - // Test int and float types. - foreach (array('int', 'float') as $type) { - foreach (array('tiny', 'small', 'medium', 'normal', 'big') as $size) { - $base_field_spec = array( - 'type' => $type, - 'size' => $size, - ); - $variations = array( - array('not null' => FALSE), - array('not null' => FALSE, 'default' => 7), - array('not null' => TRUE, 'initial' => 1), - array('not null' => TRUE, 'initial' => 1, 'default' => 7), - ); - - foreach ($variations as $variation) { - $field_spec = $variation + $base_field_spec; - $this->assertFieldAdditionRemoval($field_spec); - } - } - } - - // Test numeric types. - foreach (array(1, 5, 10, 40, 65) as $precision) { - foreach (array(0, 2, 10, 30) as $scale) { - // Skip combinations where precision is smaller than scale. - if ($precision <= $scale) { - continue; - } - - $base_field_spec = array( - 'type' => 'numeric', - 'scale' => $scale, - 'precision' => $precision, - ); - $variations = array( - array('not null' => FALSE), - array('not null' => FALSE, 'default' => 7), - array('not null' => TRUE, 'initial' => 1), - array('not null' => TRUE, 'initial' => 1, 'default' => 7), - ); - - foreach ($variations as $variation) { - $field_spec = $variation + $base_field_spec; - $this->assertFieldAdditionRemoval($field_spec); - } - } - } - } - - /** - * Asserts that a given field can be added and removed from a table. - * - * The addition test covers both defining a field of a given specification - * when initially creating at table and extending an existing table. - * - * @param $field_spec - * The schema specification of the field. - */ - protected function assertFieldAdditionRemoval($field_spec) { - // Try creating the field on a new table. - $table_name = 'test_table_' . ($this->counter++); - $table_spec = array( - 'fields' => array( - 'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), - 'test_field' => $field_spec, - ), - 'primary key' => array('serial_column'), - ); - db_create_table($table_name, $table_spec); - $this->pass(format_string('Table %table created.', array('%table' => $table_name))); - - // Check the characteristics of the field. - $this->assertFieldCharacteristics($table_name, 'test_field', $field_spec); - - // Clean-up. - db_drop_table($table_name); - - // Try adding a field to an existing table. - $table_name = 'test_table_' . ($this->counter++); - $table_spec = array( - 'fields' => array( - 'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), - ), - 'primary key' => array('serial_column'), - ); - db_create_table($table_name, $table_spec); - $this->pass(format_string('Table %table created.', array('%table' => $table_name))); - - // Insert some rows to the table to test the handling of initial values. - for ($i = 0; $i < 3; $i++) { - db_insert($table_name) - ->useDefaults(array('serial_column')) - ->execute(); - } - - db_add_field($table_name, 'test_field', $field_spec); - $this->pass(format_string('Column %column created.', array('%column' => 'test_field'))); - - // Check the characteristics of the field. - $this->assertFieldCharacteristics($table_name, 'test_field', $field_spec); - - // Clean-up. - db_drop_field($table_name, 'test_field'); - - // Add back the field and then try to delete a field which is also a primary - // key. - db_add_field($table_name, 'test_field', $field_spec); - db_drop_field($table_name, 'serial_column'); - db_drop_table($table_name); - } - - /** - * Asserts that a newly added field has the correct characteristics. - */ - protected function assertFieldCharacteristics($table_name, $field_name, $field_spec) { - // Check that the initial value has been registered. - if (isset($field_spec['initial'])) { - // There should be no row with a value different then $field_spec['initial']. - $count = db_select($table_name) - ->fields($table_name, array('serial_column')) - ->condition($field_name, $field_spec['initial'], '<>') - ->countQuery() - ->execute() - ->fetchField(); - $this->assertEqual($count, 0, 'Initial values filled out.'); - } - - // Check that the default value has been registered. - if (isset($field_spec['default'])) { - // Try inserting a row, and check the resulting value of the new column. - $id = db_insert($table_name) - ->useDefaults(array('serial_column')) - ->execute(); - $field_value = db_select($table_name) - ->fields($table_name, array($field_name)) - ->condition('serial_column', $id) - ->execute() - ->fetchField(); - $this->assertEqual($field_value, $field_spec['default'], 'Default value registered.'); - } - } - - /** - * Tests changing columns between types. - */ - function testSchemaChangeField() { - $field_specs = array( - array('type' => 'int', 'size' => 'normal', 'not null' => FALSE), - array('type' => 'int', 'size' => 'normal', 'not null' => TRUE, 'initial' => 1, 'default' => 17), - array('type' => 'float', 'size' => 'normal', 'not null' => FALSE), - array('type' => 'float', 'size' => 'normal', 'not null' => TRUE, 'initial' => 1, 'default' => 7.3), - array('type' => 'numeric', 'scale' => 2, 'precision' => 10, 'not null' => FALSE), - array('type' => 'numeric', 'scale' => 2, 'precision' => 10, 'not null' => TRUE, 'initial' => 1, 'default' => 7), - ); - - foreach ($field_specs as $i => $old_spec) { - foreach ($field_specs as $j => $new_spec) { - if ($i === $j) { - // Do not change a field into itself. - continue; - } - $this->assertFieldChange($old_spec, $new_spec); - } - } - - $field_specs = array( - array('type' => 'varchar_ascii', 'length' => '255'), - array('type' => 'varchar', 'length' => '255'), - array('type' => 'text'), - array('type' => 'blob', 'size' => 'big'), - ); - - foreach ($field_specs as $i => $old_spec) { - foreach ($field_specs as $j => $new_spec) { - if ($i === $j) { - // Do not change a field into itself. - continue; - } - // Note if the serialized data contained an object this would fail on - // Postgres. - // @see https://www.drupal.org/node/1031122 - $this->assertFieldChange($old_spec, $new_spec, serialize(['string' => "This \n has \\\\ some backslash \"*string action.\\n"])); - } - } - - } - - /** - * Asserts that a field can be changed from one spec to another. - * - * @param $old_spec - * The beginning field specification. - * @param $new_spec - * The ending field specification. - */ - protected function assertFieldChange($old_spec, $new_spec, $test_data = NULL) { - $table_name = 'test_table_' . ($this->counter++); - $table_spec = array( - 'fields' => array( - 'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), - 'test_field' => $old_spec, - ), - 'primary key' => array('serial_column'), - ); - db_create_table($table_name, $table_spec); - $this->pass(format_string('Table %table created.', array('%table' => $table_name))); - - // Check the characteristics of the field. - $this->assertFieldCharacteristics($table_name, 'test_field', $old_spec); - - // Remove inserted rows. - db_truncate($table_name)->execute(); - - if ($test_data) { - $id = db_insert($table_name) - ->fields(['test_field'], [$test_data]) - ->execute(); - } - - // Change the field. - db_change_field($table_name, 'test_field', 'test_field', $new_spec); - - if ($test_data) { - $field_value = db_select($table_name) - ->fields($table_name, ['test_field']) - ->condition('serial_column', $id) - ->execute() - ->fetchField(); - $this->assertIdentical($field_value, $test_data); - } - - // Check the field was changed. - $this->assertFieldCharacteristics($table_name, 'test_field', $new_spec); - - // Clean-up. - db_drop_table($table_name); - } - - /** - * Tests the findTables() method. - */ - public function testFindTables() { - // We will be testing with three tables, two of them using the default - // prefix and the third one with an individually specified prefix. - - // Set up a new connection with different connection info. - $connection_info = Database::getConnectionInfo(); - - // Add per-table prefix to the second table. - $new_connection_info = $connection_info['default']; - $new_connection_info['prefix']['test_2_table'] = $new_connection_info['prefix']['default'] . '_shared_'; - Database::addConnectionInfo('test', 'default', $new_connection_info); - - Database::setActiveConnection('test'); - - // Create the tables. - $table_specification = [ - 'description' => 'Test table.', - 'fields' => [ - 'id' => [ - 'type' => 'int', - 'default' => NULL, - ], - ], - ]; - Database::getConnection()->schema()->createTable('test_1_table', $table_specification); - Database::getConnection()->schema()->createTable('test_2_table', $table_specification); - Database::getConnection()->schema()->createTable('the_third_table', $table_specification); - - // Check the "all tables" syntax. - $tables = Database::getConnection()->schema()->findTables('%'); - sort($tables); - $expected = [ - // The 'config' table is added by - // \Drupal\simpletest\KernelTestBase::containerBuild(). - 'config', - 'test_1_table', - // This table uses a per-table prefix, yet it is returned as un-prefixed. - 'test_2_table', - 'the_third_table', - ]; - $this->assertEqual($tables, $expected, 'All tables were found.'); - - // Check the restrictive syntax. - $tables = Database::getConnection()->schema()->findTables('test_%'); - sort($tables); - $expected = [ - 'test_1_table', - 'test_2_table', - ]; - $this->assertEqual($tables, $expected, 'Two tables were found.'); - - // Go back to the initial connection. - Database::setActiveConnection('default'); - } - -} diff --git a/core/modules/system/src/Tests/Database/SelectCloneTest.php b/core/modules/system/src/Tests/Database/SelectCloneTest.php deleted file mode 100644 index ad13685..0000000 --- a/core/modules/system/src/Tests/Database/SelectCloneTest.php +++ /dev/null @@ -1,41 +0,0 @@ -addField('t', 'id', 'id'); - $subquery->condition('age', 28, '<'); - - $query = db_select('test', 't'); - $query->addField('t', 'name', 'name'); - $query->condition('id', $subquery, 'IN'); - - $clone = clone $query; - // Cloned query should not be altered by the following modification - // happening on original query. - $subquery->condition('age', 25, '>'); - - $clone_result = $clone->countQuery()->execute()->fetchField(); - $query_result = $query->countQuery()->execute()->fetchField(); - - // Make sure the cloned query has not been modified - $this->assertEqual(3, $clone_result, 'The cloned query returns the expected number of rows'); - $this->assertEqual(2, $query_result, 'The query returns the expected number of rows'); - } -} diff --git a/core/modules/system/src/Tests/Database/SelectComplexTest.php b/core/modules/system/src/Tests/Database/SelectComplexTest.php deleted file mode 100644 index 880c6eb..0000000 --- a/core/modules/system/src/Tests/Database/SelectComplexTest.php +++ /dev/null @@ -1,382 +0,0 @@ -join('test', 'p', 't.pid = p.id'); - $name_field = $query->addField($people_alias, 'name', 'name'); - $query->addField('t', 'task', 'task'); - $priority_field = $query->addField('t', 'priority', 'priority'); - - $query->orderBy($priority_field); - $result = $query->execute(); - - $num_records = 0; - $last_priority = 0; - foreach ($result as $record) { - $num_records++; - $this->assertTrue($record->$priority_field >= $last_priority, 'Results returned in correct order.'); - $this->assertNotEqual($record->$name_field, 'Ringo', 'Taskless person not selected.'); - $last_priority = $record->$priority_field; - } - - $this->assertEqual($num_records, 7, 'Returned the correct number of rows.'); - } - - /** - * Tests LEFT OUTER joins. - */ - function testLeftOuterJoin() { - $query = db_select('test', 'p'); - $people_alias = $query->leftJoin('test_task', 't', 't.pid = p.id'); - $name_field = $query->addField('p', 'name', 'name'); - $query->addField($people_alias, 'task', 'task'); - $query->addField($people_alias, 'priority', 'priority'); - - $query->orderBy($name_field); - $result = $query->execute(); - - $num_records = 0; - $last_name = 0; - - foreach ($result as $record) { - $num_records++; - $this->assertTrue(strcmp($record->$name_field, $last_name) >= 0, 'Results returned in correct order.'); - } - - $this->assertEqual($num_records, 8, 'Returned the correct number of rows.'); - } - - /** - * Tests GROUP BY clauses. - */ - function testGroupBy() { - $query = db_select('test_task', 't'); - $count_field = $query->addExpression('COUNT(task)', 'num'); - $task_field = $query->addField('t', 'task'); - $query->orderBy($count_field); - $query->groupBy($task_field); - $result = $query->execute(); - - $num_records = 0; - $last_count = 0; - $records = array(); - foreach ($result as $record) { - $num_records++; - $this->assertTrue($record->$count_field >= $last_count, 'Results returned in correct order.'); - $last_count = $record->$count_field; - $records[$record->$task_field] = $record->$count_field; - } - - $correct_results = array( - 'eat' => 1, - 'sleep' => 2, - 'code' => 1, - 'found new band' => 1, - 'perform at superbowl' => 1, - ); - - foreach ($correct_results as $task => $count) { - $this->assertEqual($records[$task], $count, format_string("Correct number of '@task' records found.", array('@task' => $task))); - } - - $this->assertEqual($num_records, 6, 'Returned the correct number of total rows.'); - } - - /** - * Tests GROUP BY and HAVING clauses together. - */ - function testGroupByAndHaving() { - $query = db_select('test_task', 't'); - $count_field = $query->addExpression('COUNT(task)', 'num'); - $task_field = $query->addField('t', 'task'); - $query->orderBy($count_field); - $query->groupBy($task_field); - $query->having('COUNT(task) >= 2'); - $result = $query->execute(); - - $num_records = 0; - $last_count = 0; - $records = array(); - foreach ($result as $record) { - $num_records++; - $this->assertTrue($record->$count_field >= 2, 'Record has the minimum count.'); - $this->assertTrue($record->$count_field >= $last_count, 'Results returned in correct order.'); - $last_count = $record->$count_field; - $records[$record->$task_field] = $record->$count_field; - } - - $correct_results = array( - 'sleep' => 2, - ); - - foreach ($correct_results as $task => $count) { - $this->assertEqual($records[$task], $count, format_string("Correct number of '@task' records found.", array('@task' => $task))); - } - - $this->assertEqual($num_records, 1, 'Returned the correct number of total rows.'); - } - - /** - * Tests range queries. - * - * The SQL clause varies with the database. - */ - function testRange() { - $query = db_select('test'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - $query->range(0, 2); - $query_result = $query->countQuery()->execute()->fetchField(); - - $this->assertEqual($query_result, 2, 'Returned the correct number of rows.'); - } - - /** - * Tests distinct queries. - */ - function testDistinct() { - $query = db_select('test_task'); - $query->addField('test_task', 'task'); - $query->distinct(); - $query_result = $query->countQuery()->execute()->fetchField(); - - $this->assertEqual($query_result, 6, 'Returned the correct number of rows.'); - } - - /** - * Tests that we can generate a count query from a built query. - */ - function testCountQuery() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $query->orderBy('name'); - - $count = $query->countQuery()->execute()->fetchField(); - - $this->assertEqual($count, 4, 'Counted the correct number of records.'); - - // Now make sure we didn't break the original query! We should still have - // all of the fields we asked for. - $record = $query->execute()->fetch(); - $this->assertEqual($record->$name_field, 'George', 'Correct data retrieved.'); - $this->assertEqual($record->$age_field, 27, 'Correct data retrieved.'); - } - - /** - * Tests having queries. - */ - function testHavingCountQuery() { - $query = db_select('test') - ->extend('Drupal\Core\Database\Query\PagerSelectExtender') - ->groupBy('age') - ->having('age + 1 > 0'); - $query->addField('test', 'age'); - $query->addExpression('age + 1'); - $count = count($query->execute()->fetchCol()); - $this->assertEqual($count, 4, 'Counted the correct number of records.'); - } - - /** - * Tests that countQuery removes 'all_fields' statements and ordering clauses. - */ - function testCountQueryRemovals() { - $query = db_select('test'); - $query->fields('test'); - $query->orderBy('name'); - $count = $query->countQuery(); - - // Check that the 'all_fields' statement is handled properly. - $tables = $query->getTables(); - $this->assertEqual($tables['test']['all_fields'], 1, 'Query correctly sets \'all_fields\' statement.'); - $tables = $count->getTables(); - $this->assertFalse(isset($tables['test']['all_fields']), 'Count query correctly unsets \'all_fields\' statement.'); - - // Check that the ordering clause is handled properly. - $orderby = $query->getOrderBy(); - // The orderby string is different for PostgreSQL. - // @see Drupal\Core\Database\Driver\pgsql\Select::orderBy() - $db_type = Database::getConnection()->databaseType(); - $this->assertEqual($orderby['name'], ($db_type == 'pgsql' ? 'ASC NULLS FIRST' : 'ASC'), 'Query correctly sets ordering clause.'); - $orderby = $count->getOrderBy(); - $this->assertFalse(isset($orderby['name']), 'Count query correctly unsets ordering clause.'); - - // Make sure that the count query works. - $count = $count->execute()->fetchField(); - - $this->assertEqual($count, 4, 'Counted the correct number of records.'); - } - - - /** - * Tests that countQuery properly removes fields and expressions. - */ - function testCountQueryFieldRemovals() { - // countQuery should remove all fields and expressions, so this can be - // tested by adding a non-existent field and expression: if it ends - // up in the query, an error will be thrown. If not, it will return the - // number of records, which in this case happens to be 4 (there are four - // records in the {test} table). - $query = db_select('test'); - $query->fields('test', array('fail')); - $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), 'Count Query removed fields'); - - $query = db_select('test'); - $query->addExpression('fail'); - $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), 'Count Query removed expressions'); - } - - /** - * Tests that we can generate a count query from a query with distinct. - */ - function testCountQueryDistinct() { - $query = db_select('test_task'); - $query->addField('test_task', 'task'); - $query->distinct(); - - $count = $query->countQuery()->execute()->fetchField(); - - $this->assertEqual($count, 6, 'Counted the correct number of records.'); - } - - /** - * Tests that we can generate a count query from a query with GROUP BY. - */ - function testCountQueryGroupBy() { - $query = db_select('test_task'); - $query->addField('test_task', 'pid'); - $query->groupBy('pid'); - - $count = $query->countQuery()->execute()->fetchField(); - - $this->assertEqual($count, 3, 'Counted the correct number of records.'); - - // Use a column alias as, without one, the query can succeed for the wrong - // reason. - $query = db_select('test_task'); - $query->addField('test_task', 'pid', 'pid_alias'); - $query->addExpression('COUNT(test_task.task)', 'count'); - $query->groupBy('pid_alias'); - $query->orderBy('pid_alias', 'asc'); - - $count = $query->countQuery()->execute()->fetchField(); - - $this->assertEqual($count, 3, 'Counted the correct number of records.'); - } - - /** - * Confirms that we can properly nest conditional clauses. - */ - function testNestedConditions() { - // This query should translate to: - // "SELECT job FROM {test} WHERE name = 'Paul' AND (age = 26 OR age = 27)" - // That should find only one record. Yes it's a non-optimal way of writing - // that query but that's not the point! - $query = db_select('test'); - $query->addField('test', 'job'); - $query->condition('name', 'Paul'); - $query->condition(db_or()->condition('age', 26)->condition('age', 27)); - - $job = $query->execute()->fetchField(); - $this->assertEqual($job, 'Songwriter', 'Correct data retrieved.'); - } - - /** - * Confirms we can join on a single table twice with a dynamic alias. - */ - function testJoinTwice() { - $query = db_select('test')->fields('test'); - $alias = $query->join('test', 'test', 'test.job = %alias.job'); - $query->addField($alias, 'name', 'othername'); - $query->addField($alias, 'job', 'otherjob'); - $query->where("$alias.name <> test.name"); - $crowded_job = $query->execute()->fetch(); - $this->assertEqual($crowded_job->job, $crowded_job->otherjob, 'Correctly joined same table twice.'); - $this->assertNotEqual($crowded_job->name, $crowded_job->othername, 'Correctly joined same table twice.'); - } - - /** - * Tests that we can join on a query. - */ - function testJoinSubquery() { - $this->installSchema('system', 'sequences'); - - $account = entity_create('user', array( - 'name' => $this->randomMachineName(), - 'mail' => $this->randomMachineName() . '@example.com', - )); - - $query = db_select('test_task', 'tt', array('target' => 'replica')); - $query->addExpression('tt.pid + 1', 'abc'); - $query->condition('priority', 1, '>'); - $query->condition('priority', 100, '<'); - - $subquery = db_select('test', 'tp'); - $subquery->join('test_one_blob', 'tpb', 'tp.id = tpb.id'); - $subquery->join('node', 'n', 'tp.id = n.nid'); - $subquery->addTag('node_access'); - $subquery->addMetaData('account', $account); - $subquery->addField('tp', 'id'); - $subquery->condition('age', 5, '>'); - $subquery->condition('age', 500, '<'); - - $query->leftJoin($subquery, 'sq', 'tt.pid = sq.id'); - $query->join('test_one_blob', 'tb3', 'tt.pid = tb3.id'); - - // Construct the query string. - // This is the same sequence that SelectQuery::execute() goes through. - $query->preExecute(); - $query->getArguments(); - $str = (string) $query; - - // Verify that the string only has one copy of condition placeholder 0. - $pos = strpos($str, 'db_condition_placeholder_0', 0); - $pos2 = strpos($str, 'db_condition_placeholder_0', $pos + 1); - $this->assertFalse($pos2, 'Condition placeholder is not repeated.'); - } - - /** - * Tests that rowCount() throws exception on SELECT query. - */ - function testSelectWithRowCount() { - $query = db_select('test'); - $query->addField('test', 'name'); - $result = $query->execute(); - try { - $result->rowCount(); - $exception = FALSE; - } - catch (RowCountException $e) { - $exception = TRUE; - } - $this->assertTrue($exception, 'Exception was thrown'); - } - -} diff --git a/core/modules/system/src/Tests/Database/SelectOrderedTest.php b/core/modules/system/src/Tests/Database/SelectOrderedTest.php deleted file mode 100644 index 7b63bb7..0000000 --- a/core/modules/system/src/Tests/Database/SelectOrderedTest.php +++ /dev/null @@ -1,89 +0,0 @@ -addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $query->orderBy($age_field); - $result = $query->execute(); - - $num_records = 0; - $last_age = 0; - foreach ($result as $record) { - $num_records++; - $this->assertTrue($record->age >= $last_age, 'Results returned in correct order.'); - $last_age = $record->age; - } - - $this->assertEqual($num_records, 4, 'Returned the correct number of rows.'); - } - - /** - * Tests multiple ORDER BY. - */ - function testSimpleSelectMultiOrdered() { - $query = db_select('test'); - $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $job_field = $query->addField('test', 'job'); - $query->orderBy($job_field); - $query->orderBy($age_field); - $result = $query->execute(); - - $num_records = 0; - $expected = array( - array('Ringo', 28, 'Drummer'), - array('John', 25, 'Singer'), - array('George', 27, 'Singer'), - array('Paul', 26, 'Songwriter'), - ); - $results = $result->fetchAll(\PDO::FETCH_NUM); - foreach ($expected as $k => $record) { - $num_records++; - foreach ($record as $kk => $col) { - if ($expected[$k][$kk] != $results[$k][$kk]) { - $this->assertTrue(FALSE, 'Results returned in correct order.'); - } - } - } - $this->assertEqual($num_records, 4, 'Returned the correct number of rows.'); - } - - /** - * Tests ORDER BY descending. - */ - function testSimpleSelectOrderedDesc() { - $query = db_select('test'); - $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $query->orderBy($age_field, 'DESC'); - $result = $query->execute(); - - $num_records = 0; - $last_age = 100000000; - foreach ($result as $record) { - $num_records++; - $this->assertTrue($record->age <= $last_age, 'Results returned in correct order.'); - $last_age = $record->age; - } - - $this->assertEqual($num_records, 4, 'Returned the correct number of rows.'); - } -} diff --git a/core/modules/system/src/Tests/Database/SelectSubqueryTest.php b/core/modules/system/src/Tests/Database/SelectSubqueryTest.php deleted file mode 100644 index 9eb3048..0000000 --- a/core/modules/system/src/Tests/Database/SelectSubqueryTest.php +++ /dev/null @@ -1,183 +0,0 @@ -addField('tt', 'pid', 'pid'); - $subquery->addField('tt', 'task', 'task'); - $subquery->condition('priority', 1); - - for ($i = 0; $i < 2; $i++) { - // Create another query that joins against the virtual table resulting - // from the subquery. - $select = db_select($subquery, 'tt2'); - $select->join('test', 't', 't.id=tt2.pid'); - $select->addField('t', 'name'); - if ($i) { - // Use a different number of conditions here to confuse the subquery - // placeholder counter, testing https://www.drupal.org/node/1112854. - $select->condition('name', 'John'); - } - $select->condition('task', 'code'); - - // The resulting query should be equivalent to: - // SELECT t.name - // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt WHERE priority=1) tt - // INNER JOIN test t ON t.id=tt.pid - // WHERE tt.task = 'code' - $people = $select->execute()->fetchCol(); - - $this->assertEqual(count($people), 1, 'Returned the correct number of rows.'); - } - } - - /** - * Tests that we can use a subquery in a FROM clause with a LIMIT. - */ - function testFromSubquerySelectWithLimit() { - // Create a subquery, which is just a normal query object. - $subquery = db_select('test_task', 'tt'); - $subquery->addField('tt', 'pid', 'pid'); - $subquery->addField('tt', 'task', 'task'); - $subquery->orderBy('priority', 'DESC'); - $subquery->range(0, 1); - - // Create another query that joins against the virtual table resulting - // from the subquery. - $select = db_select($subquery, 'tt2'); - $select->join('test', 't', 't.id=tt2.pid'); - $select->addField('t', 'name'); - - // The resulting query should be equivalent to: - // SELECT t.name - // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt ORDER BY priority DESC LIMIT 1 OFFSET 0) tt - // INNER JOIN test t ON t.id=tt.pid - $people = $select->execute()->fetchCol(); - - $this->assertEqual(count($people), 1, 'Returned the correct number of rows.'); - } - - /** - * Tests that we can use a subquery in a WHERE clause. - */ - function testConditionSubquerySelect() { - // Create a subquery, which is just a normal query object. - $subquery = db_select('test_task', 'tt'); - $subquery->addField('tt', 'pid', 'pid'); - $subquery->condition('tt.priority', 1); - - // Create another query that joins against the virtual table resulting - // from the subquery. - $select = db_select('test_task', 'tt2'); - $select->addField('tt2', 'task'); - $select->condition('tt2.pid', $subquery, 'IN'); - - // The resulting query should be equivalent to: - // SELECT tt2.name - // FROM test tt2 - // WHERE tt2.pid IN (SELECT tt.pid AS pid FROM test_task tt WHERE tt.priority=1) - $people = $select->execute()->fetchCol(); - $this->assertEqual(count($people), 5, 'Returned the correct number of rows.'); - } - - /** - * Tests that we can use a subquery in a JOIN clause. - */ - function testJoinSubquerySelect() { - // Create a subquery, which is just a normal query object. - $subquery = db_select('test_task', 'tt'); - $subquery->addField('tt', 'pid', 'pid'); - $subquery->condition('priority', 1); - - // Create another query that joins against the virtual table resulting - // from the subquery. - $select = db_select('test', 't'); - $select->join($subquery, 'tt', 't.id=tt.pid'); - $select->addField('t', 'name'); - - // The resulting query should be equivalent to: - // SELECT t.name - // FROM test t - // INNER JOIN (SELECT tt.pid AS pid FROM test_task tt WHERE priority=1) tt ON t.id=tt.pid - $people = $select->execute()->fetchCol(); - - $this->assertEqual(count($people), 2, 'Returned the correct number of rows.'); - } - - /** - * Tests EXISTS subquery conditionals on SELECT statements. - * - * We essentially select all rows from the {test} table that have matching - * rows in the {test_people} table based on the shared name column. - */ - function testExistsSubquerySelect() { - // Put George into {test_people}. - db_insert('test_people') - ->fields(array( - 'name' => 'George', - 'age' => 27, - 'job' => 'Singer', - )) - ->execute(); - // Base query to {test}. - $query = db_select('test', 't') - ->fields('t', array('name')); - // Subquery to {test_people}. - $subquery = db_select('test_people', 'tp') - ->fields('tp', array('name')) - ->where('tp.name = t.name'); - $query->exists($subquery); - $result = $query->execute(); - - // Ensure that we got the right record. - $record = $result->fetch(); - $this->assertEqual($record->name, 'George', 'Fetched name is correct using EXISTS query.'); - } - - /** - * Tests NOT EXISTS subquery conditionals on SELECT statements. - * - * We essentially select all rows from the {test} table that don't have - * matching rows in the {test_people} table based on the shared name column. - */ - function testNotExistsSubquerySelect() { - // Put George into {test_people}. - db_insert('test_people') - ->fields(array( - 'name' => 'George', - 'age' => 27, - 'job' => 'Singer', - )) - ->execute(); - - // Base query to {test}. - $query = db_select('test', 't') - ->fields('t', array('name')); - // Subquery to {test_people}. - $subquery = db_select('test_people', 'tp') - ->fields('tp', array('name')) - ->where('tp.name = t.name'); - $query->notExists($subquery); - - // Ensure that we got the right number of records. - $people = $query->execute()->fetchCol(); - $this->assertEqual(count($people), 3, 'NOT EXISTS query returned the correct results.'); - } -} diff --git a/core/modules/system/src/Tests/Database/SelectTest.php b/core/modules/system/src/Tests/Database/SelectTest.php deleted file mode 100644 index 32f743a..0000000 --- a/core/modules/system/src/Tests/Database/SelectTest.php +++ /dev/null @@ -1,535 +0,0 @@ -addField('test', 'name'); - $query->addField('test', 'age', 'age'); - $num_records = $query->countQuery()->execute()->fetchField(); - - $this->assertEqual($num_records, 4, 'Returned the correct number of rows.'); - } - - /** - * Tests rudimentary SELECT statement with a COMMENT. - */ - function testSimpleComment() { - $query = db_select('test')->comment('Testing query comments'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - $result = $query->execute(); - - $records = $result->fetchAll(); - - $query = (string) $query; - $expected = "/* Testing query comments */"; - - $this->assertEqual(count($records), 4, 'Returned the correct number of rows.'); - $this->assertNotIdentical(FALSE, strpos($query, $expected), 'The flattened query contains the comment string.'); - } - - /** - * Tests query COMMENT system against vulnerabilities. - */ - function testVulnerableComment() { - $query = db_select('test')->comment('Testing query comments */ SELECT nid FROM {node}; --'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - $result = $query->execute(); - - $records = $result->fetchAll(); - - $query = (string) $query; - $expected = "/* Testing query comments * / SELECT nid FROM {node}. -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test"; - - $this->assertEqual(count($records), 4, 'Returned the correct number of rows.'); - $this->assertNotIdentical(FALSE, strpos($query, $expected), 'The flattened query contains the sanitised comment string.'); - - $connection = Database::getConnection(); - foreach ($this->makeCommentsProvider() as $test_set) { - list($expected, $comments) = $test_set; - $this->assertEqual($expected, $connection->makeComment($comments)); - } - } - - /** - * Provides expected and input values for testVulnerableComment(). - */ - function makeCommentsProvider() { - return [ - [ - '/* */ ', - [''], - ], - // Try and close the comment early. - [ - '/* Exploit * / DROP TABLE node. -- */ ', - ['Exploit */ DROP TABLE node; --'], - ], - // Variations on comment closing. - [ - '/* Exploit * / * / DROP TABLE node. -- */ ', - ['Exploit */*/ DROP TABLE node; --'], - ], - [ - '/* Exploit * * // DROP TABLE node. -- */ ', - ['Exploit **// DROP TABLE node; --'], - ], - // Try closing the comment in the second string which is appended. - [ - '/* Exploit * / DROP TABLE node. --. Another try * / DROP TABLE node. -- */ ', - ['Exploit */ DROP TABLE node; --', 'Another try */ DROP TABLE node; --'], - ], - ]; - } - - /** - * Tests basic conditionals on SELECT statements. - */ - function testSimpleSelectConditional() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addField('test', 'age', 'age'); - $query->condition('age', 27); - $result = $query->execute(); - - // Check that the aliases are being created the way we want. - $this->assertEqual($name_field, 'name', 'Name field alias is correct.'); - $this->assertEqual($age_field, 'age', 'Age field alias is correct.'); - - // Ensure that we got the right record. - $record = $result->fetch(); - $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.'); - $this->assertEqual($record->$age_field, 27, 'Fetched age is correct.'); - } - - /** - * Tests SELECT statements with expressions. - */ - function testSimpleSelectExpression() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_field = $query->addExpression("age*2", 'double_age'); - $query->condition('age', 27); - $result = $query->execute(); - - // Check that the aliases are being created the way we want. - $this->assertEqual($name_field, 'name', 'Name field alias is correct.'); - $this->assertEqual($age_field, 'double_age', 'Age field alias is correct.'); - - // Ensure that we got the right record. - $record = $result->fetch(); - $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.'); - $this->assertEqual($record->$age_field, 27*2, 'Fetched age expression is correct.'); - } - - /** - * Tests SELECT statements with multiple expressions. - */ - function testSimpleSelectExpressionMultiple() { - $query = db_select('test'); - $name_field = $query->addField('test', 'name'); - $age_double_field = $query->addExpression("age*2"); - $age_triple_field = $query->addExpression("age*3"); - $query->condition('age', 27); - $result = $query->execute(); - - // Check that the aliases are being created the way we want. - $this->assertEqual($age_double_field, 'expression', 'Double age field alias is correct.'); - $this->assertEqual($age_triple_field, 'expression_2', 'Triple age field alias is correct.'); - - // Ensure that we got the right record. - $record = $result->fetch(); - $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.'); - $this->assertEqual($record->$age_double_field, 27*2, 'Fetched double age expression is correct.'); - $this->assertEqual($record->$age_triple_field, 27*3, 'Fetched triple age expression is correct.'); - } - - /** - * Tests adding multiple fields to a SELECT statement at the same time. - */ - function testSimpleSelectMultipleFields() { - $record = db_select('test') - ->fields('test', array('id', 'name', 'age', 'job')) - ->condition('age', 27) - ->execute()->fetchObject(); - - // Check that all fields we asked for are present. - $this->assertNotNull($record->id, 'ID field is present.'); - $this->assertNotNull($record->name, 'Name field is present.'); - $this->assertNotNull($record->age, 'Age field is present.'); - $this->assertNotNull($record->job, 'Job field is present.'); - - // Ensure that we got the right record. - // Check that all fields we asked for are present. - $this->assertEqual($record->id, 2, 'ID field has the correct value.'); - $this->assertEqual($record->name, 'George', 'Name field has the correct value.'); - $this->assertEqual($record->age, 27, 'Age field has the correct value.'); - $this->assertEqual($record->job, 'Singer', 'Job field has the correct value.'); - } - - /** - * Tests adding all fields from a given table to a SELECT statement. - */ - function testSimpleSelectAllFields() { - $record = db_select('test') - ->fields('test') - ->condition('age', 27) - ->execute()->fetchObject(); - - // Check that all fields we asked for are present. - $this->assertNotNull($record->id, 'ID field is present.'); - $this->assertNotNull($record->name, 'Name field is present.'); - $this->assertNotNull($record->age, 'Age field is present.'); - $this->assertNotNull($record->job, 'Job field is present.'); - - // Ensure that we got the right record. - // Check that all fields we asked for are present. - $this->assertEqual($record->id, 2, 'ID field has the correct value.'); - $this->assertEqual($record->name, 'George', 'Name field has the correct value.'); - $this->assertEqual($record->age, 27, 'Age field has the correct value.'); - $this->assertEqual($record->job, 'Singer', 'Job field has the correct value.'); - } - - /** - * Tests that a comparison with NULL is always FALSE. - */ - function testNullCondition() { - $this->ensureSampleDataNull(); - - $names = db_select('test_null', 'tn') - ->fields('tn', array('name')) - ->condition('age', NULL) - ->execute()->fetchCol(); - - $this->assertEqual(count($names), 0, 'No records found when comparing to NULL.'); - } - - /** - * Tests that we can find a record with a NULL value. - */ - function testIsNullCondition() { - $this->ensureSampleDataNull(); - - $names = db_select('test_null', 'tn') - ->fields('tn', array('name')) - ->isNull('age') - ->execute()->fetchCol(); - - $this->assertEqual(count($names), 1, 'Correct number of records found with NULL age.'); - $this->assertEqual($names[0], 'Fozzie', 'Correct record returned for NULL age.'); - } - - /** - * Tests that we can find a record without a NULL value. - */ - function testIsNotNullCondition() { - $this->ensureSampleDataNull(); - - $names = db_select('test_null', 'tn') - ->fields('tn', array('name')) - ->isNotNull('tn.age') - ->orderBy('name') - ->execute()->fetchCol(); - - $this->assertEqual(count($names), 2, 'Correct number of records found withNOT NULL age.'); - $this->assertEqual($names[0], 'Gonzo', 'Correct record returned for NOT NULL age.'); - $this->assertEqual($names[1], 'Kermit', 'Correct record returned for NOT NULL age.'); - } - - /** - * Tests that we can UNION multiple Select queries together. - * - * This is semantically equal to UNION DISTINCT, so we don't explicitly test - * that. - */ - function testUnion() { - $query_1 = db_select('test', 't') - ->fields('t', array('name')) - ->condition('age', array(27, 28), 'IN'); - - $query_2 = db_select('test', 't') - ->fields('t', array('name')) - ->condition('age', 28); - - $query_1->union($query_2); - - $names = $query_1->execute()->fetchCol(); - - // Ensure we only get 2 records. - $this->assertEqual(count($names), 2, 'UNION correctly discarded duplicates.'); - - $this->assertEqual($names[0], 'George', 'First query returned correct name.'); - $this->assertEqual($names[1], 'Ringo', 'Second query returned correct name.'); - } - - /** - * Tests that we can UNION ALL multiple SELECT queries together. - */ - function testUnionAll() { - $query_1 = db_select('test', 't') - ->fields('t', array('name')) - ->condition('age', array(27, 28), 'IN'); - - $query_2 = db_select('test', 't') - ->fields('t', array('name')) - ->condition('age', 28); - - $query_1->union($query_2, 'ALL'); - - $names = $query_1->execute()->fetchCol(); - - // Ensure we get all 3 records. - $this->assertEqual(count($names), 3, 'UNION ALL correctly preserved duplicates.'); - - $this->assertEqual($names[0], 'George', 'First query returned correct first name.'); - $this->assertEqual($names[1], 'Ringo', 'Second query returned correct second name.'); - $this->assertEqual($names[2], 'Ringo', 'Third query returned correct name.'); - } - - /** - * Tests that we can get a count query for a UNION Select query. - */ - function testUnionCount() { - $query_1 = db_select('test', 't') - ->fields('t', array('name', 'age')) - ->condition('age', array(27, 28), 'IN'); - - $query_2 = db_select('test', 't') - ->fields('t', array('name', 'age')) - ->condition('age', 28); - - $query_1->union($query_2, 'ALL'); - $names = $query_1->execute()->fetchCol(); - - $query_3 = $query_1->countQuery(); - $count = $query_3->execute()->fetchField(); - - // Ensure the counts match. - $this->assertEqual(count($names), $count, "The count query's result matched the number of rows in the UNION query."); - } - - /** - * Tests that random ordering of queries works. - * - * We take the approach of testing the Drupal layer only, rather than trying - * to test that the database's random number generator actually produces - * random queries (which is very difficult to do without an unacceptable risk - * of the test failing by accident). - * - * Therefore, in this test we simply run the same query twice and assert that - * the two results are reordered versions of each other (as well as of the - * same query without the random ordering). It is reasonable to assume that - * if we run the same select query twice and the results are in a different - * order each time, the only way this could happen is if we have successfully - * triggered the database's random ordering functionality. - */ - function testRandomOrder() { - // Use 52 items, so the chance that this test fails by accident will be the - // same as the chance that a deck of cards will come out in the same order - // after shuffling it (in other words, nearly impossible). - $number_of_items = 52; - while (db_query("SELECT MAX(id) FROM {test}")->fetchField() < $number_of_items) { - db_insert('test')->fields(array('name' => $this->randomMachineName()))->execute(); - } - - // First select the items in order and make sure we get an ordered list. - $expected_ids = range(1, $number_of_items); - $ordered_ids = db_select('test', 't') - ->fields('t', array('id')) - ->range(0, $number_of_items) - ->orderBy('id') - ->execute() - ->fetchCol(); - $this->assertEqual($ordered_ids, $expected_ids, 'A query without random ordering returns IDs in the correct order.'); - - // Now perform the same query, but instead choose a random ordering. We - // expect this to contain a differently ordered version of the original - // result. - $randomized_ids = db_select('test', 't') - ->fields('t', array('id')) - ->range(0, $number_of_items) - ->orderRandom() - ->execute() - ->fetchCol(); - $this->assertNotEqual($randomized_ids, $ordered_ids, 'A query with random ordering returns an unordered set of IDs.'); - $sorted_ids = $randomized_ids; - sort($sorted_ids); - $this->assertEqual($sorted_ids, $ordered_ids, 'After sorting the random list, the result matches the original query.'); - - // Now perform the exact same query again, and make sure the order is - // different. - $randomized_ids_second_set = db_select('test', 't') - ->fields('t', array('id')) - ->range(0, $number_of_items) - ->orderRandom() - ->execute() - ->fetchCol(); - $this->assertNotEqual($randomized_ids_second_set, $randomized_ids, 'Performing the query with random ordering a second time returns IDs in a different order.'); - $sorted_ids_second_set = $randomized_ids_second_set; - sort($sorted_ids_second_set); - $this->assertEqual($sorted_ids_second_set, $sorted_ids, 'After sorting the second random list, the result matches the sorted version of the first random list.'); - } - - /** - * Tests that filter by a regular expression works as expected. - */ - public function testRegexCondition() { - - $test_groups[] = array( - 'regex' => 'hn$', - 'expected' => array( - 'John', - ), - ); - $test_groups[] = array( - 'regex' => '^Pau', - 'expected' => array( - 'Paul', - ), - ); - $test_groups[] = array( - 'regex' => 'Ringo|George', - 'expected' => array( - 'Ringo', 'George', - ), - ); - - - $database = $this->container->get('database'); - foreach ($test_groups as $test_group) { - $query = $database->select('test', 't'); - $query->addField('t', 'name'); - $query->condition('t.name', $test_group['regex'], 'REGEXP'); - $result = $query->execute()->fetchCol(); - - $this->assertEqual(count($result), count($test_group['expected']), 'Returns the expected number of rows.'); - $this->assertEqual(sort($result), sort($test_group['expected']), 'Returns the expected rows.'); - } - - // Ensure that filter by "#" still works due to the quoting. - $database->insert('test') - ->fields(array( - 'name' => 'Pete', - 'age' => 26, - 'job' => '#Drummer', - )) - ->execute(); - - $test_groups = array(); - $test_groups[] = array( - 'regex' => '#Drummer', - 'expected' => array( - 'Pete', - ), - ); - $test_groups[] = array( - 'regex' => '#Singer', - 'expected' => array( - ), - ); - - foreach ($test_groups as $test_group) { - $query = $database->select('test', 't'); - $query->addField('t', 'name'); - $query->condition('t.job', $test_group['regex'], 'REGEXP'); - $result = $query->execute()->fetchCol(); - - $this->assertEqual(count($result), count($test_group['expected']), 'Returns the expected number of rows.'); - $this->assertEqual(sort($result), sort($test_group['expected']), 'Returns the expected rows.'); - } - } - - /** - * Tests that aliases are renamed when they are duplicates. - */ - function testSelectDuplicateAlias() { - $query = db_select('test', 't'); - $alias1 = $query->addField('t', 'name', 'the_alias'); - $alias2 = $query->addField('t', 'age', 'the_alias'); - $this->assertNotIdentical($alias1, $alias2, 'Duplicate aliases are renamed.'); - } - - /** - * Tests that an invalid merge query throws an exception. - */ - function testInvalidSelectCount() { - try { - // This query will fail because the table does not exist. - // Normally it would throw an exception but we are suppressing - // it with the throw_exception option. - $options['throw_exception'] = FALSE; - db_select('some_table_that_doesnt_exist', 't', $options) - ->fields('t') - ->countQuery() - ->execute(); - - $this->pass('$options[\'throw_exception\'] is FALSE, no Exception thrown.'); - } - catch (\Exception $e) { - $this->fail('$options[\'throw_exception\'] is FALSE, but Exception thrown for invalid query.'); - return; - } - - try { - // This query will fail because the table does not exist. - db_select('some_table_that_doesnt_exist', 't') - ->fields('t') - ->countQuery() - ->execute(); - } - catch (\Exception $e) { - $this->pass('Exception thrown for invalid query.'); - return; - } - $this->fail('No Exception thrown.'); - } - - /** - * Tests thrown exception for IN query conditions with an empty array. - */ - function testEmptyInCondition() { - try { - db_select('test', 't') - ->fields('t') - ->condition('age', array(), 'IN') - ->execute(); - - $this->fail('Expected exception not thrown'); - } - catch (InvalidQueryException $e) { - $this->assertEqual("Query condition 'age IN ()' cannot be empty.", $e->getMessage()); - } - - try { - db_select('test', 't') - ->fields('t') - ->condition('age', array(), 'NOT IN') - ->execute(); - - $this->fail('Expected exception not thrown'); - } - catch (InvalidQueryException $e) { - $this->assertEqual("Query condition 'age NOT IN ()' cannot be empty.", $e->getMessage()); - } - } - -} diff --git a/core/modules/system/src/Tests/Database/SerializeQueryTest.php b/core/modules/system/src/Tests/Database/SerializeQueryTest.php deleted file mode 100644 index 00f295f..0000000 --- a/core/modules/system/src/Tests/Database/SerializeQueryTest.php +++ /dev/null @@ -1,29 +0,0 @@ -addField('test', 'age'); - $query->condition('name', 'Ringo'); - // If this doesn't work, it will throw an exception, so no need for an - // assertion. - $query = unserialize(serialize($query)); - $results = $query->execute()->fetchCol(); - $this->assertEqual($results[0], 28, 'Query properly executed after unserialization.'); - } -} diff --git a/core/modules/system/src/Tests/Database/TaggingTest.php b/core/modules/system/src/Tests/Database/TaggingTest.php deleted file mode 100644 index a297d7b..0000000 --- a/core/modules/system/src/Tests/Database/TaggingTest.php +++ /dev/null @@ -1,132 +0,0 @@ -addField('test', 'name'); - $query->addField('test', 'age', 'age'); - - $query->addTag('test'); - - $this->assertTrue($query->hasTag('test'), 'hasTag() returned true.'); - $this->assertFalse($query->hasTag('other'), 'hasTag() returned false.'); - } - - /** - * Tests query tagging "has all of these tags" functionality. - */ - function testHasAllTags() { - $query = db_select('test'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - - $query->addTag('test'); - $query->addTag('other'); - - $this->assertTrue($query->hasAllTags('test', 'other'), 'hasAllTags() returned true.'); - $this->assertFalse($query->hasAllTags('test', 'stuff'), 'hasAllTags() returned false.'); - } - - /** - * Tests query tagging "has at least one of these tags" functionality. - */ - function testHasAnyTag() { - $query = db_select('test'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - - $query->addTag('test'); - - $this->assertTrue($query->hasAnyTag('test', 'other'), 'hasAnyTag() returned true.'); - $this->assertFalse($query->hasAnyTag('other', 'stuff'), 'hasAnyTag() returned false.'); - } - - /** - * Confirms that an extended query has a tag added to it. - */ - function testExtenderHasTag() { - $query = db_select('test') - ->extend('Drupal\Core\Database\Query\SelectExtender'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - - $query->addTag('test'); - - $this->assertTrue($query->hasTag('test'), 'hasTag() returned true.'); - $this->assertFalse($query->hasTag('other'), 'hasTag() returned false.'); - } - - /** - * Tests extended query tagging "has all of these tags" functionality. - */ - function testExtenderHasAllTags() { - $query = db_select('test') - ->extend('Drupal\Core\Database\Query\SelectExtender'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - - $query->addTag('test'); - $query->addTag('other'); - - $this->assertTrue($query->hasAllTags('test', 'other'), 'hasAllTags() returned true.'); - $this->assertFalse($query->hasAllTags('test', 'stuff'), 'hasAllTags() returned false.'); - } - - /** - * Tests extended query tagging "has at least one of these tags" functionality. - */ - function testExtenderHasAnyTag() { - $query = db_select('test') - ->extend('Drupal\Core\Database\Query\SelectExtender'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - - $query->addTag('test'); - - $this->assertTrue($query->hasAnyTag('test', 'other'), 'hasAnyTag() returned true.'); - $this->assertFalse($query->hasAnyTag('other', 'stuff'), 'hasAnyTag() returned false.'); - } - - /** - * Tests that we can attach metadata to a query object. - * - * This is how we pass additional context to alter hooks. - */ - function testMetaData() { - $query = db_select('test'); - $query->addField('test', 'name'); - $query->addField('test', 'age', 'age'); - - $data = array( - 'a' => 'A', - 'b' => 'B', - ); - - $query->addMetaData('test', $data); - - $return = $query->getMetaData('test'); - $this->assertEqual($data, $return, 'Correct metadata returned.'); - - $return = $query->getMetaData('nothere'); - $this->assertNull($return, 'Non-existent key returned NULL.'); - } -} diff --git a/core/modules/system/src/Tests/Database/TransactionTest.php b/core/modules/system/src/Tests/Database/TransactionTest.php deleted file mode 100644 index 5aef79c..0000000 --- a/core/modules/system/src/Tests/Database/TransactionTest.php +++ /dev/null @@ -1,615 +0,0 @@ -transactionDepth(); - $txn = db_transaction(); - - // Insert a single row into the testing table. - db_insert('test') - ->fields(array( - 'name' => 'David' . $suffix, - 'age' => '24', - )) - ->execute(); - - $this->assertTrue($connection->inTransaction(), 'In transaction before calling nested transaction.'); - - // We're already in a transaction, but we call ->transactionInnerLayer - // to nest another transaction inside the current one. - $this->transactionInnerLayer($suffix, $rollback, $ddl_statement); - - $this->assertTrue($connection->inTransaction(), 'In transaction after calling nested transaction.'); - - if ($rollback) { - // Roll back the transaction, if requested. - // This rollback should propagate to the last savepoint. - $txn->rollback(); - $this->assertTrue(($connection->transactionDepth() == $depth), 'Transaction has rolled back to the last savepoint after calling rollback().'); - } - } - - /** - * Creates an "inner layer" transaction. - * - * This "inner layer" transaction is either used alone or nested inside of the - * "outer layer" transaction. - * - * @param $suffix - * Suffix to add to field values to differentiate tests. - * @param $rollback - * Whether or not to try rolling back the transaction when we're done. - * @param $ddl_statement - * Whether to execute a DDL statement during the transaction. - */ - protected function transactionInnerLayer($suffix, $rollback = FALSE, $ddl_statement = FALSE) { - $connection = Database::getConnection(); - - $depth = $connection->transactionDepth(); - // Start a transaction. If we're being called from ->transactionOuterLayer, - // then we're already in a transaction. Normally, that would make starting - // a transaction here dangerous, but the database API handles this problem - // for us by tracking the nesting and avoiding the danger. - $txn = db_transaction(); - - $depth2 = $connection->transactionDepth(); - $this->assertTrue($depth < $depth2, 'Transaction depth is has increased with new transaction.'); - - // Insert a single row into the testing table. - db_insert('test') - ->fields(array( - 'name' => 'Daniel' . $suffix, - 'age' => '19', - )) - ->execute(); - - $this->assertTrue($connection->inTransaction(), 'In transaction inside nested transaction.'); - - if ($ddl_statement) { - $table = array( - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - ), - 'primary key' => array('id'), - ); - db_create_table('database_test_1', $table); - - $this->assertTrue($connection->inTransaction(), 'In transaction inside nested transaction.'); - } - - if ($rollback) { - // Roll back the transaction, if requested. - // This rollback should propagate to the last savepoint. - $txn->rollback(); - $this->assertTrue(($connection->transactionDepth() == $depth), 'Transaction has rolled back to the last savepoint after calling rollback().'); - } - } - - /** - * Tests transaction rollback on a database that supports transactions. - * - * If the active connection does not support transactions, this test does - * nothing. - */ - function testTransactionRollBackSupported() { - // This test won't work right if transactions are not supported. - if (!Database::getConnection()->supportsTransactions()) { - return; - } - try { - // Create two nested transactions. Roll back from the inner one. - $this->transactionOuterLayer('B', TRUE); - - // Neither of the rows we inserted in the two transaction layers - // should be present in the tables post-rollback. - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField(); - $this->assertNotIdentical($saved_age, '24', 'Cannot retrieve DavidB row after commit.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField(); - $this->assertNotIdentical($saved_age, '19', 'Cannot retrieve DanielB row after commit.'); - } - catch (\Exception $e) { - $this->fail($e->getMessage()); - } - } - - /** - * Tests transaction rollback on a database that doesn't support transactions. - * - * If the active driver supports transactions, this test does nothing. - */ - function testTransactionRollBackNotSupported() { - // This test won't work right if transactions are supported. - if (Database::getConnection()->supportsTransactions()) { - return; - } - try { - // Create two nested transactions. Attempt to roll back from the inner one. - $this->transactionOuterLayer('B', TRUE); - - // Because our current database claims to not support transactions, - // the inserted rows should be present despite the attempt to roll back. - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField(); - $this->assertIdentical($saved_age, '24', 'DavidB not rolled back, since transactions are not supported.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField(); - $this->assertIdentical($saved_age, '19', 'DanielB not rolled back, since transactions are not supported.'); - } - catch (\Exception $e) { - $this->fail($e->getMessage()); - } - } - - /** - * Tests a committed transaction. - * - * The behavior of this test should be identical for connections that support - * transactions and those that do not. - */ - function testCommittedTransaction() { - try { - // Create two nested transactions. The changes should be committed. - $this->transactionOuterLayer('A'); - - // Because we committed, both of the inserted rows should be present. - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidA'))->fetchField(); - $this->assertIdentical($saved_age, '24', 'Can retrieve DavidA row after commit.'); - $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielA'))->fetchField(); - $this->assertIdentical($saved_age, '19', 'Can retrieve DanielA row after commit.'); - } - catch (\Exception $e) { - $this->fail($e->getMessage()); - } - } - - /** - * Tests the compatibility of transactions with DDL statements. - */ - function testTransactionWithDdlStatement() { - // First, test that a commit works normally, even with DDL statements. - $transaction = db_transaction(); - $this->insertRow('row'); - $this->executeDDLStatement(); - unset($transaction); - $this->assertRowPresent('row'); - - // Even in different order. - $this->cleanUp(); - $transaction = db_transaction(); - $this->executeDDLStatement(); - $this->insertRow('row'); - unset($transaction); - $this->assertRowPresent('row'); - - // Even with stacking. - $this->cleanUp(); - $transaction = db_transaction(); - $transaction2 = db_transaction(); - $this->executeDDLStatement(); - unset($transaction2); - $transaction3 = db_transaction(); - $this->insertRow('row'); - unset($transaction3); - unset($transaction); - $this->assertRowPresent('row'); - - // A transaction after a DDL statement should still work the same. - $this->cleanUp(); - $transaction = db_transaction(); - $transaction2 = db_transaction(); - $this->executeDDLStatement(); - unset($transaction2); - $transaction3 = db_transaction(); - $this->insertRow('row'); - $transaction3->rollback(); - unset($transaction3); - unset($transaction); - $this->assertRowAbsent('row'); - - // The behavior of a rollback depends on the type of database server. - if (Database::getConnection()->supportsTransactionalDDL()) { - // For database servers that support transactional DDL, a rollback - // of a transaction including DDL statements should be possible. - $this->cleanUp(); - $transaction = db_transaction(); - $this->insertRow('row'); - $this->executeDDLStatement(); - $transaction->rollback(); - unset($transaction); - $this->assertRowAbsent('row'); - - // Including with stacking. - $this->cleanUp(); - $transaction = db_transaction(); - $transaction2 = db_transaction(); - $this->executeDDLStatement(); - unset($transaction2); - $transaction3 = db_transaction(); - $this->insertRow('row'); - unset($transaction3); - $transaction->rollback(); - unset($transaction); - $this->assertRowAbsent('row'); - } - else { - // For database servers that do not support transactional DDL, - // the DDL statement should commit the transaction stack. - $this->cleanUp(); - $transaction = db_transaction(); - $this->insertRow('row'); - $this->executeDDLStatement(); - // Rollback the outer transaction. - try { - $transaction->rollback(); - unset($transaction); - // @TODO: an exception should be triggered here, but is not, because - // "ROLLBACK" fails silently in MySQL if there is no transaction active. - // $this->fail(t('Rolling back a transaction containing DDL should fail.')); - } - catch (TransactionNoActiveException $e) { - $this->pass('Rolling back a transaction containing DDL should fail.'); - } - $this->assertRowPresent('row'); - } - } - - /** - * Inserts a single row into the testing table. - */ - protected function insertRow($name) { - db_insert('test') - ->fields(array( - 'name' => $name, - )) - ->execute(); - } - - /** - * Executes a DDL statement. - */ - protected function executeDDLStatement() { - static $count = 0; - $table = array( - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - ), - 'primary key' => array('id'), - ); - db_create_table('database_test_' . ++$count, $table); - } - - /** - * Starts over for a new test. - */ - protected function cleanUp() { - db_truncate('test') - ->execute(); - } - - /** - * Asserts that a given row is present in the test table. - * - * @param $name - * The name of the row. - * @param $message - * The message to log for the assertion. - */ - function assertRowPresent($name, $message = NULL) { - if (!isset($message)) { - $message = format_string('Row %name is present.', array('%name' => $name)); - } - $present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField(); - return $this->assertTrue($present, $message); - } - - /** - * Asserts that a given row is absent from the test table. - * - * @param $name - * The name of the row. - * @param $message - * The message to log for the assertion. - */ - function assertRowAbsent($name, $message = NULL) { - if (!isset($message)) { - $message = format_string('Row %name is absent.', array('%name' => $name)); - } - $present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField(); - return $this->assertFalse($present, $message); - } - - /** - * Tests transaction stacking, commit, and rollback. - */ - function testTransactionStacking() { - // This test won't work right if transactions are not supported. - if (!Database::getConnection()->supportsTransactions()) { - return; - } - - $database = Database::getConnection(); - - // Standard case: pop the inner transaction before the outer transaction. - $transaction = db_transaction(); - $this->insertRow('outer'); - $transaction2 = db_transaction(); - $this->insertRow('inner'); - // Pop the inner transaction. - unset($transaction2); - $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the inner transaction'); - // Pop the outer transaction. - unset($transaction); - $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the outer transaction'); - $this->assertRowPresent('outer'); - $this->assertRowPresent('inner'); - - // Pop the transaction in a different order they have been pushed. - $this->cleanUp(); - $transaction = db_transaction(); - $this->insertRow('outer'); - $transaction2 = db_transaction(); - $this->insertRow('inner'); - // Pop the outer transaction, nothing should happen. - unset($transaction); - $this->insertRow('inner-after-outer-commit'); - $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction'); - // Pop the inner transaction, the whole transaction should commit. - unset($transaction2); - $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction'); - $this->assertRowPresent('outer'); - $this->assertRowPresent('inner'); - $this->assertRowPresent('inner-after-outer-commit'); - - // Rollback the inner transaction. - $this->cleanUp(); - $transaction = db_transaction(); - $this->insertRow('outer'); - $transaction2 = db_transaction(); - $this->insertRow('inner'); - // Now rollback the inner transaction. - $transaction2->rollback(); - unset($transaction2); - $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction'); - // Pop the outer transaction, it should commit. - $this->insertRow('outer-after-inner-rollback'); - unset($transaction); - $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction'); - $this->assertRowPresent('outer'); - $this->assertRowAbsent('inner'); - $this->assertRowPresent('outer-after-inner-rollback'); - - // Rollback the inner transaction after committing the outer one. - $this->cleanUp(); - $transaction = db_transaction(); - $this->insertRow('outer'); - $transaction2 = db_transaction(); - $this->insertRow('inner'); - // Pop the outer transaction, nothing should happen. - unset($transaction); - $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction'); - // Now rollback the inner transaction, it should rollback. - $transaction2->rollback(); - unset($transaction2); - $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction'); - $this->assertRowPresent('outer'); - $this->assertRowAbsent('inner'); - - // Rollback the outer transaction while the inner transaction is active. - // In that case, an exception will be triggered because we cannot - // ensure that the final result will have any meaning. - $this->cleanUp(); - $transaction = db_transaction(); - $this->insertRow('outer'); - $transaction2 = db_transaction(); - $this->insertRow('inner'); - $transaction3 = db_transaction(); - $this->insertRow('inner2'); - // Rollback the outer transaction. - try { - $transaction->rollback(); - unset($transaction); - $this->fail('Rolling back the outer transaction while the inner transaction is active resulted in an exception.'); - } - catch (TransactionOutOfOrderException $e) { - $this->pass('Rolling back the outer transaction while the inner transaction is active resulted in an exception.'); - } - $this->assertFalse($database->inTransaction(), 'No more in a transaction after rolling back the outer transaction'); - // Try to commit one inner transaction. - unset($transaction3); - $this->pass('Trying to commit an inner transaction resulted in an exception.'); - // Try to rollback one inner transaction. - try { - $transaction->rollback(); - unset($transaction2); - $this->fail('Trying to commit an inner transaction resulted in an exception.'); - } - catch (TransactionNoActiveException $e) { - $this->pass('Trying to commit an inner transaction resulted in an exception.'); - } - $this->assertRowAbsent('outer'); - $this->assertRowAbsent('inner'); - $this->assertRowAbsent('inner2'); - } - - /** - * Tests that transactions can continue to be used if a query fails. - */ - public function testQueryFailureInTransaction() { - $connection = Database::getConnection(); - $transaction = $connection->startTransaction('test_transaction'); - $connection->schema()->dropTable('test'); - - // Test a failed query using the query() method. - try { - $connection->query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'David'))->fetchField(); - $this->fail('Using the query method failed.'); - } - catch (\Exception $e) { - $this->pass('Using the query method failed.'); - } - - // Test a failed select query. - try { - $connection->select('test') - ->fields('test', ['name']) - ->execute(); - - $this->fail('Select query failed.'); - } - catch (\Exception $e) { - $this->pass('Select query failed.'); - } - - // Test a failed insert query. - try { - $connection->insert('test') - ->fields([ - 'name' => 'David', - 'age' => '24', - ]) - ->execute(); - - $this->fail('Insert query failed.'); - } - catch (\Exception $e) { - $this->pass('Insert query failed.'); - } - - // Test a failed update query. - try { - $connection->update('test') - ->fields(['name' => 'Tiffany']) - ->condition('id', 1) - ->execute(); - - $this->fail('Update query failed.'); - } - catch (\Exception $e) { - $this->pass('Update query failed.'); - } - - // Test a failed delete query. - try { - $connection->delete('test') - ->condition('id', 1) - ->execute(); - - $this->fail('Delete query failed.'); - } - catch (\Exception $e) { - $this->pass('Delete query failed.'); - } - - // Test a failed merge query. - try { - $connection->merge('test') - ->key('job', 'Presenter') - ->fields([ - 'age' => '31', - 'name' => 'Tiffany', - ]) - ->execute(); - - $this->fail('Merge query failed.'); - } - catch (\Exception $e) { - $this->pass('Merge query failed.'); - } - - // Test a failed upsert query. - try { - $connection->upsert('test') - ->key('job') - ->fields(['job', 'age', 'name']) - ->values([ - 'job' => 'Presenter', - 'age' => 31, - 'name' => 'Tiffany', - ]) - ->execute(); - - $this->fail('Upset query failed.'); - } - catch (\Exception $e) { - $this->pass('Upset query failed.'); - } - - // Create the missing schema and insert a row. - $this->installSchema('database_test', ['test']); - $connection->insert('test') - ->fields(array( - 'name' => 'David', - 'age' => '24', - )) - ->execute(); - - // Commit the transaction. - unset($transaction); - - $saved_age = $connection->query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'David'))->fetchField(); - $this->assertEqual('24', $saved_age); - } - -} diff --git a/core/modules/system/src/Tests/Database/UpdateComplexTest.php b/core/modules/system/src/Tests/Database/UpdateComplexTest.php deleted file mode 100644 index a53938a..0000000 --- a/core/modules/system/src/Tests/Database/UpdateComplexTest.php +++ /dev/null @@ -1,150 +0,0 @@ -fields(array('job' => 'Musician')) - ->condition(db_or() - ->condition('name', 'John') - ->condition('name', 'Paul') - ); - $num_updated = $update->execute(); - $this->assertIdentical($num_updated, 2, 'Updated 2 records.'); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '2', 'Updated fields successfully.'); - } - - /** - * Tests WHERE IN clauses. - */ - function testInConditionUpdate() { - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition('name', array('John', 'Paul'), 'IN') - ->execute(); - $this->assertIdentical($num_updated, 2, 'Updated 2 records.'); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '2', 'Updated fields successfully.'); - } - - /** - * Tests WHERE NOT IN clauses. - */ - function testNotInConditionUpdate() { - // The o is lowercase in the 'NoT IN' operator, to make sure the operators - // work in mixed case. - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition('name', array('John', 'Paul', 'George'), 'NoT IN') - ->execute(); - $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '1', 'Updated fields successfully.'); - } - - /** - * Tests BETWEEN conditional clauses. - */ - function testBetweenConditionUpdate() { - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition('age', array(25, 26), 'BETWEEN') - ->execute(); - $this->assertIdentical($num_updated, 2, 'Updated 2 records.'); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '2', 'Updated fields successfully.'); - } - - /** - * Tests LIKE conditionals. - */ - function testLikeConditionUpdate() { - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition('name', '%ge%', 'LIKE') - ->execute(); - $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '1', 'Updated fields successfully.'); - } - - /** - * Tests UPDATE with expression values. - */ - function testUpdateExpression() { - $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField(); - $GLOBALS['larry_test'] = 1; - $num_updated = db_update('test') - ->condition('name', 'Ringo') - ->fields(array('job' => 'Musician')) - ->expression('age', 'age + :age', array(':age' => 4)) - ->execute(); - $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '1', 'Updated fields successfully.'); - - $person = db_query('SELECT * FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetch(); - $this->assertEqual($person->name, 'Ringo', 'Name set correctly.'); - $this->assertEqual($person->age, $before_age + 4, 'Age set correctly.'); - $this->assertEqual($person->job, 'Musician', 'Job set correctly.'); - $GLOBALS['larry_test'] = 0; - } - - /** - * Tests UPDATE with only expression values. - */ - function testUpdateOnlyExpression() { - $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField(); - $num_updated = db_update('test') - ->condition('name', 'Ringo') - ->expression('age', 'age + :age', array(':age' => 4)) - ->execute(); - $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); - - $after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField(); - $this->assertEqual($before_age + 4, $after_age, 'Age updated correctly'); - } - - /** - * Test UPDATE with a subselect value. - */ - function testSubSelectUpdate() { - $subselect = db_select('test_task', 't'); - $subselect->addExpression('MAX(priority) + :increment', 'max_priority', array(':increment' => 30)); - // Clone this to make sure we are running a different query when - // asserting. - $select = clone $subselect; - $query = db_update('test') - ->expression('age', $subselect) - ->condition('name', 'Ringo'); - // Save the number of rows that updated for assertion later. - $num_updated = $query->execute(); - $after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField(); - $expected_age = $select->execute()->fetchField(); - $this->assertEqual($after_age, $expected_age); - $this->assertEqual(1, $num_updated, t('Expected 1 row to be updated in subselect update query.')); - } - -} diff --git a/core/modules/system/src/Tests/Database/UpdateLobTest.php b/core/modules/system/src/Tests/Database/UpdateLobTest.php deleted file mode 100644 index 62c0f45..0000000 --- a/core/modules/system/src/Tests/Database/UpdateLobTest.php +++ /dev/null @@ -1,56 +0,0 @@ -assertTrue(strlen($data) === 15, 'Test data contains a NULL.'); - $id = db_insert('test_one_blob') - ->fields(array('blob1' => $data)) - ->execute(); - - $data .= $data; - db_update('test_one_blob') - ->condition('id', $id) - ->fields(array('blob1' => $data)) - ->execute(); - - $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc(); - $this->assertTrue($r['blob1'] === $data, format_string('Can update a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r)))); - } - - /** - * Confirms that we can update two blob columns in the same table. - */ - function testUpdateMultipleBlob() { - $id = db_insert('test_two_blobs') - ->fields(array( - 'blob1' => 'This is', - 'blob2' => 'a test', - )) - ->execute(); - - db_update('test_two_blobs') - ->condition('id', $id) - ->fields(array('blob1' => 'and so', 'blob2' => 'is this')) - ->execute(); - - $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc(); - $this->assertTrue($r['blob1'] === 'and so' && $r['blob2'] === 'is this', 'Can update multiple blobs per row.'); - } -} diff --git a/core/modules/system/src/Tests/Database/UpdateTest.php b/core/modules/system/src/Tests/Database/UpdateTest.php deleted file mode 100644 index 3d7f47d..0000000 --- a/core/modules/system/src/Tests/Database/UpdateTest.php +++ /dev/null @@ -1,162 +0,0 @@ -fields(array('name' => 'Tiffany')) - ->condition('id', 1) - ->execute(); - $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); - - $saved_name = db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 1))->fetchField(); - $this->assertIdentical($saved_name, 'Tiffany', 'Updated name successfully.'); - } - - /** - * Confirms updating to NULL. - */ - function testSimpleNullUpdate() { - $this->ensureSampleDataNull(); - $num_updated = db_update('test_null') - ->fields(array('age' => NULL)) - ->condition('name', 'Kermit') - ->execute(); - $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); - - $saved_age = db_query('SELECT age FROM {test_null} WHERE name = :name', array(':name' => 'Kermit'))->fetchField(); - $this->assertNull($saved_age, 'Updated name successfully.'); - } - - /** - * Confirms that we can update multiple records successfully. - */ - function testMultiUpdate() { - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition('job', 'Singer') - ->execute(); - $this->assertIdentical($num_updated, 2, 'Updated 2 records.'); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '2', 'Updated fields successfully.'); - } - - /** - * Confirms that we can update multiple records with a non-equality condition. - */ - function testMultiGTUpdate() { - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->condition('age', 26, '>') - ->execute(); - $this->assertIdentical($num_updated, 2, 'Updated 2 records.'); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '2', 'Updated fields successfully.'); - } - - /** - * Confirms that we can update multiple records with a where call. - */ - function testWhereUpdate() { - $num_updated = db_update('test') - ->fields(array('job' => 'Musician')) - ->where('age > :age', array(':age' => 26)) - ->execute(); - $this->assertIdentical($num_updated, 2, 'Updated 2 records.'); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '2', 'Updated fields successfully.'); - } - - /** - * Confirms that we can stack condition and where calls. - */ - function testWhereAndConditionUpdate() { - $update = db_update('test') - ->fields(array('job' => 'Musician')) - ->where('age > :age', array(':age' => 26)) - ->condition('name', 'Ringo'); - $num_updated = $update->execute(); - $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); - - $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); - $this->assertIdentical($num_matches, '1', 'Updated fields successfully.'); - } - - /** - * Tests updating with expressions. - */ - function testExpressionUpdate() { - // Ensure that expressions are handled properly. This should set every - // record's age to a square of itself. - $num_rows = db_update('test') - ->expression('age', 'age * age') - ->execute(); - $this->assertIdentical($num_rows, 4, 'Updated 4 records.'); - - $saved_name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => pow(26, 2)))->fetchField(); - $this->assertIdentical($saved_name, 'Paul', 'Successfully updated values using an algebraic expression.'); - } - - /** - * Tests return value on update. - */ - function testUpdateAffectedRows() { - // At 5am in the morning, all band members but those with a priority 1 task - // are sleeping. So we set their tasks to 'sleep'. 5 records match the - // condition and therefore are affected by the query, even though two of - // them actually don't have to be changed because their value was already - // 'sleep'. Still, execute() should return 5 affected rows, not only 3, - // because that's cross-db expected behavior. - $num_rows = db_update('test_task') - ->condition('priority', 1, '<>') - ->fields(array('task' => 'sleep')) - ->execute(); - $this->assertIdentical($num_rows, 5, 'Correctly returned 5 affected rows.'); - } - - /** - * Confirm that we can update the primary key of a record successfully. - */ - function testPrimaryKeyUpdate() { - $num_updated = db_update('test') - ->fields(array('id' => 42, 'name' => 'John')) - ->condition('id', 1) - ->execute(); - $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); - - $saved_name= db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 42))->fetchField(); - $this->assertIdentical($saved_name, 'John', 'Updated primary key successfully.'); - } - - /** - * Confirm that we can update values in a column with special name. - */ - function testSpecialColumnUpdate() { - $num_updated = db_update('test_special_columns') - ->fields(array('offset' => 'New offset value')) - ->condition('id', 1) - ->execute(); - $this->assertIdentical($num_updated, 1, 'Updated 1 special column record.'); - - $saved_value = db_query('SELECT "offset" FROM {test_special_columns} WHERE id = :id', array(':id' => 1))->fetchField(); - $this->assertIdentical($saved_value, 'New offset value', 'Updated special column name value successfully.'); - } -} diff --git a/core/modules/system/src/Tests/Database/UpsertTest.php b/core/modules/system/src/Tests/Database/UpsertTest.php deleted file mode 100644 index bd4db35..0000000 --- a/core/modules/system/src/Tests/Database/UpsertTest.php +++ /dev/null @@ -1,61 +0,0 @@ -query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - - $upsert = $connection->upsert('test_people') - ->key('job') - ->fields(['job', 'age', 'name']); - - // Add a new row. - $upsert->values([ - 'job' => 'Presenter', - 'age' => 31, - 'name' => 'Tiffany', - ]); - - // Update an existing row. - $upsert->values([ - 'job' => 'Speaker', - // The initial age was 30. - 'age' => 32, - 'name' => 'Meredith', - ]); - - $upsert->execute(); - - $num_records_after = $connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField(); - $this->assertEqual($num_records_before + 1, $num_records_after, 'Rows were inserted and updated properly.'); - - $person = $connection->query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch(); - $this->assertEqual($person->job, 'Presenter', 'Job set correctly.'); - $this->assertEqual($person->age, 31, 'Age set correctly.'); - $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.'); - - $person = $connection->query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); - $this->assertEqual($person->job, 'Speaker', 'Job was not changed.'); - $this->assertEqual($person->age, 32, 'Age updated correctly.'); - $this->assertEqual($person->name, 'Meredith', 'Name was not changed.'); - } - -} diff --git a/core/modules/system/src/Tests/Element/PathElementFormTest.php b/core/modules/system/src/Tests/Element/PathElementFormTest.php index bcbe534..308504b 100644 --- a/core/modules/system/src/Tests/Element/PathElementFormTest.php +++ b/core/modules/system/src/Tests/Element/PathElementFormTest.php @@ -42,7 +42,7 @@ class PathElementFormTest extends KernelTestBase implements FormInterface { */ protected function setUp() { parent::setUp(); - $this->installSchema('system', ['router', 'sequences', 'key_value_expire']); + $this->installSchema('system', ['sequences', 'key_value_expire']); $this->installEntitySchema('user'); \Drupal::service('router.builder')->rebuild(); /** @var \Drupal\user\RoleInterface $role */ diff --git a/core/modules/system/src/Tests/Entity/BundleConstraintValidatorTest.php b/core/modules/system/src/Tests/Entity/BundleConstraintValidatorTest.php deleted file mode 100644 index 2a433c5..0000000 --- a/core/modules/system/src/Tests/Entity/BundleConstraintValidatorTest.php +++ /dev/null @@ -1,77 +0,0 @@ -installEntitySchema('user'); - $this->typedData = $this->container->get('typed_data_manager'); - } - - /** - * Tests bundle constraint validation. - */ - public function testValidation() { - // Test with multiple values. - $this->assertValidation(array('foo', 'bar')); - // Test with a single string value as well. - $this->assertValidation('foo'); - } - - /** - * Executes the BundleConstraintValidator test for a given bundle. - * - * @param string|array $bundle - * Bundle/bundles to use as constraint option. - */ - protected function assertValidation($bundle) { - // Create a typed data definition with a Bundle constraint. - $definition = DataDefinition::create('entity_reference') - ->addConstraint('Bundle', $bundle); - - // Test the validation. - $node = $this->container->get('entity.manager')->getStorage('node')->create(array('type' => 'foo')); - - $typed_data = $this->typedData->create($definition, $node); - $violations = $typed_data->validate(); - $this->assertEqual($violations->count(), 0, 'Validation passed for correct value.'); - - // Test the validation when an invalid value is passed. - $page_node = $this->container->get('entity.manager')->getStorage('node')->create(array('type' => 'baz')); - - $typed_data = $this->typedData->create($definition, $page_node); - $violations = $typed_data->validate(); - $this->assertEqual($violations->count(), 1, 'Validation failed for incorrect value.'); - - // Make sure the information provided by a violation is correct. - $violation = $violations[0]; - $this->assertEqual($violation->getMessage(), t('The entity must be of bundle %bundle.', array('%bundle' => implode(', ', (array) $bundle))), 'The message for invalid value is correct.'); - $this->assertEqual($violation->getRoot(), $typed_data, 'Violation root is correct.'); - $this->assertEqual($violation->getInvalidValue(), $page_node, 'The invalid value is set correctly in the violation.'); - } -} diff --git a/core/modules/system/src/Tests/Entity/ConfigEntityImportTest.php b/core/modules/system/src/Tests/Entity/ConfigEntityImportTest.php index f7fbe1c..cf60631 100644 --- a/core/modules/system/src/Tests/Entity/ConfigEntityImportTest.php +++ b/core/modules/system/src/Tests/Entity/ConfigEntityImportTest.php @@ -9,7 +9,9 @@ use Drupal\Core\Entity\EntityWithPluginCollectionInterface; use Drupal\image\Entity\ImageStyle; +use Drupal\search\Entity\SearchPage; use Drupal\simpletest\WebTestBase; +use Drupal\system\Entity\Action; /** * Tests ConfigEntity importing. @@ -50,7 +52,7 @@ public function testConfigUpdateImport() { protected function doActionUpdate() { // Create a test action with a known label. $name = 'system.action.apple'; - $entity = entity_create('action', array( + $entity = Action::create(array( 'id' => 'apple', 'plugin' => 'action_message_action', )); @@ -159,10 +161,10 @@ protected function doImageStyleUpdate() { protected function doSearchPageUpdate() { // Create a test search page with a known label. $name = 'search.page.apple'; - $entity = entity_create('search_page', array( + $entity = SearchPage::create([ 'id' => 'apple', 'plugin' => 'search_extra_type_search', - )); + ]); $entity->save(); $this->checkSinglePluginConfigSync($entity, 'configuration', 'boost', 'bi'); diff --git a/core/modules/system/src/Tests/Entity/ConfigEntityQueryTest.php b/core/modules/system/src/Tests/Entity/ConfigEntityQueryTest.php deleted file mode 100644 index d3b7795..0000000 --- a/core/modules/system/src/Tests/Entity/ConfigEntityQueryTest.php +++ /dev/null @@ -1,669 +0,0 @@ -entities = array(); - $this->factory = $this->container->get('entity.query'); - - // These two are here to make sure that matchArray needs to go over several - // non-matches on every levels. - $array['level1']['level2a'] = 9; - $array['level1a']['level2'] = 9; - // The tests match array.level1.level2. - $array['level1']['level2'] = 1; - $entity = entity_create('config_query_test', array( - 'label' => $this->randomMachineName(), - 'id' => '1', - 'number' => 31, - 'array' => $array, - )); - $this->entities[] = $entity; - $entity->enforceIsNew(); - $entity->save(); - - $array['level1']['level2'] = 2; - $entity = entity_create('config_query_test', array( - 'label' => $this->randomMachineName(), - 'id' => '2', - 'number' => 41, - 'array' => $array, - )); - $this->entities[] = $entity; - $entity->enforceIsNew(); - $entity->save(); - - $array['level1']['level2'] = 1; - $entity = entity_create('config_query_test', array( - 'label' => 'test_prefix_' . $this->randomMachineName(), - 'id' => '3', - 'number' => 59, - 'array' => $array, - )); - $this->entities[] = $entity; - $entity->enforceIsNew(); - $entity->save(); - - $array['level1']['level2'] = 2; - $entity = entity_create('config_query_test', array( - 'label' => $this->randomMachineName() . '_test_suffix', - 'id' => '4', - 'number' => 26, - 'array' => $array, - )); - $this->entities[] = $entity; - $entity->enforceIsNew(); - $entity->save(); - - $array['level1']['level2'] = 3; - $entity = entity_create('config_query_test', array( - 'label' => $this->randomMachineName() . '_TEST_contains_' . $this->randomMachineName(), - 'id' => '5', - 'number' => 53, - 'array' => $array, - )); - $this->entities[] = $entity; - $entity->enforceIsNew(); - $entity->save(); - } - - /** - * Tests basic functionality. - */ - public function testConfigEntityQuery() { - // Run a test without any condition. - $this->queryResults = $this->factory->get('config_query_test') - ->execute(); - $this->assertResults(array('1', '2', '3', '4', '5')); - // No conditions, OR. - $this->queryResults = $this->factory->get('config_query_test', 'OR') - ->execute(); - $this->assertResults(array('1', '2', '3', '4', '5')); - - // Filter by ID with equality. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', '3') - ->execute(); - $this->assertResults(array('3')); - - // Filter by label with a known prefix. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('label', 'test_prefix', 'STARTS_WITH') - ->execute(); - $this->assertResults(array('3')); - - // Filter by label with a known suffix. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('label', 'test_suffix', 'ENDS_WITH') - ->execute(); - $this->assertResults(array('4')); - - // Filter by label with a known containing word. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('label', 'test_contains', 'CONTAINS') - ->execute(); - $this->assertResults(array('5')); - - // Filter by ID with the IN operator. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', array('2', '3'), 'IN') - ->execute(); - $this->assertResults(array('2', '3')); - - // Filter by ID with the implicit IN operator. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', array('2', '3')) - ->execute(); - $this->assertResults(array('2', '3')); - - // Filter by ID with the > operator. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', '3', '>') - ->execute(); - $this->assertResults(array('4', '5')); - - // Filter by ID with the >= operator. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', '3', '>=') - ->execute(); - $this->assertResults(array('3', '4', '5')); - - // Filter by ID with the <> operator. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', '3', '<>') - ->execute(); - $this->assertResults(array('1', '2', '4', '5')); - - // Filter by ID with the < operator. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', '3', '<') - ->execute(); - $this->assertResults(array('1', '2')); - - // Filter by ID with the <= operator. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', '3', '<=') - ->execute(); - $this->assertResults(array('1', '2', '3')); - - // Filter by two conditions on the same field. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('label', 'test_pref', 'STARTS_WITH') - ->condition('label', 'test_prefix', 'STARTS_WITH') - ->execute(); - $this->assertResults(array('3')); - - // Filter by two conditions on different fields. The first query matches for - // a different ID, so the result is empty. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('label', 'test_prefix', 'STARTS_WITH') - ->condition('id', '5') - ->execute(); - $this->assertResults(array()); - - // Filter by two different conditions on different fields. This time the - // first condition matches on one item, but the second one does as well. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('label', 'test_prefix', 'STARTS_WITH') - ->condition('id', '3') - ->execute(); - $this->assertResults(array('3')); - - // Filter by two different conditions, of which the first one matches for - // every entry, the second one as well, but just the third one filters so - // that just two are left. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', '1', '>=') - ->condition('number', 10, '>=') - ->condition('number', 50, '>=') - ->execute(); - $this->assertResults(array('3', '5')); - - // Filter with an OR condition group. - $this->queryResults = $this->factory->get('config_query_test', 'OR') - ->condition('id', 1) - ->condition('id', '2') - ->execute(); - $this->assertResults(array('1', '2')); - - // Simplify it with IN. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', array('1', '2')) - ->execute(); - $this->assertResults(array('1', '2')); - // Try explicit IN. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', array('1', '2'), 'IN') - ->execute(); - $this->assertResults(array('1', '2')); - // Try not IN. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', array('1', '2'), 'NOT IN') - ->execute(); - $this->assertResults(array('3', '4', '5')); - - // Filter with an OR condition group on different fields. - $this->queryResults = $this->factory->get('config_query_test', 'OR') - ->condition('id', 1) - ->condition('number', 41) - ->execute(); - $this->assertResults(array('1', '2')); - - // Filter with an OR condition group on different fields but matching on the - // same entity. - $this->queryResults = $this->factory->get('config_query_test', 'OR') - ->condition('id', 1) - ->condition('number', 31) - ->execute(); - $this->assertResults(array('1')); - - // NO simple conditions, YES complex conditions, 'AND'. - $query = $this->factory->get('config_query_test', 'AND'); - $and_condition_1 = $query->orConditionGroup() - ->condition('id', '2') - ->condition('label', $this->entities[0]->label); - $and_condition_2 = $query->orConditionGroup() - ->condition('id', 1) - ->condition('label', $this->entities[3]->label); - $this->queryResults = $query - ->condition($and_condition_1) - ->condition($and_condition_2) - ->execute(); - $this->assertResults(array('1')); - - // NO simple conditions, YES complex conditions, 'OR'. - $query = $this->factory->get('config_query_test', 'OR'); - $and_condition_1 = $query->andConditionGroup() - ->condition('id', 1) - ->condition('label', $this->entities[0]->label); - $and_condition_2 = $query->andConditionGroup() - ->condition('id', '2') - ->condition('label', $this->entities[1]->label); - $this->queryResults = $query - ->condition($and_condition_1) - ->condition($and_condition_2) - ->execute(); - $this->assertResults(array('1', '2')); - - // YES simple conditions, YES complex conditions, 'AND'. - $query = $this->factory->get('config_query_test', 'AND'); - $and_condition_1 = $query->orConditionGroup() - ->condition('id', '2') - ->condition('label', $this->entities[0]->label); - $and_condition_2 = $query->orConditionGroup() - ->condition('id', 1) - ->condition('label', $this->entities[3]->label); - $this->queryResults = $query - ->condition('number', 31) - ->condition($and_condition_1) - ->condition($and_condition_2) - ->execute(); - $this->assertResults(array('1')); - - // YES simple conditions, YES complex conditions, 'OR'. - $query = $this->factory->get('config_query_test', 'OR'); - $and_condition_1 = $query->orConditionGroup() - ->condition('id', '2') - ->condition('label', $this->entities[0]->label); - $and_condition_2 = $query->orConditionGroup() - ->condition('id', 1) - ->condition('label', $this->entities[3]->label); - $this->queryResults = $query - ->condition('number', 53) - ->condition($and_condition_1) - ->condition($and_condition_2) - ->execute(); - $this->assertResults(array('1', '2', '4', '5')); - - // Test the exists and notExists conditions. - $this->queryResults = $this->factory->get('config_query_test') - ->exists('id') - ->execute(); - $this->assertResults(array('1', '2', '3', '4', '5')); - - $this->queryResults = $this->factory->get('config_query_test') - ->exists('non-existent') - ->execute(); - $this->assertResults(array()); - - $this->queryResults = $this->factory->get('config_query_test') - ->notExists('id') - ->execute(); - $this->assertResults(array()); - - $this->queryResults = $this->factory->get('config_query_test') - ->notExists('non-existent') - ->execute(); - $this->assertResults(array('1', '2', '3', '4', '5')); - } - - /** - * Tests ID conditions. - */ - public function testStringIdConditions() { - // We need an entity with a non-numeric ID. - $entity = entity_create('config_query_test', array( - 'label' => $this->randomMachineName(), - 'id' => 'foo.bar', - )); - $this->entities[] = $entity; - $entity->enforceIsNew(); - $entity->save(); - - // Test 'STARTS_WITH' condition. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', 'foo.bar', 'STARTS_WITH') - ->execute(); - $this->assertResults(array('foo.bar')); - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', 'f', 'STARTS_WITH') - ->execute(); - $this->assertResults(array('foo.bar')); - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', 'miss', 'STARTS_WITH') - ->execute(); - $this->assertResults(array()); - - // Test 'CONTAINS' condition. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', 'foo.bar', 'CONTAINS') - ->execute(); - $this->assertResults(array('foo.bar')); - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', 'oo.ba', 'CONTAINS') - ->execute(); - $this->assertResults(array('foo.bar')); - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', 'miss', 'CONTAINS') - ->execute(); - $this->assertResults(array()); - - // Test 'ENDS_WITH' condition. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', 'foo.bar', 'ENDS_WITH') - ->execute(); - $this->assertResults(array('foo.bar')); - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', 'r', 'ENDS_WITH') - ->execute(); - $this->assertResults(array('foo.bar')); - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', 'miss', 'ENDS_WITH') - ->execute(); - $this->assertResults(array()); - } - - /** - * Tests count query. - */ - public function testCount() { - // Test count on no conditions. - $count = $this->factory->get('config_query_test') - ->count() - ->execute(); - $this->assertIdentical($count, count($this->entities)); - - // Test count on a complex query. - $query = $this->factory->get('config_query_test', 'OR'); - $and_condition_1 = $query->andConditionGroup() - ->condition('id', 1) - ->condition('label', $this->entities[0]->label); - $and_condition_2 = $query->andConditionGroup() - ->condition('id', '2') - ->condition('label', $this->entities[1]->label); - $count = $query - ->condition($and_condition_1) - ->condition($and_condition_2) - ->count() - ->execute(); - $this->assertIdentical($count, 2); - } - - /** - * Tests sorting and range on config entity queries. - */ - public function testSortRange() { - // Sort by simple ascending/descending. - $this->queryResults = $this->factory->get('config_query_test') - ->sort('number', 'DESC') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('3', '5', '2', '1', '4')); - - $this->queryResults = $this->factory->get('config_query_test') - ->sort('number', 'ASC') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('4', '1', '2', '5', '3')); - - // Apply some filters and sort. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', '3', '>') - ->sort('number', 'DESC') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('5', '4')); - - $this->queryResults = $this->factory->get('config_query_test') - ->condition('id', '3', '>') - ->sort('number', 'ASC') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('4', '5')); - - // Apply a pager and sort. - $this->queryResults = $this->factory->get('config_query_test') - ->sort('number', 'DESC') - ->range('2', '2') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('2', '1')); - - $this->queryResults = $this->factory->get('config_query_test') - ->sort('number', 'ASC') - ->range('2', '2') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('2', '5')); - - // Add a range to a query without a start parameter. - $this->queryResults = $this->factory->get('config_query_test') - ->range(0, '3') - ->sort('id', 'ASC') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('1', '2', '3')); - - // Apply a pager with limit 4. - $this->queryResults = $this->factory->get('config_query_test') - ->pager('4', 0) - ->sort('id', 'ASC') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('1', '2', '3', '4')); - } - - /** - * Tests sorting with tableSort on config entity queries. - */ - public function testTableSort() { - $header = array( - array('data' => t('ID'), 'specifier' => 'id'), - array('data' => t('Number'), 'specifier' => 'number'), - ); - - // Sort key: id - // Sorting with 'DESC' upper case - $this->queryResults = $this->factory->get('config_query_test') - ->tableSort($header) - ->sort('id', 'DESC') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('5', '4', '3', '2', '1')); - - // Sorting with 'ASC' upper case - $this->queryResults = $this->factory->get('config_query_test') - ->tableSort($header) - ->sort('id', 'ASC') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('1', '2', '3', '4', '5')); - - // Sorting with 'desc' lower case - $this->queryResults = $this->factory->get('config_query_test') - ->tableSort($header) - ->sort('id', 'desc') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('5', '4', '3', '2', '1')); - - // Sorting with 'asc' lower case - $this->queryResults = $this->factory->get('config_query_test') - ->tableSort($header) - ->sort('id', 'asc') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('1', '2', '3', '4', '5')); - - // Sort key: number - // Sorting with 'DeSc' mixed upper and lower case - $this->queryResults = $this->factory->get('config_query_test') - ->tableSort($header) - ->sort('number', 'DeSc') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('3', '5', '2', '1', '4')); - - // Sorting with 'AsC' mixed upper and lower case - $this->queryResults = $this->factory->get('config_query_test') - ->tableSort($header) - ->sort('number', 'AsC') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('4', '1', '2', '5', '3')); - - // Sorting with 'dEsC' mixed upper and lower case - $this->queryResults = $this->factory->get('config_query_test') - ->tableSort($header) - ->sort('number', 'dEsC') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('3', '5', '2', '1', '4')); - - // Sorting with 'aSc' mixed upper and lower case - $this->queryResults = $this->factory->get('config_query_test') - ->tableSort($header) - ->sort('number', 'aSc') - ->execute(); - $this->assertIdentical(array_values($this->queryResults), array('4', '1', '2', '5', '3')); - } - - /** - * Tests dotted path matching. - */ - public function testDotted() { - $this->queryResults = $this->factory->get('config_query_test') - ->condition('array.level1.*', 1) - ->execute(); - $this->assertResults(array('1', '3')); - $this->queryResults = $this->factory->get('config_query_test') - ->condition('*.level1.level2', 2) - ->execute(); - $this->assertResults(array('2', '4')); - $this->queryResults = $this->factory->get('config_query_test') - ->condition('array.level1.*', 3) - ->execute(); - $this->assertResults(array('5')); - $this->queryResults = $this->factory->get('config_query_test') - ->condition('array.level1.level2', 3) - ->execute(); - $this->assertResults(array('5')); - // Make sure that values on the wildcard level do not match if there are - // sub-keys defined. This must not find anything even if entity 2 has a - // top-level key number with value 41. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('*.level1.level2', 41) - ->execute(); - $this->assertResults(array()); - } - - /** - * Tests case sensitivity. - */ - public function testCaseSensitivity() { - // Filter by label with a known containing case-sensitive word. - $this->queryResults = $this->factory->get('config_query_test') - ->condition('label', 'TEST', 'CONTAINS') - ->execute(); - $this->assertResults(array('3', '4', '5')); - - $this->queryResults = $this->factory->get('config_query_test') - ->condition('label', 'test', 'CONTAINS') - ->execute(); - $this->assertResults(array('3', '4', '5')); - } - - /** - * Tests lookup keys are added to the key value store. - */ - public function testLookupKeys() { - \Drupal::service('state')->set('config_test.lookup_keys', TRUE); - \Drupal::entityManager()->clearCachedDefinitions(); - $key_value = $this->container->get('keyvalue')->get(QueryFactory::CONFIG_LOOKUP_PREFIX . 'config_test'); - - $test_entities = []; - $entity = entity_create('config_test', array( - 'label' => $this->randomMachineName(), - 'id' => '1', - 'style' => 'test', - )); - $test_entities[$entity->getConfigDependencyName()] = $entity; - $entity->enforceIsNew(); - $entity->save(); - - - $expected[] = $entity->getConfigDependencyName(); - $this->assertEqual($expected, $key_value->get('style:test')); - - $entity = entity_create('config_test', array( - 'label' => $this->randomMachineName(), - 'id' => '2', - 'style' => 'test', - )); - $test_entities[$entity->getConfigDependencyName()] = $entity; - $entity->enforceIsNew(); - $entity->save(); - $expected[] = $entity->getConfigDependencyName(); - $this->assertEqual($expected, $key_value->get('style:test')); - - $entity = entity_create('config_test', array( - 'label' => $this->randomMachineName(), - 'id' => '3', - 'style' => 'blah', - )); - $entity->enforceIsNew(); - $entity->save(); - // Do not add this entity to the list of expected result as it has a - // different value. - $this->assertEqual($expected, $key_value->get('style:test')); - $this->assertEqual([$entity->getConfigDependencyName()], $key_value->get('style:blah')); - - // Ensure that a delete clears a key. - $entity->delete(); - $this->assertEqual([], $key_value->get('style:blah')); - - // Ensure that delete only clears one key. - $entity_id = array_pop($expected); - $test_entities[$entity_id]->delete(); - $this->assertEqual($expected, $key_value->get('style:test')); - $entity_id = array_pop($expected); - $test_entities[$entity_id]->delete(); - $this->assertEqual($expected, $key_value->get('style:test')); - } - - /** - * Asserts the results as expected regardless of order. - * - * @param array $expected - * Array of expected entity IDs. - */ - protected function assertResults($expected) { - $this->assertIdentical(count($this->queryResults), count($expected)); - foreach ($expected as $value) { - // This also tests whether $this->queryResults[$value] is even set at all. - $this->assertIdentical($this->queryResults[$value], $value); - } - } - -} diff --git a/core/modules/system/src/Tests/Entity/ContentEntityChangedTest.php b/core/modules/system/src/Tests/Entity/ContentEntityChangedTest.php deleted file mode 100644 index 94c9b12..0000000 --- a/core/modules/system/src/Tests/Entity/ContentEntityChangedTest.php +++ /dev/null @@ -1,545 +0,0 @@ -save(); - ConfigurableLanguage::createFromLangcode('fr')->save(); - - $this->installEntitySchema('entity_test_mul_changed'); - $this->installEntitySchema('entity_test_mulrev_changed'); - - $this->mulChangedStorage = $this->entityManager->getStorage('entity_test_mul_changed'); - $this->mulRevChangedStorage = $this->entityManager->getStorage('entity_test_mulrev_changed'); - } - - /** - * Tests basic EntityChangedInterface functionality. - */ - public function testChanged() { - $user1 = $this->createUser(); - $user2 = $this->createUser(); - - // Create a test entity. - $entity = EntityTestMulChanged::create(array( - 'name' => $this->randomString(), - 'user_id' => $user1->id(), - 'language' => 'en', - )); - $entity->save(); - - $this->assertTrue( - $entity->getChangedTime() >= REQUEST_TIME, - 'Changed time of original language is valid.' - ); - - // We can't assert equality here because the created time is set to the - // request time, while instances of ChangedTestItem use the current - // timestamp every time. Therefor we check if the changed timestamp is - // between the created time and now. - $this->assertTrue( - ($entity->getChangedTime() >= $entity->get('created')->value) && - (($entity->getChangedTime() - $entity->get('created')->value) <= time() - REQUEST_TIME), - 'Changed and created time of original language can be assumed to be identical.' - ); - - $this->assertEqual( - $entity->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), - 'Changed time of original language is the same as changed time across all translations.' - ); - - $changed_en = $entity->getChangedTime(); - - /** @var \Drupal\entity_test\Entity\EntityTestMulRevChanged $german */ - $german = $entity->addTranslation('de'); - - $entity->save(); - - $this->assertEqual( - $entity->getChangedTime(), $changed_en, - 'Changed time of original language did not change.' - ); - - $this->assertTrue( - $german->getChangedTime() > $entity->getChangedTime(), - 'Changed time of the German translation is newer then the original language.' - ); - - $this->assertEqual( - $german->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), - 'Changed time of the German translation is the newest time across all translations.' - ); - - $changed_de = $german->getChangedTime(); - - $entity->save(); - - $this->assertEqual( - $entity->getChangedTime(), $changed_en, - 'Changed time of original language did not change.' - ); - - $this->assertEqual( - $german->getChangedTime(), $changed_de, - 'Changed time of the German translation did not change.' - ); - - $entity->setOwner($user2); - - $entity->save(); - - $this->assertTrue( - $entity->getChangedTime() > $changed_en, - 'Changed time of original language did change.' - ); - - $this->assertEqual( - $german->getChangedTime(), $changed_de, - 'Changed time of the German translation did not change.' - ); - - $this->assertTrue( - $entity->getChangedTime() > $german->getChangedTime(), - 'Changed time of original language is newer then the German translation.' - ); - - $this->assertEqual( - $entity->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), - 'Changed time of the original language is the newest time across all translations.' - ); - - $changed_en = $entity->getChangedTime(); - - // Save entity without any changes. - $entity->save(); - - $this->assertEqual( - $entity->getChangedTime(), $changed_en, - 'Changed time of original language did not change.' - ); - - $this->assertEqual( - $german->getChangedTime(), $changed_de, - 'Changed time of the German translation did not change.' - ); - - // At this point the changed time of the original language (en) is newer - // than the changed time of the German translation. Now test that entity - // queries work as expected. - $query = $this->mulChangedStorage->getQuery(); - $ids = $query->condition('changed', $changed_en)->execute(); - - $this->assertEqual( - reset($ids), $entity->id(), - 'Entity query can access changed time of original language.' - ); - - $query = $this->mulChangedStorage->getQuery(); - $ids = $query->condition('changed', $changed_en, '=', 'en')->execute(); - - $this->assertEqual( - reset($ids), $entity->id(), - 'Entity query can access changed time of original language by setting the original language as condition.' - ); - - $query = $this->mulChangedStorage->getQuery(); - $ids = $query->condition('changed', $changed_de, '=', 'en')->execute(); - - $this->assertFalse( - $ids, - 'There\'s no original entity stored having the changed time of the German translation.' - ); - - $query = $this->mulChangedStorage->getQuery(); - $ids = $query->condition('changed', $changed_en)->condition('default_langcode', '1')->execute(); - - $this->assertEqual( - reset($ids), $entity->id(), - 'Entity query can access changed time of default language.' - ); - - $query = $this->mulChangedStorage->getQuery(); - $ids = $query->condition('changed', $changed_de)->condition('default_langcode', '1')->execute(); - - $this->assertFalse( - $ids, - 'There\'s no entity stored using the default language having the changed time of the German translation.' - ); - - $query = $this->mulChangedStorage->getQuery(); - $ids = $query->condition('changed', $changed_de)->execute(); - - $this->assertEqual( - reset($ids), $entity->id(), - 'Entity query can access changed time of the German translation.' - ); - - $query = $this->mulChangedStorage->getQuery(); - $ids = $query->condition('changed', $changed_de, '=', 'de')->execute(); - - $this->assertEqual( - reset($ids), $entity->id(), - 'Entity query can access changed time of the German translation.' - ); - - $query = $this->mulChangedStorage->getQuery(); - $ids = $query->condition('changed', $changed_en, '=', 'de')->execute(); - - $this->assertFalse( - $ids, - 'There\'s no German translation stored having the changed time of the original language.' - ); - - $query = $this->mulChangedStorage->getQuery(); - $ids = $query->condition('changed', $changed_de, '>')->execute(); - - $this->assertEqual( - reset($ids), $entity->id(), - 'Entity query can access changed time regardless of translation.' - ); - - $query = $this->mulChangedStorage->getQuery(); - $ids = $query->condition('changed', $changed_en, '<')->execute(); - - $this->assertEqual( - reset($ids), $entity->id(), - 'Entity query can access changed time regardless of translation.' - ); - - $query = $this->mulChangedStorage->getQuery(); - $ids = $query->condition('changed', 0, '>')->execute(); - - $this->assertEqual( - reset($ids), $entity->id(), - 'Entity query can access changed time regardless of translation.' - ); - - $query = $this->mulChangedStorage->getQuery(); - $ids = $query->condition('changed', $changed_en, '>')->execute(); - - $this->assertFalse( - $ids, - 'Entity query can access changed time regardless of translation.' - ); - } - - /** - * Tests revisionable EntityChangedInterface functionality. - */ - public function testRevisionChanged() { - $user1 = $this->createUser(); - $user2 = $this->createUser(); - - // Create a test entity. - $entity = EntityTestMulRevChanged::create(array( - 'name' => $this->randomString(), - 'user_id' => $user1->id(), - 'language' => 'en', - )); - $entity->save(); - - $this->assertTrue( - $entity->getChangedTime() >= REQUEST_TIME, - 'Changed time of original language is valid.' - ); - - // We can't assert equality here because the created time is set to the - // request time while instances of ChangedTestItem use the current - // timestamp every time. - $this->assertTrue( - ($entity->getChangedTime() >= $entity->get('created')->value) && - (($entity->getChangedTime() - $entity->get('created')->value) <= time() - REQUEST_TIME), - 'Changed and created time of original language can be assumed to be identical.' - ); - - $this->assertEqual( - $entity->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), - 'Changed time of original language is the same as changed time across all translations.' - ); - - $this->assertTrue( - $this->getRevisionTranslationAffectedFlag($entity), - 'Changed flag of original language is set for a new entity.' - ); - - $changed_en = $entity->getChangedTime(); - - $entity->setNewRevision(); - // Save entity without any changes but create new revision. - $entity->save(); - // A new revision without any changes should not set a new changed time. - $this->assertEqual( - $entity->getChangedTime(), $changed_en, - 'Changed time of original language did not change.' - ); - - $this->assertFalse( - $this->getRevisionTranslationAffectedFlag($entity), - 'Changed flag of original language is not set for new revision without changes.' - ); - - $entity->setNewRevision(); - $entity->setOwner($user2); - $entity->save(); - - $this->assertTrue( - $entity->getChangedTime() > $changed_en, - 'Changed time of original language has been updated by new revision.' - ); - - $this->assertTrue( - $this->getRevisionTranslationAffectedFlag($entity), - 'Changed flag of original language is set for new revision with changes.' - ); - - $changed_en = $entity->getChangedTime(); - - /** @var \Drupal\entity_test\Entity\EntityTestMulRevChanged $german */ - $german = $entity->addTranslation('de'); - - $entity->save(); - - $this->assertEqual( - $entity->getChangedTime(), $changed_en, - 'Changed time of original language did not change.' - ); - - $this->assertTrue( - $german->getChangedTime() > $entity->getChangedTime(), - 'Changed time of the German translation is newer then the original language.' - ); - - $this->assertEqual( - $german->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), - 'Changed time of the German translation is the newest time across all translations.' - ); - - $this->assertTrue( - $this->getRevisionTranslationAffectedFlag($entity), - 'Changed flag of original language is not reset by adding a new translation.' - ); - - $this->assertTrue( - $this->getRevisionTranslationAffectedFlag($german), - 'Changed flag of German translation is set when adding the translation.' - ); - - $changed_de = $german->getChangedTime(); - - $entity->setNewRevision(); - // Save entity without any changes but create new revision. - $entity->save(); - - $this->assertEqual( - $entity->getChangedTime(), $changed_en, - 'Changed time of original language did not change.' - ); - - $this->assertEqual( - $german->getChangedTime(), $changed_de, - 'Changed time of the German translation did not change.' - ); - - $this->assertFalse( - $this->getRevisionTranslationAffectedFlag($entity), - 'Changed flag of original language is not set for new revision without changes.' - ); - - $this->assertFalse( - $this->getRevisionTranslationAffectedFlag($german), - 'Changed flag of the German translation is not set for new revision without changes.' - ); - - $entity->setNewRevision(); - $german->setOwner($user2); - $entity->save(); - - $this->assertEqual( - $entity->getChangedTime(), $changed_en, - 'Changed time of original language did not change.' - ); - - $this->assertTrue( - $german->getChangedTime() > $changed_de, - 'Changed time of the German translation did change.' - ); - - $this->assertEqual( - $german->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), - 'Changed time of the German translation is the newest time across all translations.' - ); - - $this->assertFalse( - $this->getRevisionTranslationAffectedFlag($entity), - 'Changed flag of original language is not set when changing the German Translation.' - ); - - $this->assertTrue( - $this->getRevisionTranslationAffectedFlag($german), - 'Changed flag of German translation is set when changing the German translation.' - ); - - $french = $entity->addTranslation('fr'); - - $entity->setNewRevision(); - $entity->save(); - - $this->assertEqual( - $entity->getChangedTime(), $changed_en, - 'Changed time of original language did not change.' - ); - - $this->assertTrue( - $french->getChangedTime() > $entity->getChangedTime(), - 'Changed time of the French translation is newer then the original language.' - ); - - $this->assertTrue( - $french->getChangedTime() > $entity->getChangedTime(), - 'Changed time of the French translation is newer then the German translation.' - ); - - $this->assertEqual( - $french->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), - 'Changed time of the French translation is the newest time across all translations.' - ); - - $this->assertFalse( - $this->getRevisionTranslationAffectedFlag($entity), - 'Changed flag of original language is reset by adding a new translation and a new revision.' - ); - - $this->assertFalse( - $this->getRevisionTranslationAffectedFlag($german), - 'Changed flag of German translation is reset by adding a new translation and a new revision.' - ); - - $this->assertTrue( - $this->getRevisionTranslationAffectedFlag($french), - 'Changed flag of French translation is set when adding the translation and a new revision.' - ); - - $entity->removeTranslation('fr'); - - $entity->setNewRevision(); - $entity->save(); - - // This block simulates exactly the flow of a node form submission of a new - // translation and a new revision. - $form_entity_builder_entity = EntityTestMulRevChanged::load($entity->id()); - // ContentTranslationController::prepareTranslation(). - $form_entity_builder_entity = $form_entity_builder_entity->addTranslation('fr', $form_entity_builder_entity->toArray()); - // EntityForm::buildEntity() during form submit. - $form_entity_builder_clone = clone $form_entity_builder_entity; - // NodeForm::submitForm(). - $form_entity_builder_clone->setNewRevision(); - // EntityForm::save(). - $form_entity_builder_clone->save(); - - // The assertion fails unless https://www.drupal.org/node/2513094 is - // committed. - $this->assertFalse( - $this->getRevisionTranslationAffectedFlag($entity), - 'Changed flag of original language is reset by adding a new translation and a new revision.' - ); - - $this->assertFalse( - $this->getRevisionTranslationAffectedFlag($german), - 'Changed flag of German translation is reset by adding a new translation and a new revision.' - ); - - $this->assertTrue( - $this->getRevisionTranslationAffectedFlag($french), - 'Changed flag of French translation is set when adding the translation and a new revision.' - ); - - $german->setOwner($user1); - $german->setRevisionTranslationAffected(FALSE); - $entity->save(); - - $this->assertFalse( - $this->getRevisionTranslationAffectedFlag($german), - 'German translation changed but the changed flag is reset manually.' - ); - - $entity->setNewRevision(); - $german->setRevisionTranslationAffected(TRUE); - $entity->save(); - - $this->assertTrue( - $this->getRevisionTranslationAffectedFlag($german), - 'German translation is not changed and a new revision is created but the changed flag is set manually.' - ); - - $german->setOwner($user2); - $entity->setNewRevision(); - $german->setRevisionTranslationAffected(FALSE); - $entity->save(); - - $this->assertFalse( - $this->getRevisionTranslationAffectedFlag($german), - 'German translation changed and a new revision is created but the changed flag is reset manually.' - ); - - } - - /** - * Retrieves the revision translation affected flag value. - * - * @param \Drupal\entity_test\Entity\EntityTestMulRevChanged $entity - * The entity object to be checked. - * - * @return bool - * The flag value. - */ - protected function getRevisionTranslationAffectedFlag(EntityTestMulRevChanged $entity) { - $query = $this->mulRevChangedStorage->getQuery(); - $ids = $query->condition('revision_translation_affected', 1, '=', $entity->language()->getId())->execute(); - $id = reset($ids); - return (bool) ($id == $entity->id()); - } - -} diff --git a/core/modules/system/src/Tests/Entity/ContentEntityCloneTest.php b/core/modules/system/src/Tests/Entity/ContentEntityCloneTest.php deleted file mode 100644 index 77d1603..0000000 --- a/core/modules/system/src/Tests/Entity/ContentEntityCloneTest.php +++ /dev/null @@ -1,70 +0,0 @@ -save(); - - $this->installEntitySchema('entity_test_mul'); - } - - /** - * Tests if entity references on fields are still correct after cloning. - */ - public function testFieldEntityReferenceAfterClone() { - $user = $this->createUser(); - - // Create a test entity. - $entity = EntityTestMul::create([ - 'name' => $this->randomString(), - 'user_id' => $user->id(), - 'language' => 'en', - ]); - - $clone = clone $entity->addTranslation('de'); - - $this->assertEqual($entity->getTranslationLanguages(), $clone->getTranslationLanguages(), 'The entity and its clone have the same translation languages.'); - - $default_langcode = $entity->getUntranslated()->language()->getId(); - foreach (array_keys($clone->getTranslationLanguages()) as $langcode) { - $translation = $clone->getTranslation($langcode); - foreach ($translation->getFields() as $field_name => $field) { - if ($field->getFieldDefinition()->isTranslatable()) { - $args = ['%field_name' => $field_name, '%langcode' => $langcode]; - $this->assertEqual($langcode, $field->getEntity()->language()->getId(), format_string('Translatable field %field_name on translation %langcode has correct entity reference in translation %langcode after cloning.', $args)); - } - else { - $args = ['%field_name' => $field_name, '%langcode' => $langcode, '%default_langcode' => $default_langcode]; - $this->assertEqual($default_langcode, $field->getEntity()->language()->getId(), format_string('Non translatable field %field_name on translation %langcode has correct entity reference in the default translation %default_langcode after cloning.', $args)); - } - } - } - } - -} diff --git a/core/modules/system/src/Tests/Entity/ContentEntityNullStorageTest.php b/core/modules/system/src/Tests/Entity/ContentEntityNullStorageTest.php deleted file mode 100644 index 3eeee3b..0000000 --- a/core/modules/system/src/Tests/Entity/ContentEntityNullStorageTest.php +++ /dev/null @@ -1,83 +0,0 @@ -assertIdentical(0, \Drupal::entityQuery('contact_message')->count()->execute(), 'Counting a null storage returns 0.'); - $this->assertIdentical([], \Drupal::entityQuery('contact_message')->execute(), 'Querying a null storage returns an empty array.'); - $this->assertIdentical([], \Drupal::entityQuery('contact_message')->condition('contact_form', 'test')->execute(), 'Querying a null storage returns an empty array and conditions are ignored.'); - $this->assertIdentical([], \Drupal::entityQueryAggregate('contact_message')->aggregate('name', 'AVG')->execute(), 'Aggregate querying a null storage returns an empty array'); - - } - - /** - * Tests deleting a contact form entity via a configuration import. - * - * @see \Drupal\Core\Entity\Event\BundleConfigImportValidate - */ - public function testDeleteThroughImport() { - $contact_form = ContactForm::create(['id' => 'test']); - $contact_form->save(); - - $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync')); - - // Set up the ConfigImporter object for testing. - $storage_comparer = new StorageComparer( - $this->container->get('config.storage.sync'), - $this->container->get('config.storage'), - $this->container->get('config.manager') - ); - $config_importer = new ConfigImporter( - $storage_comparer->createChangelist(), - $this->container->get('event_dispatcher'), - $this->container->get('config.manager'), - $this->container->get('lock'), - $this->container->get('config.typed'), - $this->container->get('module_handler'), - $this->container->get('module_installer'), - $this->container->get('theme_handler'), - $this->container->get('string_translation') - ); - - // Delete the contact message in sync. - $sync = $this->container->get('config.storage.sync'); - $sync->delete($contact_form->getConfigDependencyName()); - - // Import. - $config_importer->reset()->import(); - $this->assertNull(ContactForm::load($contact_form->id()), 'The contact form has been deleted.'); - } - -} diff --git a/core/modules/system/src/Tests/Entity/Element/EntityAutocompleteElementFormTest.php b/core/modules/system/src/Tests/Entity/Element/EntityAutocompleteElementFormTest.php deleted file mode 100644 index 3e0386c..0000000 --- a/core/modules/system/src/Tests/Entity/Element/EntityAutocompleteElementFormTest.php +++ /dev/null @@ -1,332 +0,0 @@ -installSchema('system', ['router', 'key_value_expire']); - \Drupal::service('router.builder')->rebuild(); - - $this->testUser = User::create(array( - 'name' => 'foobar1', - 'mail' => 'foobar1@example.com', - )); - $this->testUser->save(); - \Drupal::service('current_user')->setAccount($this->testUser); - - $this->testAutocreateUser = User::create(array( - 'name' => 'foobar2', - 'mail' => 'foobar2@example.com', - )); - $this->testAutocreateUser->save(); - - for ($i = 1; $i < 3; $i++) { - $entity = EntityTest::create(array( - 'name' => $this->randomMachineName() - )); - $entity->save(); - $this->referencedEntities[] = $entity; - } - } - - /** - * {@inheritdoc} - */ - public function getFormId() { - return 'test_entity_autocomplete'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state) { - $form['single'] = array( - '#type' => 'entity_autocomplete', - '#target_type' => 'entity_test', - ); - $form['single_autocreate'] = array( - '#type' => 'entity_autocomplete', - '#target_type' => 'entity_test', - '#autocreate' => array( - 'bundle' => 'entity_test', - ), - ); - $form['single_autocreate_specific_uid'] = array( - '#type' => 'entity_autocomplete', - '#target_type' => 'entity_test', - '#autocreate' => array( - 'bundle' => 'entity_test', - 'uid' => $this->testAutocreateUser->id(), - ), - ); - - $form['tags'] = array( - '#type' => 'entity_autocomplete', - '#target_type' => 'entity_test', - '#tags' => TRUE, - ); - $form['tags_autocreate'] = array( - '#type' => 'entity_autocomplete', - '#target_type' => 'entity_test', - '#tags' => TRUE, - '#autocreate' => array( - 'bundle' => 'entity_test', - ), - ); - $form['tags_autocreate_specific_uid'] = array( - '#type' => 'entity_autocomplete', - '#target_type' => 'entity_test', - '#tags' => TRUE, - '#autocreate' => array( - 'bundle' => 'entity_test', - 'uid' => $this->testAutocreateUser->id(), - ), - ); - - $form['single_no_validate'] = array( - '#type' => 'entity_autocomplete', - '#target_type' => 'entity_test', - '#validate_reference' => FALSE, - ); - $form['single_autocreate_no_validate'] = array( - '#type' => 'entity_autocomplete', - '#target_type' => 'entity_test', - '#validate_reference' => FALSE, - '#autocreate' => array( - 'bundle' => 'entity_test', - ), - ); - - $form['single_access'] = array( - '#type' => 'entity_autocomplete', - '#target_type' => 'entity_test', - '#default_value' => $this->referencedEntities[0], - ); - $form['tags_access'] = array( - '#type' => 'entity_autocomplete', - '#target_type' => 'entity_test', - '#tags' => TRUE, - '#default_value' => array($this->referencedEntities[0], $this->referencedEntities[1]), - ); - - return $form; - } - - /** - * {@inheritdoc} - */ - public function submitForm(array &$form, FormStateInterface $form_state) { } - - /** - * {@inheritdoc} - */ - public function validateForm(array &$form, FormStateInterface $form_state) { } - - /** - * Tests valid entries in the EntityAutocomplete Form API element. - */ - public function testValidEntityAutocompleteElement() { - $form_state = (new FormState()) - ->setValues([ - 'single' => $this->getAutocompleteInput($this->referencedEntities[0]), - 'single_autocreate' => 'single - autocreated entity label', - 'single_autocreate_specific_uid' => 'single - autocreated entity label with specific uid', - 'tags' => $this->getAutocompleteInput($this->referencedEntities[0]) . ', ' . $this->getAutocompleteInput($this->referencedEntities[1]), - 'tags_autocreate' => - $this->getAutocompleteInput($this->referencedEntities[0]) - . ', tags - autocreated entity label, ' - . $this->getAutocompleteInput($this->referencedEntities[1]), - 'tags_autocreate_specific_uid' => - $this->getAutocompleteInput($this->referencedEntities[0]) - . ', tags - autocreated entity label with specific uid, ' - . $this->getAutocompleteInput($this->referencedEntities[1]), - ]); - $form_builder = $this->container->get('form_builder'); - $form_builder->submitForm($this, $form_state); - - // Valid form state. - $this->assertEqual(count($form_state->getErrors()), 0); - - // Test the 'single' element. - $this->assertEqual($form_state->getValue('single'), $this->referencedEntities[0]->id()); - - // Test the 'single_autocreate' element. - $value = $form_state->getValue('single_autocreate'); - $this->assertEqual($value['entity']->label(), 'single - autocreated entity label'); - $this->assertEqual($value['entity']->bundle(), 'entity_test'); - $this->assertEqual($value['entity']->getOwnerId(), $this->testUser->id()); - - // Test the 'single_autocreate_specific_uid' element. - $value = $form_state->getValue('single_autocreate_specific_uid'); - $this->assertEqual($value['entity']->label(), 'single - autocreated entity label with specific uid'); - $this->assertEqual($value['entity']->bundle(), 'entity_test'); - $this->assertEqual($value['entity']->getOwnerId(), $this->testAutocreateUser->id()); - - // Test the 'tags' element. - $expected = array( - array('target_id' => $this->referencedEntities[0]->id()), - array('target_id' => $this->referencedEntities[1]->id()), - ); - $this->assertEqual($form_state->getValue('tags'), $expected); - - // Test the 'single_autocreate' element. - $value = $form_state->getValue('tags_autocreate'); - // First value is an existing entity. - $this->assertEqual($value[0]['target_id'], $this->referencedEntities[0]->id()); - // Second value is an autocreated entity. - $this->assertTrue(!isset($value[1]['target_id'])); - $this->assertEqual($value[1]['entity']->label(), 'tags - autocreated entity label'); - $this->assertEqual($value[1]['entity']->getOwnerId(), $this->testUser->id()); - // Third value is an existing entity. - $this->assertEqual($value[2]['target_id'], $this->referencedEntities[1]->id()); - - // Test the 'tags_autocreate_specific_uid' element. - $value = $form_state->getValue('tags_autocreate_specific_uid'); - // First value is an existing entity. - $this->assertEqual($value[0]['target_id'], $this->referencedEntities[0]->id()); - // Second value is an autocreated entity. - $this->assertTrue(!isset($value[1]['target_id'])); - $this->assertEqual($value[1]['entity']->label(), 'tags - autocreated entity label with specific uid'); - $this->assertEqual($value[1]['entity']->getOwnerId(), $this->testAutocreateUser->id()); - // Third value is an existing entity. - $this->assertEqual($value[2]['target_id'], $this->referencedEntities[1]->id()); - } - - /** - * Tests invalid entries in the EntityAutocomplete Form API element. - */ - public function testInvalidEntityAutocompleteElement() { - $form_builder = $this->container->get('form_builder'); - - // Test 'single' with a entity label that doesn't exist - $form_state = (new FormState()) - ->setValues([ - 'single' => 'single - non-existent label', - ]); - $form_builder->submitForm($this, $form_state); - $this->assertEqual(count($form_state->getErrors()), 1); - $this->assertEqual($form_state->getErrors()['single'], t('There are no entities matching "%value".', array('%value' => 'single - non-existent label'))); - - // Test 'single' with a entity ID that doesn't exist. - $form_state = (new FormState()) - ->setValues([ - 'single' => 'single - non-existent label (42)', - ]); - $form_builder->submitForm($this, $form_state); - $this->assertEqual(count($form_state->getErrors()), 1); - $this->assertEqual($form_state->getErrors()['single'], t('The referenced entity (%type: %id) does not exist.', array('%type' => 'entity_test', '%id' => 42))); - - // Do the same tests as above but on an element with '#validate_reference' - // set to FALSE. - $form_state = (new FormState()) - ->setValues([ - 'single_no_validate' => 'single - non-existent label', - 'single_autocreate_no_validate' => 'single - autocreate non-existent label' - ]); - $form_builder->submitForm($this, $form_state); - - // The element without 'autocreate' support still has to emit a warning when - // the input doesn't end with an entity ID enclosed in parentheses. - $this->assertEqual(count($form_state->getErrors()), 1); - $this->assertEqual($form_state->getErrors()['single_no_validate'], t('There are no entities matching "%value".', array('%value' => 'single - non-existent label'))); - - $form_state = (new FormState()) - ->setValues([ - 'single_no_validate' => 'single - non-existent label (42)', - 'single_autocreate_no_validate' => 'single - autocreate non-existent label (43)' - ]); - $form_builder->submitForm($this, $form_state); - - // The input is complete (i.e. contains an entity ID at the end), no errors - // are triggered. - $this->assertEqual(count($form_state->getErrors()), 0); - } - - /** - * Tests that access is properly checked by the EntityAutocomplete element. - */ - public function testEntityAutocompleteAccess() { - $form_builder = $this->container->get('form_builder'); - $form = $form_builder->getForm($this); - - // Check that the current user has proper access to view entity labels. - $expected = $this->referencedEntities[0]->label() . ' (' . $this->referencedEntities[0]->id() . ')'; - $this->assertEqual($form['single_access']['#value'], $expected); - - $expected .= ', ' . $this->referencedEntities[1]->label() . ' (' . $this->referencedEntities[1]->id() . ')'; - $this->assertEqual($form['tags_access']['#value'], $expected); - - // Set up a non-admin user that is *not* allowed to view test entities. - \Drupal::currentUser()->setAccount($this->createUser(array(), array())); - - // Rebuild the form. - $form = $form_builder->getForm($this); - - $expected = t('- Restricted access -') . ' (' . $this->referencedEntities[0]->id() . ')'; - $this->assertEqual($form['single_access']['#value'], $expected); - - $expected .= ', ' . t('- Restricted access -') . ' (' . $this->referencedEntities[1]->id() . ')'; - $this->assertEqual($form['tags_access']['#value'], $expected); - } - - /** - * Returns an entity label in the format needed by the EntityAutocomplete - * element. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * A Drupal entity. - * - * @return string - * A string that can be used as a value for EntityAutocomplete elements. - */ - protected function getAutocompleteInput(EntityInterface $entity) { - return EntityAutocomplete::getEntityLabels(array($entity)); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityAccessControlHandlerTest.php b/core/modules/system/src/Tests/Entity/EntityAccessControlHandlerTest.php deleted file mode 100644 index 3833476..0000000 --- a/core/modules/system/src/Tests/Entity/EntityAccessControlHandlerTest.php +++ /dev/null @@ -1,170 +0,0 @@ -installSchema('system', 'url_alias'); - } - - /** - * Asserts entity access correctly grants or denies access. - */ - function assertEntityAccess($ops, AccessibleInterface $object, AccountInterface $account = NULL) { - foreach ($ops as $op => $result) { - $message = format_string("Entity access returns @result with operation '@op'.", array( - '@result' => !isset($result) ? 'null' : ($result ? 'true' : 'false'), - '@op' => $op, - )); - - $this->assertEqual($result, $object->access($op, $account), $message); - } - } - - /** - * Ensures entity access is properly working. - */ - function testEntityAccess() { - // Set up a non-admin user that is allowed to view test entities. - \Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2), array('view test entity'))); - $entity = entity_create('entity_test', array( - 'name' => 'test', - )); - - // The current user is allowed to view entities. - $this->assertEntityAccess(array( - 'create' => FALSE, - 'update' => FALSE, - 'delete' => FALSE, - 'view' => TRUE, - ), $entity); - - // The custom user is not allowed to perform any operation on test entities. - $custom_user = $this->createUser(); - $this->assertEntityAccess(array( - 'create' => FALSE, - 'update' => FALSE, - 'delete' => FALSE, - 'view' => FALSE, - ), $entity, $custom_user); - } - - /** - * Ensures default entity access is checked when necessary. - * - * This ensures that the default checkAccess() implementation of the - * entity access control handler is considered if hook_entity_access() has not - * explicitly forbidden access. Therefore the default checkAccess() - * implementation can forbid access, even after access was already explicitly - * allowed by hook_entity_access(). - * - * @see \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess() - * @see entity_test_entity_access() - */ - function testDefaultEntityAccess() { - // Set up a non-admin user that is allowed to view test entities. - \Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2), array('view test entity'))); - $entity = entity_create('entity_test', array( - 'name' => 'forbid_access', - )); - - // The user is denied access to the entity. - $this->assertEntityAccess(array( - 'create' => FALSE, - 'update' => FALSE, - 'delete' => FALSE, - 'view' => FALSE, - ), $entity); - } - - /** - * Ensures that the default handler is used as a fallback. - */ - function testEntityAccessDefaultController() { - // The implementation requires that the global user id can be loaded. - \Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2))); - - // Check that the default access control handler is used for entities that don't - // have a specific access control handler defined. - $handler = $this->container->get('entity.manager')->getAccessControlHandler('entity_test_default_access'); - $this->assertTrue($handler instanceof EntityAccessControlHandler, 'The default entity handler is used for the entity_test_default_access entity type.'); - - $entity = entity_create('entity_test_default_access'); - $this->assertEntityAccess(array( - 'create' => FALSE, - 'update' => FALSE, - 'delete' => FALSE, - 'view' => FALSE, - ), $entity); - } - - /** - * Ensures entity access for entity translations is properly working. - */ - function testEntityTranslationAccess() { - - // Set up a non-admin user that is allowed to view test entity translations. - \Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2), array('view test entity translations'))); - - // Create two test languages. - foreach (array('foo', 'bar') as $langcode) { - ConfigurableLanguage::create(array( - 'id' => $langcode, - 'label' => $this->randomString(), - ))->save(); - } - - $entity = entity_create('entity_test', array( - 'name' => 'test', - 'langcode' => 'foo', - )); - $entity->save(); - - $translation = $entity->addTranslation('bar'); - $this->assertEntityAccess(array( - 'view' => TRUE, - ), $translation); - } - - /** - * Tests hook invocations. - */ - public function testHooks() { - $state = $this->container->get('state'); - $entity = entity_create('entity_test', array( - 'name' => 'test', - )); - - // Test hook_entity_create_access() and hook_ENTITY_TYPE_create_access(). - $entity->access('create'); - $this->assertEqual($state->get('entity_test_entity_create_access'), TRUE); - $this->assertIdentical($state->get('entity_test_entity_create_access_context'), [ - 'entity_type_id' => 'entity_test', - 'langcode' => LanguageInterface::LANGCODE_DEFAULT, - ]); - $this->assertEqual($state->get('entity_test_entity_test_create_access'), TRUE); - - // Test hook_entity_access() and hook_ENTITY_TYPE_access(). - $entity->access('view'); - $this->assertEqual($state->get('entity_test_entity_access'), TRUE); - $this->assertEqual($state->get('entity_test_entity_test_access'), TRUE); - } -} diff --git a/core/modules/system/src/Tests/Entity/EntityAddUITest.php b/core/modules/system/src/Tests/Entity/EntityAddUITest.php new file mode 100644 index 0000000..0df247a --- /dev/null +++ b/core/modules/system/src/Tests/Entity/EntityAddUITest.php @@ -0,0 +1,106 @@ +drupalCreateUser([ + "administer entity_test_with_bundle content", + "administer entity_test content", + ]); + $this->drupalLogin($web_user); + } + + /** + * Tests the add page for an entity type using bundle entities. + */ + public function testAddPageWithBundleEntities() { + $this->drupalGet('/entity_test_with_bundle/add'); + // No bundles exist, the add bundle message should be present. + $this->assertText('There is no test entity bundle yet.'); + $this->assertLink('Add a new test entity bundle.'); + + // One bundle exists, confirm redirection to the add-form. + EntityTestBundle::create([ + 'id' => 'test', + 'label' => 'Test label', + 'description' => 'My test description', + ])->save(); + $this->drupalGet('/entity_test_with_bundle/add'); + $this->assertUrl('/entity_test_with_bundle/add/test'); + + // Two bundles exist, confirm both are shown. + EntityTestBundle::create([ + 'id' => 'test2', + 'label' => 'Test2 label', + 'description' => 'My test2 description', + ])->save(); + $this->drupalGet('/entity_test_with_bundle/add'); + + $this->assertLink('Test label'); + $this->assertLink('Test2 label'); + $this->assertText('My test description'); + $this->assertText('My test2 description'); + + $this->clickLink('Test2 label'); + $this->drupalGet('/entity_test_with_bundle/add/test2'); + + $this->drupalPostForm(NULL, ['name[0][value]' => 'test name'], t('Save')); + $entity = EntityTestWithBundle::load(1); + $this->assertEqual('test name', $entity->label()); + } + + /** + * Tests the add page for an entity type not using bundle entities. + */ + public function testAddPageWithoutBundleEntities() { + entity_test_create_bundle('test', 'Test label', 'entity_test_mul'); + // Delete the default bundle, so that we can rely on our own. + entity_test_delete_bundle('entity_test_mul', 'entity_test_mul'); + + // One bundle exists, confirm redirection to the add-form. + $this->drupalGet('/entity_test_mul/add'); + $this->assertUrl('/entity_test_mul/add/test'); + + // Two bundles exist, confirm both are shown. + entity_test_create_bundle('test2', 'Test2 label', 'entity_test_mul'); + $this->drupalGet('/entity_test_mul/add'); + + $this->assertLink('Test label'); + $this->assertLink('Test2 label'); + + $this->clickLink('Test2 label'); + $this->drupalGet('/entity_test_mul/add/test2'); + + $this->drupalPostForm(NULL, ['name[0][value]' => 'test name'], t('Save')); + $entity = EntityTestMul::load(1); + $this->assertEqual('test name', $entity->label()); + } + +} diff --git a/core/modules/system/src/Tests/Entity/EntityApiTest.php b/core/modules/system/src/Tests/Entity/EntityApiTest.php deleted file mode 100644 index e8153ce..0000000 --- a/core/modules/system/src/Tests/Entity/EntityApiTest.php +++ /dev/null @@ -1,171 +0,0 @@ -installEntitySchema($entity_type_id); - } - } - } - - /** - * Tests basic CRUD functionality of the Entity API. - */ - public function testCRUD() { - // All entity variations have to have the same results. - foreach (entity_test_entity_types() as $entity_type) { - $this->assertCRUD($entity_type, $this->createUser()); - } - } - - /** - * Executes a test set for a defined entity type and user. - * - * @param string $entity_type - * The entity type to run the tests with. - * @param \Drupal\user\UserInterface $user1 - * The user to run the tests with. - */ - protected function assertCRUD($entity_type, UserInterface $user1) { - // Create some test entities. - $entity = entity_create($entity_type, array('name' => 'test', 'user_id' => $user1->id())); - $entity->save(); - $entity = entity_create($entity_type, array('name' => 'test2', 'user_id' => $user1->id())); - $entity->save(); - $entity = entity_create($entity_type, array('name' => 'test', 'user_id' => NULL)); - $entity->save(); - - $entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test'))); - $this->assertEqual($entities[0]->name->value, 'test', format_string('%entity_type: Created and loaded entity', array('%entity_type' => $entity_type))); - $this->assertEqual($entities[1]->name->value, 'test', format_string('%entity_type: Created and loaded entity', array('%entity_type' => $entity_type))); - - // Test loading a single entity. - $loaded_entity = entity_load($entity_type, $entity->id()); - $this->assertEqual($loaded_entity->id(), $entity->id(), format_string('%entity_type: Loaded a single entity by id.', array('%entity_type' => $entity_type))); - - // Test deleting an entity. - $entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test2'))); - $entities[0]->delete(); - $entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test2'))); - $this->assertEqual($entities, array(), format_string('%entity_type: Entity deleted.', array('%entity_type' => $entity_type))); - - // Test updating an entity. - $entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test'))); - $entities[0]->name->value = 'test3'; - $entities[0]->save(); - $entity = entity_load($entity_type, $entities[0]->id()); - $this->assertEqual($entity->name->value, 'test3', format_string('%entity_type: Entity updated.', array('%entity_type' => $entity_type))); - - // Try deleting multiple test entities by deleting all. - $ids = array_keys(entity_load_multiple($entity_type)); - entity_delete_multiple($entity_type, $ids); - - $all = entity_load_multiple($entity_type); - $this->assertTrue(empty($all), format_string('%entity_type: Deleted all entities.', array('%entity_type' => $entity_type))); - - // Verify that all data got deleted. - $definition = \Drupal::entityManager()->getDefinition($entity_type); - $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition->getBaseTable() . '}')->fetchField(), 'Base table was emptied'); - if ($data_table = $definition->getDataTable()) { - $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $data_table . '}')->fetchField(), 'Data table was emptied'); - } - if ($revision_table = $definition->getRevisionTable()) { - $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $revision_table . '}')->fetchField(), 'Data table was emptied'); - } - - // Test deleting a list of entities not indexed by entity id. - $entities = array(); - $entity = entity_create($entity_type, array('name' => 'test', 'user_id' => $user1->id())); - $entity->save(); - $entities['test'] = $entity; - $entity = entity_create($entity_type, array('name' => 'test2', 'user_id' => $user1->id())); - $entity->save(); - $entities['test2'] = $entity; - $controller = \Drupal::entityManager()->getStorage($entity_type); - $controller->delete($entities); - - // Verify that entities got deleted. - $all = entity_load_multiple($entity_type); - $this->assertTrue(empty($all), format_string('%entity_type: Deleted all entities.', array('%entity_type' => $entity_type))); - - // Verify that all data got deleted from the tables. - $definition = \Drupal::entityManager()->getDefinition($entity_type); - $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition->getBaseTable() . '}')->fetchField(), 'Base table was emptied'); - if ($data_table = $definition->getDataTable()) { - $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $data_table . '}')->fetchField(), 'Data table was emptied'); - } - if ($revision_table = $definition->getRevisionTable()) { - $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $revision_table . '}')->fetchField(), 'Data table was emptied'); - } - } - - /** - * Tests that exceptions are thrown when saving or deleting an entity. - */ - public function testEntityStorageExceptionHandling() { - $entity = entity_create('entity_test', array('name' => 'test')); - try { - $GLOBALS['entity_test_throw_exception'] = TRUE; - $entity->save(); - $this->fail('Entity presave EntityStorageException thrown but not caught.'); - } - catch (EntityStorageException $e) { - $this->assertEqual($e->getcode(), 1, 'Entity presave EntityStorageException caught.'); - } - - $entity = entity_create('entity_test', array('name' => 'test2')); - try { - unset($GLOBALS['entity_test_throw_exception']); - $entity->save(); - $this->pass('Exception presave not thrown and not caught.'); - } - catch (EntityStorageException $e) { - $this->assertNotEqual($e->getCode(), 1, 'Entity presave EntityStorageException caught.'); - } - - $entity = entity_create('entity_test', array('name' => 'test3')); - $entity->save(); - try { - $GLOBALS['entity_test_throw_exception'] = TRUE; - $entity->delete(); - $this->fail('Entity predelete EntityStorageException not thrown.'); - } - catch (EntityStorageException $e) { - $this->assertEqual($e->getCode(), 2, 'Entity predelete EntityStorageException caught.'); - } - - unset($GLOBALS['entity_test_throw_exception']); - $entity = entity_create('entity_test', array('name' => 'test4')); - $entity->save(); - try { - $entity->delete(); - $this->pass('Entity predelete EntityStorageException not thrown and not caught.'); - } - catch (EntityStorageException $e) { - $this->assertNotEqual($e->getCode(), 2, 'Entity predelete EntityStorageException thrown.'); - } - } -} diff --git a/core/modules/system/src/Tests/Entity/EntityAutocompleteTest.php b/core/modules/system/src/Tests/Entity/EntityAutocompleteTest.php deleted file mode 100644 index d42a07a..0000000 --- a/core/modules/system/src/Tests/Entity/EntityAutocompleteTest.php +++ /dev/null @@ -1,164 +0,0 @@ -installSchema('system', ['key_value']); - } - - /** - * Tests autocompletion edge cases with slashes in the names. - */ - function testEntityReferenceAutocompletion() { - // Add an entity with a slash in its name. - $entity_1 = entity_create($this->entityType, array('name' => '10/16/2011')); - $entity_1->save(); - - // Add another entity that differs after the slash character. - $entity_2 = entity_create($this->entityType, array('name' => '10/17/2011')); - $entity_2->save(); - - // Add another entity that has both a comma, a slash and markup. - $entity_3 = entity_create($this->entityType, array('name' => 'label with, and / test')); - $entity_3->save(); - - // Try to autocomplete a entity label that matches both entities. - // We should get both entities in a JSON encoded string. - $input = '10/'; - $data = $this->getAutocompleteResult($input); - $this->assertIdentical($data[0]['label'], Html::escape($entity_1->name->value), 'Autocomplete returned the first matching entity'); - $this->assertIdentical($data[1]['label'], Html::escape($entity_2->name->value), 'Autocomplete returned the second matching entity'); - - // Try to autocomplete a entity label that matches the first entity. - // We should only get the first entity in a JSON encoded string. - $input = '10/16'; - $data = $this->getAutocompleteResult($input); - $target = array( - 'value' => $entity_1->name->value . ' (1)', - 'label' => Html::escape($entity_1->name->value), - ); - $this->assertIdentical(reset($data), $target, 'Autocomplete returns only the expected matching entity.'); - - // Try to autocomplete a entity label that matches the second entity, and - // the first entity is already typed in the autocomplete (tags) widget. - $input = $entity_1->name->value . ' (1), 10/17'; - $data = $this->getAutocompleteResult($input); - $this->assertIdentical($data[0]['label'], Html::escape($entity_2->name->value), 'Autocomplete returned the second matching entity'); - - // Try to autocomplete a entity label with both a comma, a slash and markup. - $input = '"label with, and / '; - $data = $this->getAutocompleteResult($input); - $n = $entity_3->name->value . ' (3)'; - // Entity labels containing commas or quotes must be wrapped in quotes. - $n = Tags::encode($n); - $target = array( - 'value' => $n, - 'label' => Html::escape($entity_3->name->value), - ); - $this->assertIdentical(reset($data), $target, 'Autocomplete returns an entity label containing a comma and a slash.'); - } - - /** - * Tests that missing or invalid selection setting key are handled correctly. - */ - public function testSelectionSettingsHandling() { - $entity_reference_controller = EntityAutocompleteController::create($this->container); - $request = Request::create('entity_reference_autocomplete/' . $this->entityType . '/default'); - $request->query->set('q', $this->randomString()); - - try { - // Pass an invalid selection settings key (i.e. one that does not exist - // in the key/value store). - $selection_settings_key = $this->randomString(); - $entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key); - - $this->fail('Non-existent selection settings key throws an exception.'); - } - catch (AccessDeniedHttpException $e) { - $this->pass('Non-existent selection settings key throws an exception.'); - } - - try { - // Generate a valid hash key but store a modified settings array. - $selection_settings = []; - $selection_settings_key = Crypt::hmacBase64(serialize($selection_settings) . $this->entityType . 'default', Settings::getHashSalt()); - - $selection_settings[$this->randomMachineName()] = $this->randomString(); - \Drupal::keyValue('entity_autocomplete')->set($selection_settings_key, $selection_settings); - - $entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key); - } - catch (AccessDeniedHttpException $e) { - if ($e->getMessage() == 'Invalid selection settings key.') { - $this->pass('Invalid selection settings key throws an exception.'); - } - else { - $this->fail('Invalid selection settings key throws an exception.'); - } - } - - } - - /** - * Returns the result of an Entity reference autocomplete request. - * - * @param string $input - * The label of the entity to query by. - * - * @return mixed - * The JSON value encoded in its appropriate PHP type. - */ - protected function getAutocompleteResult($input) { - $request = Request::create('entity_reference_autocomplete/' . $this->entityType . '/default'); - $request->query->set('q', $input); - - $selection_settings = []; - $selection_settings_key = Crypt::hmacBase64(serialize($selection_settings) . $this->entityType . 'default', Settings::getHashSalt()); - \Drupal::keyValue('entity_autocomplete')->set($selection_settings_key, $selection_settings); - - $entity_reference_controller = EntityAutocompleteController::create($this->container); - $result = $entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key)->getContent(); - - return Json::decode($result); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php b/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php deleted file mode 100644 index f1385ca..0000000 --- a/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php +++ /dev/null @@ -1,119 +0,0 @@ -installSchema('user', array('users_data')); - $this->installSchema('system', array('router')); - $this->moduleHandler = $this->container->get('module_handler'); - $this->database = $this->container->get('database'); - } - - /** - * Tests making use of a custom bundle field. - */ - public function testCustomBundleFieldUsage() { - entity_test_create_bundle('custom'); - - // Check that an entity with bundle entity_test does not have the custom - // field. - $storage = $this->entityManager->getStorage('entity_test'); - $entity = $storage->create([ - 'type' => 'entity_test', - ]); - $this->assertFalse($entity->hasField('custom_bundle_field')); - - // Check that the custom bundle has the defined custom field and check - // saving and deleting of custom field data. - $entity = $storage->create([ - 'type' => 'custom', - ]); - $this->assertTrue($entity->hasField('custom_bundle_field')); - - // Ensure that the field exists in the field map. - $field_map = \Drupal::entityManager()->getFieldMap(); - $this->assertEqual($field_map['entity_test']['custom_bundle_field'], ['type' => 'string', 'bundles' => ['custom' => 'custom']]); - - $entity->custom_bundle_field->value = 'swanky'; - $entity->save(); - $storage->resetCache(); - $entity = $storage->load($entity->id()); - $this->assertEqual($entity->custom_bundle_field->value, 'swanky', 'Entity was saved correctly'); - - $entity->custom_bundle_field->value = 'cozy'; - $entity->save(); - $storage->resetCache(); - $entity = $storage->load($entity->id()); - $this->assertEqual($entity->custom_bundle_field->value, 'cozy', 'Entity was updated correctly.'); - - $entity->delete(); - /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ - $table_mapping = $storage->getTableMapping(); - $table = $table_mapping->getDedicatedDataTableName($entity->getFieldDefinition('custom_bundle_field')); - $result = $this->database->select($table, 'f') - ->fields('f') - ->condition('f.entity_id', $entity->id()) - ->execute(); - $this->assertFalse($result->fetchAssoc(), 'Field data has been deleted'); - - // Create another entity to test that values are marked as deleted when a - // bundle is deleted. - $entity = $storage->create(['type' => 'custom', 'custom_bundle_field' => 'new']); - $entity->save(); - entity_test_delete_bundle('custom'); - - $table = $table_mapping->getDedicatedDataTableName($entity->getFieldDefinition('custom_bundle_field')); - $result = $this->database->select($table, 'f') - ->condition('f.entity_id', $entity->id()) - ->condition('deleted', 1) - ->countQuery() - ->execute(); - $this->assertEqual(1, $result->fetchField(), 'Field data has been deleted'); - - // Ensure that the field no longer exists in the field map. - $field_map = \Drupal::entityManager()->getFieldMap(); - $this->assertFalse(isset($field_map['entity_test']['custom_bundle_field'])); - - // @todo Test field purge and table deletion once supported. See - // https://www.drupal.org/node/2282119. - // $this->assertFalse($this->database->schema()->tableExists($table), 'Custom field table was deleted'); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php index dd35bbd..2c712b1 100644 --- a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php +++ b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php @@ -73,19 +73,19 @@ protected function setUp() { if ($this->entity->getEntityType()->get('field_ui_base_route')) { // Add field, so we can modify the field storage and field entities to // verify that changes to those indeed clear cache tags. - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'field_name' => 'configurable_field', 'entity_type' => $this->entity->getEntityTypeId(), 'type' => 'test_field', 'settings' => array(), ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'entity_type' => $this->entity->getEntityTypeId(), 'bundle' => $this->entity->bundle(), 'field_name' => 'configurable_field', 'label' => 'Configurable field', 'settings' => array(), - ))->save(); + ])->save(); // Reload the entity now that a new field has been added to it. $storage = $this->container @@ -246,7 +246,7 @@ protected function createReferenceTestEntities($referenced_entity) { // Add a field of the given type to the given entity type's "foo" bundle. $field_name = $referenced_entity->getEntityTypeId() . '_reference'; - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'field_name' => $field_name, 'entity_type' => $entity_type, 'type' => 'entity_reference', @@ -255,7 +255,7 @@ protected function createReferenceTestEntities($referenced_entity) { 'target_type' => $referenced_entity->getEntityTypeId(), ), ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_name' => $field_name, 'entity_type' => $entity_type, 'bundle' => $bundle, @@ -269,7 +269,7 @@ protected function createReferenceTestEntities($referenced_entity) { 'auto_create' => FALSE, ), ), - ))->save(); + ])->save(); if (!$this->entity->getEntityType()->hasHandlerClass('view_builder')) { entity_get_display($entity_type, $bundle, 'full') ->setComponent($field_name, array( @@ -291,20 +291,24 @@ protected function createReferenceTestEntities($referenced_entity) { // Create an entity that does reference the entity being tested. $label_key = \Drupal::entityManager()->getDefinition($entity_type)->getKey('label'); - $referencing_entity = entity_create($entity_type, array( - $label_key => 'Referencing ' . $entity_type, - 'status' => 1, - 'type' => $bundle, - $field_name => array('target_id' => $referenced_entity->id()), - )); + $referencing_entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + $label_key => 'Referencing ' . $entity_type, + 'status' => 1, + 'type' => $bundle, + $field_name => array('target_id' => $referenced_entity->id()), + )); $referencing_entity->save(); // Create an entity that does not reference the entity being tested. - $non_referencing_entity = entity_create($entity_type, array( - $label_key => 'Non-referencing ' . $entity_type, - 'status' => 1, - 'type' => $bundle, - )); + $non_referencing_entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + $label_key => 'Non-referencing ' . $entity_type, + 'status' => 1, + 'type' => $bundle, + )); $non_referencing_entity->save(); return array( diff --git a/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php b/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php deleted file mode 100644 index 2885d47..0000000 --- a/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php +++ /dev/null @@ -1,562 +0,0 @@ -installSchema('user', array('users_data')); - $this->installSchema('file', array('file_usage')); - $this->installSchema('node', array('node_access')); - $this->installSchema('comment', array('comment_entity_statistics')); - $this->installConfig(['node', 'comment']); - } - - /** - * Checks the order of CRUD hook execution messages. - * - * entity_crud_hook_test.module implements all core entity CRUD hooks and - * stores a message for each in $GLOBALS['entity_crud_hook_test']. - * - * @param $messages - * An array of plain-text messages in the order they should appear. - */ - protected function assertHookMessageOrder($messages) { - $positions = array(); - foreach ($messages as $message) { - // Verify that each message is found and record its position. - $position = array_search($message, $GLOBALS['entity_crud_hook_test']); - if ($this->assertTrue($position !== FALSE, $message)) { - $positions[] = $position; - } - } - - // Sort the positions and ensure they remain in the same order. - $sorted = $positions; - sort($sorted); - $this->assertTrue($sorted == $positions, 'The hook messages appear in the correct order.'); - } - - /** - * Tests hook invocations for CRUD operations on blocks. - */ - public function testBlockHooks() { - $entity = entity_create('block', array( - 'id' => 'stark_test_html', - 'plugin' => 'test_html', - 'theme' => 'stark', - )); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_block_create called', - 'entity_crud_hook_test_entity_create called for type block', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $entity->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_block_presave called', - 'entity_crud_hook_test_entity_presave called for type block', - 'entity_crud_hook_test_block_insert called', - 'entity_crud_hook_test_entity_insert called for type block', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $entity = Block::load($entity->id()); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_entity_load called for type block', - 'entity_crud_hook_test_block_load called', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $entity->label = 'New label'; - $entity->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_block_presave called', - 'entity_crud_hook_test_entity_presave called for type block', - 'entity_crud_hook_test_block_update called', - 'entity_crud_hook_test_entity_update called for type block', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $entity->delete(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_block_predelete called', - 'entity_crud_hook_test_entity_predelete called for type block', - 'entity_crud_hook_test_block_delete called', - 'entity_crud_hook_test_entity_delete called for type block', - )); - } - - /** - * Tests hook invocations for CRUD operations on comments. - */ - public function testCommentHooks() { - $account = $this->createUser(); - entity_create('node_type', array( - 'type' => 'article', - 'name' => 'Article', - ))->save(); - $this->addDefaultCommentField('node', 'article', 'comment', CommentItemInterface::OPEN); - - $node = entity_create('node', array( - 'uid' => $account->id(), - 'type' => 'article', - 'title' => 'Test node', - 'status' => 1, - 'promote' => 0, - 'sticky' => 0, - 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - 'created' => REQUEST_TIME, - 'changed' => REQUEST_TIME, - )); - $node->save(); - $nid = $node->id(); - $GLOBALS['entity_crud_hook_test'] = array(); - - $comment = entity_create('comment', array( - 'cid' => NULL, - 'pid' => 0, - 'entity_id' => $nid, - 'entity_type' => 'node', - 'field_name' => 'comment', - 'uid' => $account->id(), - 'subject' => 'Test comment', - 'created' => REQUEST_TIME, - 'changed' => REQUEST_TIME, - 'status' => 1, - 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - )); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_comment_create called', - 'entity_crud_hook_test_entity_create called for type comment', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $comment->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_comment_presave called', - 'entity_crud_hook_test_entity_presave called for type comment', - 'entity_crud_hook_test_comment_insert called', - 'entity_crud_hook_test_entity_insert called for type comment', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $comment = Comment::load($comment->id()); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_entity_load called for type comment', - 'entity_crud_hook_test_comment_load called', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $comment->setSubject('New subject'); - $comment->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_comment_presave called', - 'entity_crud_hook_test_entity_presave called for type comment', - 'entity_crud_hook_test_comment_update called', - 'entity_crud_hook_test_entity_update called for type comment', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $comment->delete(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_comment_predelete called', - 'entity_crud_hook_test_entity_predelete called for type comment', - 'entity_crud_hook_test_comment_delete called', - 'entity_crud_hook_test_entity_delete called for type comment', - )); - } - - /** - * Tests hook invocations for CRUD operations on files. - */ - public function testFileHooks() { - $this->installEntitySchema('file'); - - $url = 'public://entity_crud_hook_test.file'; - file_put_contents($url, 'Test test test'); - $file = entity_create('file', array( - 'fid' => NULL, - 'uid' => 1, - 'filename' => 'entity_crud_hook_test.file', - 'uri' => $url, - 'filemime' => 'text/plain', - 'filesize' => filesize($url), - 'status' => 1, - 'created' => REQUEST_TIME, - 'changed' => REQUEST_TIME, - )); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_file_create called', - 'entity_crud_hook_test_entity_create called for type file', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $file->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_file_presave called', - 'entity_crud_hook_test_entity_presave called for type file', - 'entity_crud_hook_test_file_insert called', - 'entity_crud_hook_test_entity_insert called for type file', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $file = File::load($file->id()); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_entity_load called for type file', - 'entity_crud_hook_test_file_load called', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $file->setFilename('new.entity_crud_hook_test.file'); - $file->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_file_presave called', - 'entity_crud_hook_test_entity_presave called for type file', - 'entity_crud_hook_test_file_update called', - 'entity_crud_hook_test_entity_update called for type file', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $file->delete(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_file_predelete called', - 'entity_crud_hook_test_entity_predelete called for type file', - 'entity_crud_hook_test_file_delete called', - 'entity_crud_hook_test_entity_delete called for type file', - )); - } - - /** - * Tests hook invocations for CRUD operations on nodes. - */ - public function testNodeHooks() { - $account = $this->createUser(); - - $node = entity_create('node', array( - 'uid' => $account->id(), - 'type' => 'article', - 'title' => 'Test node', - 'status' => 1, - 'promote' => 0, - 'sticky' => 0, - 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - 'created' => REQUEST_TIME, - 'changed' => REQUEST_TIME, - )); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_node_create called', - 'entity_crud_hook_test_entity_create called for type node', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $node->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_node_presave called', - 'entity_crud_hook_test_entity_presave called for type node', - 'entity_crud_hook_test_node_insert called', - 'entity_crud_hook_test_entity_insert called for type node', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $node = Node::load($node->id()); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_entity_load called for type node', - 'entity_crud_hook_test_node_load called', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $node->title = 'New title'; - $node->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_node_presave called', - 'entity_crud_hook_test_entity_presave called for type node', - 'entity_crud_hook_test_node_update called', - 'entity_crud_hook_test_entity_update called for type node', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $node->delete(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_node_predelete called', - 'entity_crud_hook_test_entity_predelete called for type node', - 'entity_crud_hook_test_node_delete called', - 'entity_crud_hook_test_entity_delete called for type node', - )); - } - - /** - * Tests hook invocations for CRUD operations on taxonomy terms. - */ - public function testTaxonomyTermHooks() { - $this->installEntitySchema('taxonomy_term'); - - $vocabulary = entity_create('taxonomy_vocabulary', array( - 'name' => 'Test vocabulary', - 'vid' => 'test', - 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - 'description' => NULL, - 'module' => 'entity_crud_hook_test', - )); - $vocabulary->save(); - $GLOBALS['entity_crud_hook_test'] = array(); - - $term = entity_create('taxonomy_term', array( - 'vid' => $vocabulary->id(), - 'name' => 'Test term', - 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - 'description' => NULL, - 'format' => 1, - )); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_taxonomy_term_create called', - 'entity_crud_hook_test_entity_create called for type taxonomy_term', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $term->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_taxonomy_term_presave called', - 'entity_crud_hook_test_entity_presave called for type taxonomy_term', - 'entity_crud_hook_test_taxonomy_term_insert called', - 'entity_crud_hook_test_entity_insert called for type taxonomy_term', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $term = Term::load($term->id()); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_entity_load called for type taxonomy_term', - 'entity_crud_hook_test_taxonomy_term_load called', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $term->setName('New name'); - $term->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_taxonomy_term_presave called', - 'entity_crud_hook_test_entity_presave called for type taxonomy_term', - 'entity_crud_hook_test_taxonomy_term_update called', - 'entity_crud_hook_test_entity_update called for type taxonomy_term', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $term->delete(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_taxonomy_term_predelete called', - 'entity_crud_hook_test_entity_predelete called for type taxonomy_term', - 'entity_crud_hook_test_taxonomy_term_delete called', - 'entity_crud_hook_test_entity_delete called for type taxonomy_term', - )); - } - - /** - * Tests hook invocations for CRUD operations on taxonomy vocabularies. - */ - public function testTaxonomyVocabularyHooks() { - $this->installEntitySchema('taxonomy_term'); - - $vocabulary = entity_create('taxonomy_vocabulary', array( - 'name' => 'Test vocabulary', - 'vid' => 'test', - 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - 'description' => NULL, - 'module' => 'entity_crud_hook_test', - )); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_taxonomy_vocabulary_create called', - 'entity_crud_hook_test_entity_create called for type taxonomy_vocabulary', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $vocabulary->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_taxonomy_vocabulary_presave called', - 'entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary', - 'entity_crud_hook_test_taxonomy_vocabulary_insert called', - 'entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $vocabulary = Vocabulary::load($vocabulary->id()); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_entity_load called for type taxonomy_vocabulary', - 'entity_crud_hook_test_taxonomy_vocabulary_load called', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $vocabulary->set('name', 'New name'); - $vocabulary->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_taxonomy_vocabulary_presave called', - 'entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary', - 'entity_crud_hook_test_taxonomy_vocabulary_update called', - 'entity_crud_hook_test_entity_update called for type taxonomy_vocabulary', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $vocabulary->delete(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_taxonomy_vocabulary_predelete called', - 'entity_crud_hook_test_entity_predelete called for type taxonomy_vocabulary', - 'entity_crud_hook_test_taxonomy_vocabulary_delete called', - 'entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary', - )); - } - - /** - * Tests hook invocations for CRUD operations on users. - */ - public function testUserHooks() { - $account = entity_create('user', array( - 'name' => 'Test user', - 'mail' => 'test@example.com', - 'created' => REQUEST_TIME, - 'status' => 1, - 'language' => 'en', - )); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_user_create called', - 'entity_crud_hook_test_entity_create called for type user', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $account->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_user_presave called', - 'entity_crud_hook_test_entity_presave called for type user', - 'entity_crud_hook_test_user_insert called', - 'entity_crud_hook_test_entity_insert called for type user', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - User::load($account->id()); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_entity_load called for type user', - 'entity_crud_hook_test_user_load called', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - $account->name = 'New name'; - $account->save(); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_user_presave called', - 'entity_crud_hook_test_entity_presave called for type user', - 'entity_crud_hook_test_user_update called', - 'entity_crud_hook_test_entity_update called for type user', - )); - - $GLOBALS['entity_crud_hook_test'] = array(); - user_delete($account->id()); - - $this->assertHookMessageOrder(array( - 'entity_crud_hook_test_user_predelete called', - 'entity_crud_hook_test_entity_predelete called for type user', - 'entity_crud_hook_test_user_delete called', - 'entity_crud_hook_test_entity_delete called for type user', - )); - } - - /** - * Tests rollback from failed entity save. - */ - function testEntityRollback() { - // Create a block. - try { - entity_create('entity_test', array('name' => 'fail_insert'))->save(); - $this->fail('Expected exception has not been thrown.'); - } - catch (\Exception $e) { - $this->pass('Expected exception has been thrown.'); - } - - if (Database::getConnection()->supportsTransactions()) { - // Check that the block does not exist in the database. - $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute(); - $this->assertTrue(empty($ids), 'Transactions supported, and entity not found in database.'); - } - else { - // Check that the block exists in the database. - $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute(); - $this->assertFalse(empty($ids), 'Transactions not supported, and entity found in database.'); - } - } -} diff --git a/core/modules/system/src/Tests/Entity/EntityDefinitionUpdateTest.php b/core/modules/system/src/Tests/Entity/EntityDefinitionUpdateTest.php deleted file mode 100644 index a9b86b9..0000000 --- a/core/modules/system/src/Tests/Entity/EntityDefinitionUpdateTest.php +++ /dev/null @@ -1,813 +0,0 @@ -entityDefinitionUpdateManager = $this->container->get('entity.definition_update_manager'); - $this->database = $this->container->get('database'); - - // Install every entity type's schema that wasn't installed in the parent - // method. - foreach (array_diff_key($this->entityManager->getDefinitions(), array_flip(array('user', 'entity_test'))) as $entity_type_id => $entity_type) { - $this->installEntitySchema($entity_type_id); - } - } - - /** - * Tests that new entity type definitions are correctly handled. - */ - public function testNewEntityType() { - $entity_type_id = 'entity_test_new'; - $schema = $this->database->schema(); - - // Check that the "entity_test_new" is not defined. - $entity_types = $this->entityManager->getDefinitions(); - $this->assertFalse(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type does not exist.'); - $this->assertFalse($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type does not exist.'); - - // Check that the "entity_test_new" is now defined and the related schema - // has been created. - $this->enableNewEntityType(); - $entity_types = $this->entityManager->getDefinitions(); - $this->assertTrue(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type exists.'); - $this->assertTrue($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type has been created.'); - } - - /** - * Tests when no definition update is needed. - */ - public function testNoUpdates() { - // Ensure that the definition update manager reports no updates. - $this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that no updates are needed.'); - $this->assertIdentical($this->entityDefinitionUpdateManager->getChangeSummary(), array(), 'EntityDefinitionUpdateManager reports an empty change summary.'); - - // Ensure that applyUpdates() runs without error (it's not expected to do - // anything when there aren't updates). - $this->entityDefinitionUpdateManager->applyUpdates(); - } - - /** - * Tests updating entity schema when there are no existing entities. - */ - public function testEntityTypeUpdateWithoutData() { - // The 'entity_test_update' entity type starts out non-revisionable, so - // ensure the revision table hasn't been created during setUp(). - $this->assertFalse($this->database->schema()->tableExists('entity_test_update_revision'), 'Revision table not created for entity_test_update.'); - - // Update it to be revisionable and ensure the definition update manager - // reports that an update is needed. - $this->updateEntityTypeToRevisionable(); - $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); - $expected = array( - 'entity_test_update' => array( - t('Update the %entity_type entity type.', array('%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel())), - ), - ); - $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected); //, 'EntityDefinitionUpdateManager reports the expected change summary.'); - - // Run the update and ensure the revision table is created. - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertTrue($this->database->schema()->tableExists('entity_test_update_revision'), 'Revision table created for entity_test_update.'); - } - - /** - * Tests updating entity schema when there are existing entities. - */ - public function testEntityTypeUpdateWithData() { - // Save an entity. - $this->entityManager->getStorage('entity_test_update')->create()->save(); - - // Update the entity type to be revisionable and try to apply the update. - // It's expected to throw an exception. - $this->updateEntityTypeToRevisionable(); - try { - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->fail('EntityStorageException thrown when trying to apply an update that requires data migration.'); - } - catch (EntityStorageException $e) { - $this->pass('EntityStorageException thrown when trying to apply an update that requires data migration.'); - } - } - - /** - * Tests creating, updating, and deleting a base field if no entities exist. - */ - public function testBaseFieldCreateUpdateDeleteWithoutData() { - // Add a base field, ensure the update manager reports it, and the update - // creates its schema. - $this->addBaseField(); - $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); - $expected = array( - 'entity_test_update' => array( - t('Create the %field_name field.', array('%field_name' => t('A new base field'))), - ), - ); - $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), 'Column created in shared table for new_base_field.'); - - // Add an index on the base field, ensure the update manager reports it, - // and the update creates it. - $this->addBaseFieldIndex(); - $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); - $expected = array( - 'entity_test_update' => array( - t('Update the %field_name field.', array('%field_name' => t('A new base field'))), - ), - ); - $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), 'Index created.'); - - // Remove the above index, ensure the update manager reports it, and the - // update deletes it. - $this->removeBaseFieldIndex(); - $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); - $expected = array( - 'entity_test_update' => array( - t('Update the %field_name field.', array('%field_name' => t('A new base field'))), - ), - ); - $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), 'Index deleted.'); - - // Update the type of the base field from 'string' to 'text', ensure the - // update manager reports it, and the update adjusts the schema - // accordingly. - $this->modifyBaseField(); - $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); - $expected = array( - 'entity_test_update' => array( - t('Update the %field_name field.', array('%field_name' => t('A new base field'))), - ), - ); - $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), 'Original column deleted in shared table for new_base_field.'); - $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field__value'), 'Value column created in shared table for new_base_field.'); - $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field__format'), 'Format column created in shared table for new_base_field.'); - - // Remove the base field, ensure the update manager reports it, and the - // update deletes the schema. - $this->removeBaseField(); - $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); - $expected = array( - 'entity_test_update' => array( - t('Delete the %field_name field.', array('%field_name' => t('A new base field'))), - ), - ); - $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field_value'), 'Value column deleted from shared table for new_base_field.'); - $this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field_format'), 'Format column deleted from shared table for new_base_field.'); - } - - /** - * Tests creating, updating, and deleting a bundle field if no entities exist. - */ - public function testBundleFieldCreateUpdateDeleteWithoutData() { - // Add a bundle field, ensure the update manager reports it, and the update - // creates its schema. - $this->addBundleField(); - $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); - $expected = array( - 'entity_test_update' => array( - t('Create the %field_name field.', array('%field_name' => t('A new bundle field'))), - ), - ); - $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertTrue($this->database->schema()->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table created for new_bundle_field.'); - - // Update the type of the base field from 'string' to 'text', ensure the - // update manager reports it, and the update adjusts the schema - // accordingly. - $this->modifyBundleField(); - $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); - $expected = array( - 'entity_test_update' => array( - t('Update the %field_name field.', array('%field_name' => t('A new bundle field'))), - ), - ); - $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertTrue($this->database->schema()->fieldExists('entity_test_update__new_bundle_field', 'new_bundle_field_format'), 'Format column created in dedicated table for new_base_field.'); - - // Remove the bundle field, ensure the update manager reports it, and the - // update deletes the schema. - $this->removeBundleField(); - $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); - $expected = array( - 'entity_test_update' => array( - t('Delete the %field_name field.', array('%field_name' => t('A new bundle field'))), - ), - ); - $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertFalse($this->database->schema()->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table deleted for new_bundle_field.'); - } - - /** - * Tests creating and deleting a base field if entities exist. - * - * This tests deletion when there are existing entities, but not existing data - * for the field being deleted. - * - * @see testBaseFieldDeleteWithExistingData() - */ - public function testBaseFieldCreateDeleteWithExistingEntities() { - // Save an entity. - $name = $this->randomString(); - $storage = $this->entityManager->getStorage('entity_test_update'); - $entity = $storage->create(array('name' => $name)); - $entity->save(); - - // Add a base field and run the update. Ensure the base field's column is - // created and the prior saved entity data is still there. - $this->addBaseField(); - $this->entityDefinitionUpdateManager->applyUpdates(); - $schema_handler = $this->database->schema(); - $this->assertTrue($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column created in shared table for new_base_field.'); - $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id()); - $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field creation.'); - - // Remove the base field and run the update. Ensure the base field's column - // is deleted and the prior saved entity data is still there. - $this->removeBaseField(); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertFalse($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column deleted from shared table for new_base_field.'); - $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id()); - $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field deletion.'); - - // Add a base field with a required property and run the update. Ensure - // 'not null' is not applied and thus no exception is thrown. - $this->addBaseField('shape_required'); - $this->entityDefinitionUpdateManager->applyUpdates(); - $assert = $schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && $schema_handler->fieldExists('entity_test_update', 'new_base_field__color'); - $this->assertTrue($assert, 'Columns created in shared table for new_base_field.'); - - // Recreate the field after emptying the base table and check that its - // columns are not 'not null'. - // @todo Revisit this test when allowing for required storage field - // definitions. See https://www.drupal.org/node/2390495. - $entity->delete(); - $this->removeBaseField(); - $this->entityDefinitionUpdateManager->applyUpdates(); - $assert = !$schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && !$schema_handler->fieldExists('entity_test_update', 'new_base_field__color'); - $this->assert($assert, 'Columns removed from the shared table for new_base_field.'); - $this->addBaseField('shape_required'); - $this->entityDefinitionUpdateManager->applyUpdates(); - $assert = $schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && $schema_handler->fieldExists('entity_test_update', 'new_base_field__color'); - $this->assertTrue($assert, 'Columns created again in shared table for new_base_field.'); - $entity = $storage->create(array('name' => $name)); - $entity->save(); - $this->pass('The new_base_field columns are still nullable'); - } - - /** - * Tests creating and deleting a bundle field if entities exist. - * - * This tests deletion when there are existing entities, but not existing data - * for the field being deleted. - * - * @see testBundleFieldDeleteWithExistingData() - */ - public function testBundleFieldCreateDeleteWithExistingEntities() { - // Save an entity. - $name = $this->randomString(); - $storage = $this->entityManager->getStorage('entity_test_update'); - $entity = $storage->create(array('name' => $name)); - $entity->save(); - - // Add a bundle field and run the update. Ensure the bundle field's table - // is created and the prior saved entity data is still there. - $this->addBundleField(); - $this->entityDefinitionUpdateManager->applyUpdates(); - $schema_handler = $this->database->schema(); - $this->assertTrue($schema_handler->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table created for new_bundle_field.'); - $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id()); - $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field creation.'); - - // Remove the base field and run the update. Ensure the bundle field's - // table is deleted and the prior saved entity data is still there. - $this->removeBundleField(); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertFalse($schema_handler->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table deleted for new_bundle_field.'); - $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id()); - $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field deletion.'); - - // Test that required columns are created as 'not null'. - $this->addBundleField('shape_required'); - $this->entityDefinitionUpdateManager->applyUpdates(); - $message = 'The new_bundle_field_shape column is not nullable.'; - $values = array( - 'bundle' => $entity->bundle(), - 'deleted'=> 0, - 'entity_id' => $entity->id(), - 'revision_id' => $entity->id(), - 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - 'delta' => 0, - 'new_bundle_field_color' => $this->randomString(), - ); - try { - // Try to insert a record without providing a value for the 'not null' - // column. This should fail. - $this->database->insert('entity_test_update__new_bundle_field') - ->fields($values) - ->execute(); - $this->fail($message); - } - catch (\RuntimeException $e) { - if ($e instanceof DatabaseExceptionWrapper || $e instanceof IntegrityConstraintViolationException) { - // Now provide a value for the 'not null' column. This is expected to - // succeed. - $values['new_bundle_field_shape'] = $this->randomString(); - $this->database->insert('entity_test_update__new_bundle_field') - ->fields($values) - ->execute(); - $this->pass($message); - } else { - // Keep throwing it. - throw $e; - } - } - } - - /** - * Tests deleting a base field when it has existing data. - */ - public function testBaseFieldDeleteWithExistingData() { - // Add the base field and run the update. - $this->addBaseField(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - // Save an entity with the base field populated. - $this->entityManager->getStorage('entity_test_update')->create(array('new_base_field' => 'foo'))->save(); - - // Remove the base field and apply updates. It's expected to throw an - // exception. - // @todo Revisit that expectation once purging is implemented for - // all fields: https://www.drupal.org/node/2282119. - $this->removeBaseField(); - try { - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.'); - } - catch (FieldStorageDefinitionUpdateForbiddenException $e) { - $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.'); - } - } - - /** - * Tests deleting a bundle field when it has existing data. - */ - public function testBundleFieldDeleteWithExistingData() { - // Add the bundle field and run the update. - $this->addBundleField(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - // Save an entity with the bundle field populated. - entity_test_create_bundle('custom'); - $this->entityManager->getStorage('entity_test_update')->create(array('type' => 'test_bundle', 'new_bundle_field' => 'foo'))->save(); - - // Remove the bundle field and apply updates. It's expected to throw an - // exception. - // @todo Revisit that expectation once purging is implemented for - // all fields: https://www.drupal.org/node/2282119. - $this->removeBundleField(); - try { - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.'); - } - catch (FieldStorageDefinitionUpdateForbiddenException $e) { - $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.'); - } - } - - /** - * Tests updating a base field when it has existing data. - */ - public function testBaseFieldUpdateWithExistingData() { - // Add the base field and run the update. - $this->addBaseField(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - // Save an entity with the base field populated. - $this->entityManager->getStorage('entity_test_update')->create(array('new_base_field' => 'foo'))->save(); - - // Change the field's field type and apply updates. It's expected to - // throw an exception. - $this->modifyBaseField(); - try { - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.'); - } - catch (FieldStorageDefinitionUpdateForbiddenException $e) { - $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.'); - } - } - - /** - * Tests updating a bundle field when it has existing data. - */ - public function testBundleFieldUpdateWithExistingData() { - // Add the bundle field and run the update. - $this->addBundleField(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - // Save an entity with the bundle field populated. - entity_test_create_bundle('custom'); - $this->entityManager->getStorage('entity_test_update')->create(array('type' => 'test_bundle', 'new_bundle_field' => 'foo'))->save(); - - // Change the field's field type and apply updates. It's expected to - // throw an exception. - $this->modifyBundleField(); - try { - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.'); - } - catch (FieldStorageDefinitionUpdateForbiddenException $e) { - $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.'); - } - } - - /** - * Tests creating and deleting a multi-field index when there are no existing entities. - */ - public function testEntityIndexCreateDeleteWithoutData() { - // Add an entity index and ensure the update manager reports that as an - // update to the entity type. - $this->addEntityIndex(); - $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); - $expected = array( - 'entity_test_update' => array( - t('Update the %entity_type entity type.', array('%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel())), - ), - ); - $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); - - // Run the update and ensure the new index is created. - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created.'); - - // Remove the index and ensure the update manager reports that as an - // update to the entity type. - $this->removeEntityIndex(); - $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); - $expected = array( - 'entity_test_update' => array( - t('Update the %entity_type entity type.', array('%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel())), - ), - ); - $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); - - // Run the update and ensure the index is deleted. - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.'); - - // Test that composite indexes are handled correctly when dropping and - // re-creating one of their columns. - $this->addEntityIndex(); - $this->entityDefinitionUpdateManager->applyUpdates(); - $storage_definition = $this->entityDefinitionUpdateManager->getFieldStorageDefinition('name', 'entity_test_update'); - $this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition); - $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created.'); - $this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition); - $this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.'); - $this->entityDefinitionUpdateManager->installFieldStorageDefinition('name', 'entity_test_update', 'entity_test', $storage_definition); - $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created again.'); - } - - /** - * Tests creating a multi-field index when there are existing entities. - */ - public function testEntityIndexCreateWithData() { - // Save an entity. - $name = $this->randomString(); - $entity = $this->entityManager->getStorage('entity_test_update')->create(array('name' => $name)); - $entity->save(); - - // Add an entity index, run the update. Ensure that the index is created - // despite having data. - $this->addEntityIndex(); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index added.'); - } - - /** - * Tests entity type and field storage definition events. - */ - public function testDefinitionEvents() { - /** @var \Drupal\entity_test\EntityTestDefinitionSubscriber $event_subscriber */ - $event_subscriber = $this->container->get('entity_test.definition.subscriber'); - $event_subscriber->enableEventTracking(); - - // Test field storage definition events. - $storage_definition = current($this->entityManager->getFieldStorageDefinitions('entity_test_rev')); - $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::DELETE), 'Entity type delete was not dispatched yet.'); - $this->entityManager->onFieldStorageDefinitionDelete($storage_definition); - $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::DELETE), 'Entity type delete event successfully dispatched.'); - $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::CREATE), 'Entity type create was not dispatched yet.'); - $this->entityManager->onFieldStorageDefinitionCreate($storage_definition); - $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::CREATE), 'Entity type create event successfully dispatched.'); - $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::UPDATE), 'Entity type update was not dispatched yet.'); - $this->entityManager->onFieldStorageDefinitionUpdate($storage_definition, $storage_definition); - $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::UPDATE), 'Entity type update event successfully dispatched.'); - - // Test entity type events. - $entity_type = $this->entityManager->getDefinition('entity_test_rev'); - $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::CREATE), 'Entity type create was not dispatched yet.'); - $this->entityManager->onEntityTypeCreate($entity_type); - $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::CREATE), 'Entity type create event successfully dispatched.'); - $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::UPDATE), 'Entity type update was not dispatched yet.'); - $this->entityManager->onEntityTypeUpdate($entity_type, $entity_type); - $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::UPDATE), 'Entity type update event successfully dispatched.'); - $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::DELETE), 'Entity type delete was not dispatched yet.'); - $this->entityManager->onEntityTypeDelete($entity_type); - $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::DELETE), 'Entity type delete event successfully dispatched.'); - } - - /** - * Tests updating entity schema and creating a base field. - * - * This tests updating entity schema and creating a base field at the same - * time when there are no existing entities. - */ - public function testEntityTypeSchemaUpdateAndBaseFieldCreateWithoutData() { - $this->updateEntityTypeToRevisionable(); - $this->addBaseField(); - $message = 'Successfully updated entity schema and created base field at the same time.'; - // Entity type updates create base fields as well, thus make sure doing both - // at the same time does not lead to errors due to the base field being - // created twice. - try { - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->pass($message); - } - catch (\Exception $e) { - $this->fail($message); - throw $e; - } - } - - /** - * Tests updating entity schema and creating a revisionable base field. - * - * This tests updating entity schema and creating a revisionable base field - * at the same time when there are no existing entities. - */ - public function testEntityTypeSchemaUpdateAndRevisionableBaseFieldCreateWithoutData() { - $this->updateEntityTypeToRevisionable(); - $this->addRevisionableBaseField(); - $message = 'Successfully updated entity schema and created revisionable base field at the same time.'; - // Entity type updates create base fields as well, thus make sure doing both - // at the same time does not lead to errors due to the base field being - // created twice. - try { - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->pass($message); - } - catch (\Exception $e) { - $this->fail($message); - throw $e; - } - } - - /** - * Tests applying single updates. - */ - public function testSingleActionCalls() { - $db_schema = $this->database->schema(); - - // Ensure that a non-existing entity type cannot be installed. - $message = 'A non-existing entity type cannot be installed'; - try { - $this->entityDefinitionUpdateManager->installEntityType(new ContentEntityType(['id' => 'foo'])); - $this->fail($message); - } - catch (PluginNotFoundException $e) { - $this->pass($message); - } - - // Ensure that a field cannot be installed on non-existing entity type. - $message = 'A field cannot be installed on a non-existing entity type'; - try { - $storage_definition = BaseFieldDefinition::create('string') - ->setLabel(t('A new revisionable base field')) - ->setRevisionable(TRUE); - $this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'foo', 'entity_test', $storage_definition); - $this->fail($message); - } - catch (PluginNotFoundException $e) { - $this->pass($message); - } - - // Ensure that a non-existing field cannot be installed. - $storage_definition = BaseFieldDefinition::create('string') - ->setLabel(t('A new revisionable base field')) - ->setRevisionable(TRUE); - $this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'entity_test_update', 'entity_test', $storage_definition); - $this->assertFalse($db_schema->fieldExists('entity_test_update', 'bar'), "A non-existing field cannot be installed."); - - // Ensure that installing an existing entity type is a no-op. - $entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update'); - $this->entityDefinitionUpdateManager->installEntityType($entity_type); - $this->assertTrue($db_schema->tableExists('entity_test_update'), 'Installing an existing entity type is a no-op'); - - // Create a new base field. - $this->addRevisionableBaseField(); - $storage_definition = BaseFieldDefinition::create('string') - ->setLabel(t('A new revisionable base field')) - ->setRevisionable(TRUE); - $this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' does not exist before applying the update."); - $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition); - $this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table."); - - // Ensure that installing an existing field is a no-op. - $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition); - $this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), 'Installing an existing field is a no-op'); - - // Update an existing field schema. - $this->modifyBaseField(); - $storage_definition = BaseFieldDefinition::create('text') - ->setName('new_base_field') - ->setTargetEntityTypeId('entity_test_update') - ->setLabel(t('A new revisionable base field')) - ->setRevisionable(TRUE); - $this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition); - $this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "Previous schema for 'new_base_field' no longer exists."); - $this->assertTrue( - $db_schema->fieldExists('entity_test_update', 'new_base_field__value') && $db_schema->fieldExists('entity_test_update', 'new_base_field__format'), - "New schema for 'new_base_field' has been created." - ); - - // Drop an existing field schema. - $this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition); - $this->assertFalse( - $db_schema->fieldExists('entity_test_update', 'new_base_field__value') || $db_schema->fieldExists('entity_test_update', 'new_base_field__format'), - "The schema for 'new_base_field' has been dropped." - ); - - // Make the entity type revisionable. - $this->updateEntityTypeToRevisionable(); - $this->assertFalse($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' does not exist before applying the update."); - $entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update'); - $keys = $entity_type->getKeys(); - $keys['revision'] = 'revision_id'; - $entity_type->set('entity_keys', $keys); - $this->entityDefinitionUpdateManager->updateEntityType($entity_type); - $this->assertTrue($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' table has been created."); - } - - /** - * Ensures that a new field and index on a shared table are created. - * - * @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::createSharedTableSchema - */ - public function testCreateFieldAndIndexOnSharedTable() { - $this->addBaseField(); - $this->addBaseFieldIndex(); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table."); - $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), "New index 'entity_test_update_field__new_base_field' has been created on the 'entity_test_update' table."); - // Check index size in for MySQL. - if (Database::getConnection()->driver() == 'mysql') { - $result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update_field__new_base_field\' and column_name = \'new_base_field\'')->fetchObject(); - $this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.'); - } - } - - /** - * Ensures that a new entity level index is created when data exists. - * - * @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::onEntityTypeUpdate - */ - public function testCreateIndexUsingEntityStorageSchemaWithData() { - // Save an entity. - $name = $this->randomString(); - $storage = $this->entityManager->getStorage('entity_test_update'); - $entity = $storage->create(array('name' => $name)); - $entity->save(); - - // Create an index. - $indexes = array( - 'entity_test_update__type_index' => array('type'), - ); - $this->state->set('entity_test_update.additional_entity_indexes', $indexes); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__type_index'), "New index 'entity_test_update__type_index' has been created on the 'entity_test_update' table."); - // Check index size in for MySQL. - if (Database::getConnection()->driver() == 'mysql') { - $result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update__type_index\' and column_name = \'type\'')->fetchObject(); - $this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.'); - } - } - - /** - * Tests updating a base field when it has existing data. - */ - public function testBaseFieldEntityKeyUpdateWithExistingData() { - // Add the base field and run the update. - $this->addBaseField(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - // Save an entity with the base field populated. - $this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => $this->randomString()])->save(); - - // Save an entity with the base field not populated. - /** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */ - $entity = $this->entityManager->getStorage('entity_test_update')->create(); - $entity->save(); - - // Promote the base field to an entity key. This will trigger the addition - // of a NOT NULL constraint. - $this->makeBaseFieldEntityKey(); - - // Try to apply the update and verify they fail since we have a NULL value. - $message = 'An error occurs when trying to enabling NOT NULL constraints with NULL data.'; - try { - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->fail($message); - } - catch (EntityStorageException $e) { - $this->pass($message); - } - - // Check that the update is correctly applied when no NULL data is left. - $entity->set('new_base_field', $this->randomString()); - $entity->save(); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->pass('The update is correctly performed when no NULL data exists.'); - - // Check that the update actually applied a NOT NULL constraint. - $entity->set('new_base_field', NULL); - $message = 'The NOT NULL constraint was correctly applied.'; - try { - $entity->save(); - $this->fail($message); - } - catch (EntityStorageException $e) { - $this->pass($message); - } - } - - /** - * Check that field schema is correctly handled with long-named fields. - */ - function testLongNameFieldIndexes() { - $this->addLongNameBaseField(); - $entity_type_id = 'entity_test_update'; - $entity_type = $this->entityManager->getDefinition($entity_type_id); - $definitions = EntityTestUpdate::baseFieldDefinitions($entity_type); - $name = 'new_long_named_entity_reference_base_field'; - $this->entityDefinitionUpdateManager->installFieldStorageDefinition($name, $entity_type_id, 'entity_test', $definitions[$name]); - $this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'Entity and field schema data are correctly detected.'); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityFieldDefaultValueTest.php b/core/modules/system/src/Tests/Entity/EntityFieldDefaultValueTest.php deleted file mode 100644 index e8453ff..0000000 --- a/core/modules/system/src/Tests/Entity/EntityFieldDefaultValueTest.php +++ /dev/null @@ -1,79 +0,0 @@ -uuid = $this->container->get('uuid'); - } - - /** - * Tests default values on entities and fields. - */ - public function testDefaultValues() { - // All entity variations have to have the same results. - foreach (entity_test_entity_types() as $entity_type) { - $this->assertDefaultValues($entity_type); - } - } - - /** - * Executes a test set for a defined entity type. - * - * @param string $entity_type_id - * The entity type to run the tests with. - */ - protected function assertDefaultValues($entity_type_id) { - $entity = entity_create($entity_type_id); - $definition = $this->entityManager->getDefinition($entity_type_id); - $langcode_key = $definition->getKey('langcode'); - $this->assertEqual($entity->{$langcode_key}->value, 'en', SafeMarkup::format('%entity_type: Default language', array('%entity_type' => $entity_type_id))); - $this->assertTrue(Uuid::isValid($entity->uuid->value), SafeMarkup::format('%entity_type: Default UUID', array('%entity_type' => $entity_type_id))); - $this->assertEqual($entity->name->getValue(), array(), 'Field has one empty value by default.'); - } - - /** - * Tests custom default value callbacks. - */ - public function testDefaultValueCallback() { - $entity = $this->entityManager->getStorage('entity_test_default_value')->create(); - // The description field has a default value callback for testing, see - // entity_test_field_default_value(). - $string = 'description_' . $entity->language()->getId(); - $expected = array( - array( - 'shape' => "shape:0:$string", - 'color' => "color:0:$string", - ), - array( - 'shape' => "shape:1:$string", - 'color' => "color:1:$string", - ), - ); - $this->assertEqual($entity->description->getValue(), $expected); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityFieldTest.php b/core/modules/system/src/Tests/Entity/EntityFieldTest.php deleted file mode 100644 index 5c9161e..0000000 --- a/core/modules/system/src/Tests/Entity/EntityFieldTest.php +++ /dev/null @@ -1,739 +0,0 @@ -installEntitySchema($entity_type_id); - } - } - - // Create the test field. - module_load_install('entity_test'); - entity_test_install(); - - // Install required default configuration for filter module. - $this->installConfig(array('system', 'filter')); - } - - /** - * Creates a test entity. - * - * @return \Drupal\Core\Entity\EntityInterface - */ - protected function createTestEntity($entity_type) { - $this->entityName = $this->randomMachineName(); - $this->entityUser = $this->createUser(); - $this->entityFieldText = $this->randomMachineName(); - - // Pass in the value of the name field when creating. With the user - // field we test setting a field after creation. - $entity = entity_create($entity_type); - $entity->user_id->target_id = $this->entityUser->id(); - $entity->name->value = $this->entityName; - - // Set a value for the test field. - $entity->field_test_text->value = $this->entityFieldText; - - return $entity; - } - - /** - * Tests reading and writing properties and field items. - */ - public function testReadWrite() { - // All entity variations have to have the same results. - foreach (entity_test_entity_types() as $entity_type) { - $this->doTestReadWrite($entity_type); - } - } - - /** - * Executes the read write test set for a defined entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function doTestReadWrite($entity_type) { - $entity = $this->createTestEntity($entity_type); - - $langcode = 'en'; - - // Access the name field. - $this->assertTrue($entity->name instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', array('%entity_type' => $entity_type))); - $this->assertTrue($entity->name[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', array('%entity_type' => $entity_type))); - - $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($this->entityName, $entity->name[0]->value, format_string('%entity_type: Name value can be read through list access.', array('%entity_type' => $entity_type))); - $this->assertEqual($entity->name->getValue(), array(0 => array('value' => $this->entityName)), format_string('%entity_type: Plain field value returned.', array('%entity_type' => $entity_type))); - - // Change the name. - $new_name = $this->randomMachineName(); - $entity->name->value = $new_name; - $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read.', array('%entity_type' => $entity_type))); - $this->assertEqual($entity->name->getValue(), array(0 => array('value' => $new_name)), format_string('%entity_type: Plain field value reflects the update.', array('%entity_type' => $entity_type))); - - $new_name = $this->randomMachineName(); - $entity->name[0]->value = $new_name; - $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read through list access.', array('%entity_type' => $entity_type))); - - // Access the user field. - $this->assertTrue($entity->user_id instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', array('%entity_type' => $entity_type))); - $this->assertTrue($entity->user_id[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', array('%entity_type' => $entity_type))); - - $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type))); - - // Change the assigned user by entity. - $new_user1 = $this->createUser(); - $entity->user_id->entity = $new_user1; - $this->assertEqual($new_user1->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($new_user1->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', array('%entity_type' => $entity_type))); - - // Change the assigned user by id. - $new_user2 = $this->createUser(); - $entity->user_id->target_id = $new_user2->id(); - $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', array('%entity_type' => $entity_type))); - - // Try unsetting a field property. - $entity->name->value = NULL; - $entity->user_id->target_id = NULL; - $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', array('%entity_type' => $entity_type))); - $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', array('%entity_type' => $entity_type))); - $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', array('%entity_type' => $entity_type))); - - // Test setting the values via the typed data API works as well. - // Change the assigned user by entity. - $entity->user_id->first()->get('entity')->setValue($new_user2); - $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type))); - - // Change the assigned user by id. - $entity->user_id->first()->get('target_id')->setValue($new_user2->id()); - $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type))); - - // Try unsetting a field. - $entity->name->first()->get('value')->setValue(NULL); - $entity->user_id->first()->get('target_id')->setValue(NULL); - $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', array('%entity_type' => $entity_type))); - $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', array('%entity_type' => $entity_type))); - $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', array('%entity_type' => $entity_type))); - - // Create a fresh entity so target_id does not get its property object - // instantiated, then verify setting a new value via typed data API works. - $entity2 = entity_create($entity_type, array( - 'user_id' => array('target_id' => $new_user1->id()), - )); - // Access the property object, and set a value. - $entity2->user_id->first()->get('target_id')->setValue($new_user2->id()); - $this->assertEqual($new_user2->id(), $entity2->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($new_user2->name->value, $entity2->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type))); - - // Test using isset(), empty() and unset(). - $entity->name->value = 'test unset'; - unset($entity->name->value); - $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type))); - $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type))); - $this->assertTrue(empty($entity->name->value), format_string('%entity_type: Name is empty.', array('%entity_type' => $entity_type))); - $this->assertTrue(empty($entity->name[0]->value), format_string('%entity_type: Name is empty.', array('%entity_type' => $entity_type))); - - $entity->name->value = 'a value'; - $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type))); - $this->assertTrue(isset($entity->name[0]->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type))); - $this->assertFalse(empty($entity->name->value), format_string('%entity_type: Name is not empty.', array('%entity_type' => $entity_type))); - $this->assertFalse(empty($entity->name[0]->value), format_string('%entity_type: Name is not empty.', array('%entity_type' => $entity_type))); - $this->assertTrue(isset($entity->name[0]), format_string('%entity_type: Name string item is set.', array('%entity_type' => $entity_type))); - $this->assertFalse(isset($entity->name[1]), format_string('%entity_type: Second name string item is not set as it does not exist', array('%entity_type' => $entity_type))); - $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', array('%entity_type' => $entity_type))); - $this->assertFalse(isset($entity->nameInvalid), format_string('%entity_type: Not existing field is not set.', array('%entity_type' => $entity_type))); - - unset($entity->name[0]); - $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', array('%entity_type' => $entity_type))); - $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type))); - $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type))); - - // Test emptying a field by assigning an empty value. NULL and array() - // behave the same. - foreach ([NULL, array(), 'unset'] as $empty) { - // Make sure a value is present - $entity->name->value = 'a value'; - $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type))); - // Now, empty the field. - if ($empty === 'unset') { - unset($entity->name); - } - else { - $entity->name = $empty; - } - $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', array('%entity_type' => $entity_type))); - $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is set.', array('%entity_type' => $entity_type))); - $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', array('%entity_type' => $entity_type))); - $this->assertIdentical($entity->name->getValue(), array(), format_string('%entity_type: Name field value is an empty array.', array('%entity_type' => $entity_type))); - $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', array('%entity_type' => $entity_type))); - $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: First name item value is not set.', array('%entity_type' => $entity_type))); - $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name value is not set.', array('%entity_type' => $entity_type))); - } - - // Access the language field. - $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); - $this->assertEqual($langcode, $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual(\Drupal::languageManager()->getLanguage($langcode), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type))); - - // Change the language by code. - $entity->{$langcode_key}->value = \Drupal::languageManager()->getDefaultLanguage()->getId(); - $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type))); - - // Revert language by code then try setting it by language object. - $entity->{$langcode_key}->value = $langcode; - $entity->{$langcode_key}->language = \Drupal::languageManager()->getDefaultLanguage(); - $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type))); - - // Access the text field and test updating. - $this->assertEqual($entity->field_test_text->value, $this->entityFieldText, format_string('%entity_type: Text field can be read.', array('%entity_type' => $entity_type))); - $new_text = $this->randomMachineName(); - $entity->field_test_text->value = $new_text; - $this->assertEqual($entity->field_test_text->value, $new_text, format_string('%entity_type: Updated text field can be read.', array('%entity_type' => $entity_type))); - - // Test creating the entity by passing in plain values. - $this->entityName = $this->randomMachineName(); - $name_item[0]['value'] = $this->entityName; - $this->entityUser = $this->createUser(); - $user_item[0]['target_id'] = $this->entityUser->id(); - $this->entityFieldText = $this->randomMachineName(); - $text_item[0]['value'] = $this->entityFieldText; - - $entity = entity_create($entity_type, array( - 'name' => $name_item, - 'user_id' => $user_item, - 'field_test_text' => $text_item, - )); - $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', array('%entity_type' => $entity_type))); - - // Tests copying field values by assigning the TypedData objects. - $entity2 = $this->createTestEntity($entity_type); - $entity2->name = $entity->name; - $entity2->user_id = $entity->user_id; - $entity2->field_test_text = $entity->field_test_text; - $this->assertFalse($entity->name === $entity2->name, format_string('%entity_type: Copying properties results in a different field object.', array('%entity_type' => $entity_type))); - $this->assertEqual($entity->name->value, $entity2->name->value, format_string('%entity_type: Name field copied.', array('%entity_type' => $entity_type))); - $this->assertEqual($entity->user_id->target_id, $entity2->user_id->target_id, format_string('%entity_type: User id field copied.', array('%entity_type' => $entity_type))); - $this->assertEqual($entity->field_test_text->value, $entity2->field_test_text->value, format_string('%entity_type: Text field copied.', array('%entity_type' => $entity_type))); - - // Tests that assigning TypedData objects to non-field properties keeps the - // assigned value as is. - $entity2 = $this->createTestEntity($entity_type); - $entity2->_not_a_field = $entity->name; - $this->assertTrue($entity2->_not_a_field === $entity->name, format_string('%entity_type: Typed data objects can be copied to non-field properties as is.', array('%entity_type' => $entity_type))); - - // Tests adding a value to a field item list. - $entity->name[] = 'Another name'; - $this->assertEqual($entity->name[1]->value, 'Another name', format_string('%entity_type: List item added via [] and the first property.', array('%entity_type' => $entity_type))); - $entity->name[] = array('value' => 'Third name'); - $this->assertEqual($entity->name[2]->value, 'Third name', format_string('%entity_type: List item added via [] and an array of properties.', array('%entity_type' => $entity_type))); - $entity->name[3] = array('value' => 'Fourth name'); - $this->assertEqual($entity->name[3]->value, 'Fourth name', format_string('%entity_type: List item added via offset and an array of properties.', array('%entity_type' => $entity_type))); - unset($entity->name[3]); - - // Test removing and empty-ing list items. - $this->assertEqual(count($entity->name), 3, format_string('%entity_type: List has 3 items.', array('%entity_type' => $entity_type))); - unset($entity->name[1]); - $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Second list item has been removed.', array('%entity_type' => $entity_type))); - $this->assertEqual($entity->name[1]->value, 'Third name', format_string('%entity_type: The subsequent items have been shifted up.', array('%entity_type' => $entity_type))); - $this->assertEqual($entity->name[1]->getName(), 1, format_string('%entity_type: The items names have been updated to their new delta.', array('%entity_type' => $entity_type))); - $entity->name[1] = NULL; - $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Assigning NULL does not reduce array count.', array('%entity_type' => $entity_type))); - $this->assertTrue($entity->name[1]->isEmpty(), format_string('%entity_type: Assigning NULL empties the item.', array('%entity_type' => $entity_type))); - - // Test using isEmpty(). - unset($entity->name[1]); - $this->assertFalse($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is not empty.', array('%entity_type' => $entity_type))); - $entity->name->value = NULL; - $this->assertTrue($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is empty.', array('%entity_type' => $entity_type))); - $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is empty.', array('%entity_type' => $entity_type))); - $this->assertEqual(count($entity->name), 1, format_string('%entity_type: Empty item is considered when counting.', array('%entity_type' => $entity_type))); - $this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), format_string('%entity_type: Count matches iterator count.', array('%entity_type' => $entity_type))); - $this->assertTrue($entity->name->getValue() === array(0 => array('value' => NULL)), format_string('%entity_type: Name field value contains a NULL value.', array('%entity_type' => $entity_type))); - - // Test using filterEmptyItems(). - $entity->name = array(NULL, 'foo'); - $this->assertEqual(count($entity->name), 2, format_string('%entity_type: List has 2 items.', array('%entity_type' => $entity_type))); - $entity->name->filterEmptyItems(); - $this->assertEqual(count($entity->name), 1, format_string('%entity_type: The empty item was removed.', array('%entity_type' => $entity_type))); - $this->assertEqual($entity->name[0]->value, 'foo', format_string('%entity_type: The items were renumbered.', array('%entity_type' => $entity_type))); - $this->assertEqual($entity->name[0]->getName(), 0, format_string('%entity_type: The deltas were updated in the items.', array('%entity_type' => $entity_type))); - - // Test get and set field values. - $entity->name = 'foo'; - $this->assertEqual($entity->name[0]->toArray(), array('value' => 'foo'), format_string('%entity_type: Field value has been retrieved via toArray()', array('%entity_type' => $entity_type))); - - $values = $entity->toArray(); - $this->assertEqual($values['name'], array(0 => array('value' => 'foo')), format_string('%entity_type: Field value has been retrieved via toArray() from an entity.', array('%entity_type' => $entity_type))); - - // Make sure the user id can be set to zero. - $user_item[0]['target_id'] = 0; - $entity = entity_create($entity_type, array( - 'name' => $name_item, - 'user_id' => $user_item, - 'field_test_text' => $text_item, - )); - $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', array('%entity_type' => $entity_type))); - $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', array('%entity_type' => $entity_type))); - - // Test setting the ID with the value only. - $entity = entity_create($entity_type, array( - 'name' => $name_item, - 'user_id' => 0, - 'field_test_text' => $text_item, - )); - $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', array('%entity_type' => $entity_type))); - $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', array('%entity_type' => $entity_type))); - } - - /** - * Tries to save and load an entity again. - */ - public function testSave() { - // All entity variations have to have the same results. - foreach (entity_test_entity_types() as $entity_type) { - $this->doTestSave($entity_type); - } - } - - /** - * Executes the save tests for the given entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function doTestSave($entity_type) { - $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); - $entity = $this->createTestEntity($entity_type); - $entity->save(); - $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity has received an id.', array('%entity_type' => $entity_type))); - - $entity = entity_load($entity_type, $entity->id()); - $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity loaded.', array('%entity_type' => $entity_type))); - - // Access the name field. - $this->assertEqual(1, $entity->id->value, format_string('%entity_type: ID value can be read.', array('%entity_type' => $entity_type))); - $this->assertTrue(is_string($entity->uuid->value), format_string('%entity_type: UUID value can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual('en', $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual(\Drupal::languageManager()->getLanguage('en'), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type))); - $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', array('%entity_type' => $entity_type))); - } - - /** - * Tests introspection and getting metadata upfront. - */ - public function testIntrospection() { - // All entity variations have to have the same results. - foreach (entity_test_entity_types() as $entity_type) { - $this->doTestIntrospection($entity_type); - } - } - - /** - * Executes the introspection tests for the given entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function doTestIntrospection($entity_type) { - // Test getting metadata upfront. The entity types used for this test have - // a default bundle that is the same as the entity type. - $definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity_type); - $this->assertEqual($definitions['name']->getType(), 'string', $entity_type .': Name field found.'); - $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type .': User field found.'); - $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type .': Test-text-field field found.'); - - // Test deriving further metadata. - $this->assertTrue($definitions['name'] instanceof FieldDefinitionInterface); - $field_item_definition = $definitions['name']->getItemDefinition(); - $this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface); - $this->assertEqual($field_item_definition->getDataType(), 'field_item:string'); - $value_definition = $field_item_definition->getPropertyDefinition('value'); - $this->assertTrue($value_definition instanceof DataDefinitionInterface); - $this->assertEqual($value_definition->getDataType(), 'string'); - - // Test deriving metadata from references. - $entity_definition = \Drupal\Core\Entity\TypedData\EntityDataDefinition::create($entity_type); - $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); - $reference_definition = $entity_definition->getPropertyDefinition($langcode_key) - ->getPropertyDefinition('language') - ->getTargetDefinition(); - $this->assertEqual($reference_definition->getDataType(), 'language'); - - $reference_definition = $entity_definition->getPropertyDefinition('user_id') - ->getPropertyDefinition('entity') - ->getTargetDefinition(); - - $this->assertTrue($reference_definition instanceof \Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface, 'Definition of the referenced user retrieved.'); - $this->assertEqual($reference_definition->getEntityTypeId(), 'user', 'Referenced entity is of type "user".'); - - // Test propagating down. - $name_definition = $reference_definition->getPropertyDefinition('name'); - $this->assertTrue($name_definition instanceof FieldDefinitionInterface); - $this->assertEqual($name_definition->getPropertyDefinition('value')->getDataType(), 'string'); - - // Test introspecting an entity object. - // @todo: Add bundles and test bundles as well. - $entity = entity_create($entity_type); - - $definitions = $entity->getFieldDefinitions(); - $this->assertEqual($definitions['name']->getType(), 'string', $entity_type .': Name field found.'); - $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type .': User field found.'); - $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type .': Test-text-field field found.'); - - $name_properties = $entity->name->getFieldDefinition()->getPropertyDefinitions(); - $this->assertEqual($name_properties['value']->getDataType(), 'string', $entity_type .': String value property of the name found.'); - - $userref_properties = $entity->user_id->getFieldDefinition()->getPropertyDefinitions(); - $this->assertEqual($userref_properties['target_id']->getDataType(), 'integer', $entity_type .': Entity id property of the user found.'); - $this->assertEqual($userref_properties['entity']->getDataType(), 'entity_reference', $entity_type .': Entity reference property of the user found.'); - - $textfield_properties = $entity->field_test_text->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinitions(); - $this->assertEqual($textfield_properties['value']->getDataType(), 'string', $entity_type .': String value property of the test-text field found.'); - $this->assertEqual($textfield_properties['format']->getDataType(), 'filter_format', $entity_type .': String format field of the test-text field found.'); - $this->assertEqual($textfield_properties['processed']->getDataType(), 'string', $entity_type .': String processed property of the test-text field found.'); - - // Make sure provided contextual information is right. - $entity_adapter = $entity->getTypedData(); - $this->assertIdentical($entity_adapter->getRoot(), $entity_adapter, 'Entity is root object.'); - $this->assertEqual($entity_adapter->getPropertyPath(), ''); - $this->assertEqual($entity_adapter->getName(), ''); - $this->assertEqual($entity_adapter->getParent(), NULL); - - $field = $entity->user_id; - $this->assertIdentical($field->getRoot()->getValue(), $entity, 'Entity is root object.'); - $this->assertIdentical($field->getEntity(), $entity, 'getEntity() returns the entity.'); - $this->assertEqual($field->getPropertyPath(), 'user_id'); - $this->assertEqual($field->getName(), 'user_id'); - $this->assertIdentical($field->getParent()->getValue(), $entity, 'Parent object matches.'); - - $field_item = $field[0]; - $this->assertIdentical($field_item->getRoot()->getValue(), $entity, 'Entity is root object.'); - $this->assertIdentical($field_item->getEntity(), $entity, 'getEntity() returns the entity.'); - $this->assertEqual($field_item->getPropertyPath(), 'user_id.0'); - $this->assertEqual($field_item->getName(), '0'); - $this->assertIdentical($field_item->getParent(), $field, 'Parent object matches.'); - - $item_value = $field_item->get('entity'); - $this->assertIdentical($item_value->getRoot()->getValue(), $entity, 'Entity is root object.'); - $this->assertEqual($item_value->getPropertyPath(), 'user_id.0.entity'); - $this->assertEqual($item_value->getName(), 'entity'); - $this->assertIdentical($item_value->getParent(), $field_item, 'Parent object matches.'); - } - - /** - * Tests iterating over properties. - */ - public function testIterator() { - // All entity variations have to have the same results. - foreach (entity_test_entity_types() as $entity_type) { - $this->doTestIterator($entity_type); - } - } - - /** - * Executes the iterator tests for the given entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function doTestIterator($entity_type) { - $entity = $this->createTestEntity($entity_type); - - foreach ($entity as $name => $field) { - $this->assertTrue($field instanceof FieldItemListInterface, $entity_type . ": Field $name implements interface."); - - foreach ($field as $delta => $item) { - $this->assertTrue($field[0] instanceof FieldItemInterface, $entity_type . ": Item $delta of field $name implements interface."); - - foreach ($item as $value_name => $value_property) { - $this->assertTrue($value_property instanceof TypedDataInterface, $entity_type . ": Value $value_name of item $delta of field $name implements interface."); - - $value = $value_property->getValue(); - $this->assertTrue(!isset($value) || is_scalar($value) || $value instanceof EntityInterface, $entity_type . ": Value $value_name of item $delta of field $name is a primitive or an entity."); - } - } - } - - $fields = $entity->getFields(); - $this->assertEqual(array_keys($fields), array_keys($entity->getTypedData()->getDataDefinition()->getPropertyDefinitions()), format_string('%entity_type: All fields returned.', array('%entity_type' => $entity_type))); - $this->assertEqual($fields, iterator_to_array($entity->getIterator()), format_string('%entity_type: Entity iterator iterates over all fields.', array('%entity_type' => $entity_type))); - } - - /** - * Tests working with the entity based upon the TypedData API. - */ - public function testDataStructureInterfaces() { - // All entity variations have to have the same results. - foreach (entity_test_entity_types() as $entity_type) { - $this->doTestDataStructureInterfaces($entity_type); - } - } - - /** - * Executes the data structure interfaces tests for the given entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function doTestDataStructureInterfaces($entity_type) { - $entity = $this->createTestEntity($entity_type); - - // Test using the whole tree of typed data by navigating through the tree of - // contained properties and getting all contained strings, limited by a - // certain depth. - $strings = array(); - $this->getContainedStrings($entity->getTypedData(), 0, $strings); - - // @todo: Once the user entity has defined properties this should contain - // the user name and other user entity strings as well. - $target_strings = array( - $entity->uuid->value, - 'en', - $this->entityName, - // Bundle name. - $entity->bundle(), - $this->entityFieldText, - // Field format. - NULL, - ); - asort($strings); - asort($target_strings); - $this->assertEqual(array_values($strings), array_values($target_strings), format_string('%entity_type: All contained strings found.', array('%entity_type' => $entity_type))); - } - - /** - * Recursive helper for getting all contained strings, - * i.e. properties of type string. - */ - public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &$strings) { - - if ($wrapper instanceof StringInterface) { - $strings[] = $wrapper->getValue(); - } - - // Recurse until a certain depth is reached if possible. - if ($depth < 7) { - if ($wrapper instanceof \Drupal\Core\TypedData\ListInterface) { - foreach ($wrapper as $item) { - $this->getContainedStrings($item, $depth + 1, $strings); - } - } - elseif ($wrapper instanceof \Drupal\Core\TypedData\ComplexDataInterface) { - foreach ($wrapper as $property) { - $this->getContainedStrings($property, $depth + 1, $strings); - } - } - } - } - - /** - * Makes sure data types are correctly derived for all entity types. - */ - public function testDataTypes() { - $types = \Drupal::typedDataManager()->getDefinitions(); - foreach (entity_test_entity_types() as $entity_type) { - $this->assertTrue($types['entity:' . $entity_type]['class'], 'Entity data type registered.'); - } - // Check bundle types are provided as well. - entity_test_create_bundle('bundle'); - $types = \Drupal::typedDataManager()->getDefinitions(); - $this->assertTrue($types['entity:entity_test:bundle']['class'], 'Entity bundle data type registered.'); - } - - /** - * Tests a base field override on a non-existing base field. - * - * @see entity_test_entity_base_field_info_alter() - */ - public function testBaseFieldNonExistingBaseField() { - $this->entityManager->getStorage('node_type')->create(array( - 'type' => 'page', - 'name' => 'page', - ))->save(); - $this->entityManager->clearCachedFieldDefinitions(); - $fields = $this->entityManager->getFieldDefinitions('node', 'page'); - $override = $fields['status']->getConfig('page'); - $override->setLabel($this->randomString())->save(); - \Drupal::state()->set('entity_test.node_remove_status_field', TRUE); - $this->entityManager->clearCachedFieldDefinitions(); - $fields = $this->entityManager->getFieldDefinitions('node', 'page'); - // A base field override on a non-existing base field should not cause a - // field definition to come into existence. - $this->assertFalse(isset($fields['status']), 'Node\'s status base field does not exist.'); - } - - /** - * Tests creating a field override config for a bundle field. - * - * @see entity_test_entity_base_field_info_alter() - */ - public function testFieldOverrideBundleField() { - // First make sure the bundle field override in code, which is provided by - // the test entity works. - entity_test_create_bundle('some_test_bundle', 'Some test bundle', 'entity_test_field_override'); - $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'entity_test_field_override'); - $this->assertEqual($field_definitions['name']->getDescription(), 'The default description.'); - $this->assertNull($field_definitions['name']->getTargetBundle()); - - $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle'); - $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.'); - $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle'); - - // Now create a config override of the bundle field. - $field_config = $field_definitions['name']->getConfig('some_test_bundle'); - $field_config->setTranslatable(FALSE); - $field_config->save(); - - // Make sure both overrides are present. - $this->entityManager->clearCachedFieldDefinitions(); - $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle'); - $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.'); - $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle'); - $this->assertFalse($field_definitions['name']->isTranslatable()); - } - - /** - * Tests validation constraints provided by the Entity API. - */ - public function testEntityConstraintValidation() { - $entity = $this->createTestEntity('entity_test'); - $entity->save(); - // Create a reference field item and let it reference the entity. - $definition = BaseFieldDefinition::create('entity_reference') - ->setLabel('Test entity') - ->setSetting('target_type', 'entity_test'); - $reference_field = \Drupal::typedDataManager()->create($definition); - $reference = $reference_field->appendItem(array('entity' => $entity))->get('entity'); - - // Test validation the typed data object. - $violations = $reference->validate(); - $this->assertEqual($violations->count(), 0); - - // Test validating an entity of the wrong type. - $user = $this->createUser(); - $user->save(); - $node = entity_create('node', array( - 'type' => 'page', - 'uid' => $user->id(), - 'title' => $this->randomString(), - )); - $reference->setValue($node); - $violations = $reference->validate(); - $this->assertEqual($violations->count(), 1); - - // Test bundle validation. - NodeType::create(array('type' => 'article')) - ->save(); - $definition = BaseFieldDefinition::create('entity_reference') - ->setLabel('Test entity') - ->setSetting('target_type', 'node') - ->setSetting('handler_settings', ['target_bundles' => ['article' => 'article']]); - $reference_field = \Drupal::TypedDataManager()->create($definition); - $reference_field->appendItem(array('entity' => $node)); - $violations = $reference_field->validate(); - $this->assertEqual($violations->count(), 1); - - $node = entity_create('node', array( - 'type' => 'article', - 'uid' => $user->id(), - 'title' => $this->randomString(), - )); - $node->save(); - $reference_field->entity = $node; - $violations = $reference_field->validate(); - $this->assertEqual($violations->count(), 0); - } - - /** - * Tests getting processed property values via a computed property. - */ - public function testComputedProperties() { - // All entity variations have to have the same results. - foreach (entity_test_entity_types() as $entity_type) { - $this->doTestComputedProperties($entity_type); - } - } - - /** - * Executes the computed properties tests for the given entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function doTestComputedProperties($entity_type) { - $entity = $this->createTestEntity($entity_type); - $entity->field_test_text->value = "The text text to filter."; - $entity->field_test_text->format = filter_default_format(); - - $target = "

                          The <strong>text</strong> text to filter.

                          \n"; - $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', array('%entity_type' => $entity_type))); - - // Save and load entity and make sure it still works. - $entity->save(); - $entity = entity_load($entity_type, $entity->id()); - $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', array('%entity_type' => $entity_type))); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityLanguageTestBase.php b/core/modules/system/src/Tests/Entity/EntityLanguageTestBase.php deleted file mode 100644 index 61a5ad2..0000000 --- a/core/modules/system/src/Tests/Entity/EntityLanguageTestBase.php +++ /dev/null @@ -1,141 +0,0 @@ -languageManager = $this->container->get('language_manager'); - - foreach (entity_test_entity_types() as $entity_type_id) { - // The entity_test schema is installed by the parent. - if ($entity_type_id != 'entity_test') { - $this->installEntitySchema($entity_type_id); - } - } - - $this->installConfig(array('language')); - - // Create the test field. - module_load_install('entity_test'); - entity_test_install(); - - // Enable translations for the test entity type. - $this->state->set('entity_test.translation', TRUE); - - // Create a translatable test field. - $this->fieldName = Unicode::strtolower($this->randomMachineName() . '_field_name'); - - // Create an untranslatable test field. - $this->untranslatableFieldName = Unicode::strtolower($this->randomMachineName() . '_field_name'); - - // Create field fields in all entity variations. - foreach (entity_test_entity_types() as $entity_type) { - entity_create('field_storage_config', array( - 'field_name' => $this->fieldName, - 'entity_type' => $entity_type, - 'type' => 'text', - 'cardinality' => 4, - ))->save(); - entity_create('field_config', array( - 'field_name' => $this->fieldName, - 'entity_type' => $entity_type, - 'bundle' => $entity_type, - 'translatable' => TRUE, - ))->save(); - $this->field[$entity_type] = FieldConfig::load($entity_type . '.' . $entity_type . '.' . $this->fieldName); - - entity_create('field_storage_config', array( - 'field_name' => $this->untranslatableFieldName, - 'entity_type' => $entity_type, - 'type' => 'text', - 'cardinality' => 4, - ))->save(); - entity_create('field_config', array( - 'field_name' => $this->untranslatableFieldName, - 'entity_type' => $entity_type, - 'bundle' => $entity_type, - 'translatable' => FALSE, - ))->save(); - } - - // Create the default languages. - $this->installConfig(array('language')); - - // Create test languages. - $this->langcodes = array(); - for ($i = 0; $i < 3; ++$i) { - $language = ConfigurableLanguage::create(array( - 'id' => 'l' . $i, - 'label' => $this->randomString(), - 'weight' => $i, - )); - $this->langcodes[$i] = $language->getId(); - $language->save(); - } - } - - /** - * Toggles field storage translatability. - * - * @param string $entity_type - * The type of the entity fields are attached to. - */ - protected function toggleFieldTranslatability($entity_type, $bundle) { - $fields = array($this->fieldName, $this->untranslatableFieldName); - foreach ($fields as $field_name) { - $field = FieldConfig::loadByName($entity_type, $bundle, $field_name); - $translatable = !$field->isTranslatable(); - $field->set('translatable', $translatable); - $field->save(); - $field = FieldConfig::loadByName($entity_type, $bundle, $field_name); - $this->assertEqual($field->isTranslatable(), $translatable, 'Field translatability changed.'); - } - \Drupal::cache('entity')->deleteAll(); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityListBuilderTest.php b/core/modules/system/src/Tests/Entity/EntityListBuilderTest.php index f38c62d..ec2ac42 100644 --- a/core/modules/system/src/Tests/Entity/EntityListBuilderTest.php +++ b/core/modules/system/src/Tests/Entity/EntityListBuilderTest.php @@ -8,6 +8,7 @@ namespace Drupal\system\Tests\Entity; use Drupal\Core\Language\LanguageInterface; +use Drupal\entity_test\Entity\EntityTest; use Drupal\simpletest\WebTestBase; /** @@ -41,7 +42,7 @@ protected function setUp() { public function testPager() { // Create 51 test entities. for ($i = 1; $i < 52; $i++) { - entity_create('entity_test', array('name' => 'Test entity ' . $i))->save(); + EntityTest::create(array('name' => 'Test entity ' . $i))->save(); } // Load the listing page. diff --git a/core/modules/system/src/Tests/Entity/EntityQueryAggregateTest.php b/core/modules/system/src/Tests/Entity/EntityQueryAggregateTest.php deleted file mode 100644 index a3262ea..0000000 --- a/core/modules/system/src/Tests/Entity/EntityQueryAggregateTest.php +++ /dev/null @@ -1,585 +0,0 @@ -entityStorage = $this->entityManager->getStorage('entity_test'); - $this->factory = $this->container->get('entity.query'); - - // Add some fieldapi fields to be used in the test. - for ($i = 1; $i <= 2; $i++) { - $field_name = 'field_test_' . $i; - entity_create('field_storage_config', array( - 'field_name' => $field_name, - 'entity_type' => 'entity_test', - 'type' => 'integer', - 'cardinality' => 2, - ))->save(); - entity_create('field_config', array( - 'field_name' => $field_name, - 'entity_type' => 'entity_test', - 'bundle' => 'entity_test', - ))->save(); - } - - $entity = $this->entityStorage->create(array( - 'id' => 1, - 'user_id' => 1, - 'field_test_1' => 1, - 'field_test_2' => 2, - )); - $entity->enforceIsNew(); - $entity->save(); - - $entity = $this->entityStorage->create(array( - 'id' => 2, - 'user_id' => 2, - 'field_test_1' => 1, - 'field_test_2' => 7, - )); - $entity->enforceIsNew(); - $entity->save(); - $entity = $this->entityStorage->create(array( - 'id' => 3, - 'user_id' => 2, - 'field_test_1' => 2, - 'field_test_2' => 1, - )); - $entity->enforceIsNew(); - $entity->save(); - $entity = $this->entityStorage->create(array( - 'id' => 4, - 'user_id' => 2, - 'field_test_1' => 2, - 'field_test_2' => 8, - )); - $entity->enforceIsNew(); - $entity->save(); - $entity = $this->entityStorage->create(array( - 'id' => 5, - 'user_id' => 3, - 'field_test_1' => 2, - 'field_test_2' => 2, - )); - $entity->enforceIsNew(); - $entity->save(); - $entity = $this->entityStorage->create(array( - 'id' => 6, - 'user_id' => 3, - 'field_test_1' => 3, - 'field_test_2' => 8, - )); - $entity->enforceIsNew(); - $entity->save(); - - } - - /** - * Test aggregation support. - */ - public function testAggregation() { - // Apply a simple groupby. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->groupBy('user_id') - ->execute(); - - $this->assertResults(array( - array('user_id' => 1), - array('user_id' => 2), - array('user_id' => 3), - )); - - $function_expected = array(); - $function_expected['count'] = array(array('id_count' => 6)); - $function_expected['min'] = array(array('id_min' => 1)); - $function_expected['max'] = array(array('id_max' => 6)); - $function_expected['sum'] = array(array('id_sum' => 21)); - $function_expected['avg'] = array(array('id_avg' => (21.0/6.0))); - - // Apply a simple aggregation for different aggregation functions. - foreach ($function_expected as $aggregation_function => $expected) { - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('id', $aggregation_function) - ->execute(); - $this->assertEqual($this->queryResult, $expected); - } - - // Apply aggregation and groupby on the same query. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('id', 'COUNT') - ->groupBy('user_id') - ->execute(); - $this->assertResults(array( - array('user_id' => 1, 'id_count' => 1), - array('user_id' => 2, 'id_count' => 3), - array('user_id' => 3, 'id_count' => 2), - )); - - // Apply aggregation and a condition which matches. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('id', 'COUNT') - ->groupBy('id') - ->conditionAggregate('id', 'COUNT', 8) - ->execute(); - $this->assertResults(array()); - - // Don't call aggregate to test the implicit aggregate call. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->groupBy('id') - ->conditionAggregate('id', 'COUNT', 8) - ->execute(); - $this->assertResults(array()); - - // Apply aggregation and a condition which matches. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('id', 'count') - ->groupBy('id') - ->conditionAggregate('id', 'COUNT', 6) - ->execute(); - $this->assertResults(array(array('id_count' => 6))); - - // Apply aggregation, a groupby and a condition which matches partially via - // the operator '='. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('id', 'count') - ->conditionAggregate('id', 'count', 2) - ->groupBy('user_id') - ->execute(); - $this->assertResults(array(array('id_count' => 2, 'user_id' => 3))); - - // Apply aggregation, a groupby and a condition which matches partially via - // the operator '>'. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('id', 'count') - ->conditionAggregate('id', 'COUNT', 1, '>') - ->groupBy('user_id') - ->execute(); - $this->assertResults(array( - array('id_count' => 2, 'user_id' => 3), - array('id_count' => 3, 'user_id' => 2), - )); - - // Apply aggregation and a sort. This might not be useful, but have a proper - // test coverage. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('id', 'COUNT') - ->sortAggregate('id', 'COUNT') - ->execute(); - $this->assertSortedResults(array(array('id_count' => 6))); - - // Don't call aggregate to test the implicit aggregate call. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->sortAggregate('id', 'COUNT') - ->execute(); - $this->assertSortedResults(array(array('id_count' => 6))); - - // Apply aggregation, groupby and a sort descending. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('id', 'COUNT') - ->groupBy('user_id') - ->sortAggregate('id', 'COUNT', 'DESC') - ->execute(); - $this->assertSortedResults(array( - array('user_id' => 2, 'id_count' => 3), - array('user_id' => 3, 'id_count' => 2), - array('user_id' => 1, 'id_count' => 1), - )); - - // Apply aggregation, groupby and a sort ascending. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('id', 'COUNT') - ->groupBy('user_id') - ->sortAggregate('id', 'COUNT', 'ASC') - ->execute(); - $this->assertSortedResults(array( - array('user_id' => 1, 'id_count' => 1), - array('user_id' => 3, 'id_count' => 2), - array('user_id' => 2, 'id_count' => 3), - )); - - // Apply aggregation, groupby, an aggregation condition and a sort with the - // operator '='. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('id', 'COUNT') - ->groupBy('user_id') - ->sortAggregate('id', 'COUNT') - ->conditionAggregate('id', 'COUNT', 2) - ->execute(); - $this->assertSortedResults(array(array('id_count' => 2, 'user_id' => 3))); - - // Apply aggregation, groupby, an aggregation condition and a sort with the - // operator '<' and order ASC. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('id', 'COUNT') - ->groupBy('user_id') - ->sortAggregate('id', 'COUNT', 'ASC') - ->conditionAggregate('id', 'COUNT', 3, '<') - ->execute(); - $this->assertSortedResults(array( - array('id_count' => 1, 'user_id' => 1), - array('id_count' => 2, 'user_id' => 3), - )); - - // Apply aggregation, groupby, an aggregation condition and a sort with the - // operator '<' and order DESC. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('id', 'COUNT') - ->groupBy('user_id') - ->sortAggregate('id', 'COUNT', 'DESC') - ->conditionAggregate('id', 'COUNT', 3, '<') - ->execute(); - $this->assertSortedResults(array( - array('id_count' => 2, 'user_id' => 3), - array('id_count' => 1, 'user_id' => 1), - )); - - // Test aggregation/groupby support for fieldapi fields. - - // Just group by a fieldapi field. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->groupBy('field_test_1') - ->execute(); - $this->assertResults(array( - array('field_test_1' => 1), - array('field_test_1' => 2), - array('field_test_1' => 3), - )); - - // Group by a fieldapi field and aggregate a normal property. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('user_id', 'COUNT') - ->groupBy('field_test_1') - ->execute(); - - $this->assertResults(array( - array('field_test_1' => 1, 'user_id_count' => 2), - array('field_test_1' => 2, 'user_id_count' => 3), - array('field_test_1' => 3, 'user_id_count' => 1), - )); - - // Group by a normal property and aggregate a fieldapi field. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('field_test_1', 'COUNT') - ->groupBy('user_id') - ->execute(); - - $this->assertResults(array( - array('user_id' => 1, 'field_test_1_count' => 1), - array('user_id' => 2, 'field_test_1_count' => 3), - array('user_id' => 3, 'field_test_1_count' => 2), - )); - - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('field_test_1', 'SUM') - ->groupBy('user_id') - ->execute(); - $this->assertResults(array( - array('user_id' => 1, 'field_test_1_sum' => 1), - array('user_id' => 2, 'field_test_1_sum' => 5), - array('user_id' => 3, 'field_test_1_sum' => 5), - )); - - // Aggregate by two different fieldapi fields. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('field_test_1', 'SUM') - ->aggregate('field_test_2', 'SUM') - ->groupBy('user_id') - ->execute(); - $this->assertResults(array( - array('user_id' => 1, 'field_test_1_sum' => 1, 'field_test_2_sum' => 2), - array('user_id' => 2, 'field_test_1_sum' => 5, 'field_test_2_sum' => 16), - array('user_id' => 3, 'field_test_1_sum' => 5, 'field_test_2_sum' => 10), - )); - - // This time aggregate the same field twice. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('field_test_1', 'SUM') - ->aggregate('field_test_1', 'COUNT') - ->groupBy('user_id') - ->execute(); - $this->assertResults(array( - array('user_id' => 1, 'field_test_1_sum' => 1, 'field_test_1_count' => 1), - array('user_id' => 2, 'field_test_1_sum' => 5, 'field_test_1_count' => 3), - array('user_id' => 3, 'field_test_1_sum' => 5, 'field_test_1_count' => 2), - )); - - // Group by and aggregate by a fieldapi field. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->groupBy('field_test_1') - ->aggregate('field_test_2', 'COUNT') - ->execute(); - $this->assertResults(array( - array('field_test_1' => 1, 'field_test_2_count' => 2), - array('field_test_1' => 2, 'field_test_2_count' => 3), - array('field_test_1' => 3, 'field_test_2_count' => 1), - )); - - // Group by and aggregate by a fieldapi field and use multiple aggregate - // functions. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->groupBy('field_test_1') - ->aggregate('field_test_2', 'COUNT') - ->aggregate('field_test_2', 'SUM') - ->execute(); - $this->assertResults(array( - array('field_test_1' => 1, 'field_test_2_count' => 2, 'field_test_2_sum' => 9), - array('field_test_1' => 2, 'field_test_2_count' => 3, 'field_test_2_sum' => 11), - array('field_test_1' => 3, 'field_test_2_count' => 1, 'field_test_2_sum' => 8), - )); - - // Apply an aggregate condition for a fieldapi field and group by a simple - // property. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->conditionAggregate('field_test_1', 'COUNT', 3) - ->groupBy('user_id') - ->execute(); - $this->assertResults(array( - array('user_id' => 2, 'field_test_1_count' => 3), - array('user_id' => 3, 'field_test_1_count' => 2), - )); - - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('field_test_1', 'SUM') - ->conditionAggregate('field_test_1', 'COUNT', 2, '>') - ->groupBy('user_id') - ->execute(); - $this->assertResults(array( - array('user_id' => 2, 'field_test_1_sum' => 5, 'field_test_1_count' => 3), - array('user_id' => 3, 'field_test_1_sum' => 5, 'field_test_1_count' => 2), - )); - - // Apply an aggregate condition for a simple property and a group by a - // fieldapi field. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->conditionAggregate('user_id', 'COUNT', 2) - ->groupBy('field_test_1') - ->execute(); - $this->assertResults(array( - array('field_test_1' => 1, 'user_id_count' => 2), - )); - - $this->queryResult = $this->factory->getAggregate('entity_test') - ->conditionAggregate('user_id', 'COUNT', 2, '>') - ->groupBy('field_test_1') - ->execute(); - $this->assertResults(array( - array('field_test_1' => 1, 'user_id_count' => 2), - array('field_test_1' => 2, 'user_id_count' => 3), - )); - - // Apply an aggregate condition and a group by fieldapi fields. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->groupBy('field_test_1') - ->conditionAggregate('field_test_2', 'COUNT', 2) - ->execute(); - $this->assertResults(array( - array('field_test_1' => 1, 'field_test_2_count' => 2), - )); - $this->queryResult = $this->factory->getAggregate('entity_test') - ->groupBy('field_test_1') - ->conditionAggregate('field_test_2', 'COUNT', 2, '>') - ->execute(); - $this->assertResults(array( - array('field_test_1' => 1, 'field_test_2_count' => 2), - array('field_test_1' => 2, 'field_test_2_count' => 3), - )); - - // Apply an aggregate condition and a group by fieldapi fields with multiple - // conditions via AND. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->groupBy('field_test_1') - ->conditionAggregate('field_test_2', 'COUNT', 2) - ->conditionAggregate('field_test_2', 'SUM', 8) - ->execute(); - $this->assertResults(array()); - - // Apply an aggregate condition and a group by fieldapi fields with multiple - // conditions via OR. - $this->queryResult = $this->factory->getAggregate('entity_test', 'OR') - ->groupBy('field_test_1') - ->conditionAggregate('field_test_2', 'COUNT', 2) - ->conditionAggregate('field_test_2', 'SUM', 8) - ->execute(); - $this->assertResults(array( - array('field_test_1' => 1, 'field_test_2_count' => 2, 'field_test_2_sum' => 9), - array('field_test_1' => 3, 'field_test_2_count' => 1, 'field_test_2_sum' => 8), - )); - - // Group by a normal property and aggregate a fieldapi field and sort by the - // groupby field. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('field_test_1', 'COUNT') - ->groupBy('user_id') - ->sort('user_id', 'DESC') - ->execute(); - $this->assertSortedResults(array( - array('user_id' => 3, 'field_test_1_count' => 2), - array('user_id' => 2, 'field_test_1_count' => 3), - array('user_id' => 1, 'field_test_1_count' => 1), - )); - - $this->queryResult = $this->factory->getAggregate('entity_test') - ->aggregate('field_test_1', 'COUNT') - ->groupBy('user_id') - ->sort('user_id', 'ASC') - ->execute(); - $this->assertSortedResults(array( - array('user_id' => 1, 'field_test_1_count' => 1), - array('user_id' => 2, 'field_test_1_count' => 3), - array('user_id' => 3, 'field_test_1_count' => 2), - )); - - $this->queryResult = $this->factory->getAggregate('entity_test') - ->conditionAggregate('field_test_1', 'COUNT', 2, '>') - ->groupBy('user_id') - ->sort('user_id', 'ASC') - ->execute(); - $this->assertSortedResults(array( - array('user_id' => 2, 'field_test_1_count' => 3), - array('user_id' => 3, 'field_test_1_count' => 2), - )); - - // Group by a normal property, aggregate a fieldapi field, and sort by the - // aggregated field. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->sortAggregate('field_test_1', 'COUNT', 'DESC') - ->groupBy('user_id') - ->execute(); - $this->assertSortedResults(array( - array('user_id' => 2, 'field_test_1_count' => 3), - array('user_id' => 3, 'field_test_1_count' => 2), - array('user_id' => 1, 'field_test_1_count' => 1), - )); - - $this->queryResult = $this->factory->getAggregate('entity_test') - ->sortAggregate('field_test_1', 'COUNT', 'ASC') - ->groupBy('user_id') - ->execute(); - $this->assertSortedResults(array( - array('user_id' => 1, 'field_test_1_count' => 1), - array('user_id' => 3, 'field_test_1_count' => 2), - array('user_id' => 2, 'field_test_1_count' => 3), - )); - - // Group by and aggregate by fieldapi field, and sort by the groupby field. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->groupBy('field_test_1') - ->aggregate('field_test_2', 'COUNT') - ->sort('field_test_1', 'ASC') - ->execute(); - $this->assertSortedResults(array( - array('field_test_1' => 1, 'field_test_2_count' => 2), - array('field_test_1' => 2, 'field_test_2_count' => 3), - array('field_test_1' => 3, 'field_test_2_count' => 1), - )); - - $this->queryResult = $this->factory->getAggregate('entity_test') - ->groupBy('field_test_1') - ->aggregate('field_test_2', 'COUNT') - ->sort('field_test_1', 'DESC') - ->execute(); - $this->assertSortedResults(array( - array('field_test_1' => 3, 'field_test_2_count' => 1), - array('field_test_1' => 2, 'field_test_2_count' => 3), - array('field_test_1' => 1, 'field_test_2_count' => 2), - )); - - // Groupby and aggregate by fieldapi field, and sort by the aggregated - // field. - $this->queryResult = $this->factory->getAggregate('entity_test') - ->groupBy('field_test_1') - ->sortAggregate('field_test_2', 'COUNT', 'DESC') - ->execute(); - $this->assertSortedResults(array( - array('field_test_1' => 2, 'field_test_2_count' => 3), - array('field_test_1' => 1, 'field_test_2_count' => 2), - array('field_test_1' => 3, 'field_test_2_count' => 1), - )); - - $this->queryResult = $this->factory->getAggregate('entity_test') - ->groupBy('field_test_1') - ->sortAggregate('field_test_2', 'COUNT', 'ASC') - ->execute(); - $this->assertSortedResults(array( - array('field_test_1' => 3, 'field_test_2_count' => 1), - array('field_test_1' => 1, 'field_test_2_count' => 2), - array('field_test_1' => 2, 'field_test_2_count' => 3), - )); - - } - - /** - * Asserts the results as expected regardless of order between and in rows. - * - * @param array $expected - * An array of the expected results. - */ - protected function assertResults($expected, $sorted = FALSE) { - $found = TRUE; - $expected_keys = array_keys($expected); - foreach ($this->queryResult as $key => $row) { - $keys = $sorted ? array($key) : $expected_keys; - foreach ($keys as $key) { - $expected_row = $expected[$key]; - if (!array_diff_assoc($row, $expected_row) && !array_diff_assoc($expected_row, $row)) { - continue 2; - } - } - $found = FALSE; - break; - } - return $this->assertTrue($found, strtr('!expected expected, !found found', array('!expected' => print_r($expected, TRUE), '!found' => print_r($this->queryResult, TRUE)))); - } - - /** - * Asserts the results as expected regardless of order in rows. - * - * @param array $expected - * An array of the expected results. - */ - protected function assertSortedResults($expected) { - return $this->assertResults($expected, TRUE); - } -} diff --git a/core/modules/system/src/Tests/Entity/EntityQueryRelationshipTest.php b/core/modules/system/src/Tests/Entity/EntityQueryRelationshipTest.php deleted file mode 100644 index 02a1566..0000000 --- a/core/modules/system/src/Tests/Entity/EntityQueryRelationshipTest.php +++ /dev/null @@ -1,174 +0,0 @@ -installEntitySchema('taxonomy_term'); - - // We want an entity reference field. It needs a vocabulary, terms, a field - // storage and a field. First, create the vocabulary. - $vocabulary = entity_create('taxonomy_vocabulary', array( - 'vid' => Unicode::strtolower($this->randomMachineName()), - )); - $vocabulary->save(); - - // Second, create the field. - entity_test_create_bundle('test_bundle'); - $this->fieldName = strtolower($this->randomMachineName()); - $handler_settings = array( - 'target_bundles' => array( - $vocabulary->id() => $vocabulary->id(), - ), - 'auto_create' => TRUE, - ); - $this->createEntityReferenceField('entity_test', 'test_bundle', $this->fieldName, NULL, 'taxonomy_term', 'default', $handler_settings); - - // Create two terms and also two accounts. - for ($i = 0; $i <= 1; $i++) { - $term = entity_create('taxonomy_term', array( - 'name' => $this->randomMachineName(), - 'vid' => $vocabulary->id(), - )); - $term->save(); - $this->terms[] = $term; - $this->accounts[] = $this->createUser(); - } - // Create three entity_test entities, the 0th entity will point to the - // 0th account and 0th term, the 1st and 2nd entity will point to the - // 1st account and 1st term. - for ($i = 0; $i <= 2; $i++) { - $entity = entity_create('entity_test', array('type' => 'test_bundle')); - $entity->name->value = $this->randomMachineName(); - $index = $i ? 1 : 0; - $entity->user_id->target_id = $this->accounts[$index]->id(); - $entity->{$this->fieldName}->target_id = $this->terms[$index]->id(); - $entity->save(); - $this->entities[] = $entity; - } - $this->factory = \Drupal::service('entity.query'); - } - - /** - * Tests querying. - */ - public function testQuery() { - // This returns the 0th entity as that's only one pointing to the 0th - // account. - $this->queryResults = $this->factory->get('entity_test') - ->condition("user_id.entity.name", $this->accounts[0]->getUsername()) - ->execute(); - $this->assertResults(array(0)); - // This returns the 1st and 2nd entity as those point to the 1st account. - $this->queryResults = $this->factory->get('entity_test') - ->condition("user_id.entity.name", $this->accounts[0]->getUsername(), '<>') - ->execute(); - $this->assertResults(array(1, 2)); - // This returns all three entities because all of them point to an - // account. - $this->queryResults = $this->factory->get('entity_test') - ->exists("user_id.entity.name") - ->execute(); - $this->assertResults(array(0, 1, 2)); - // This returns no entities because all of them point to an account. - $this->queryResults = $this->factory->get('entity_test') - ->notExists("user_id.entity.name") - ->execute(); - $this->assertEqual(count($this->queryResults), 0); - // This returns the 0th entity as that's only one pointing to the 0th - // term (test without specifying the field column). - $this->queryResults = $this->factory->get('entity_test') - ->condition("$this->fieldName.entity.name", $this->terms[0]->name->value) - ->execute(); - $this->assertResults(array(0)); - // This returns the 0th entity as that's only one pointing to the 0th - // term (test with specifying the column name). - $this->queryResults = $this->factory->get('entity_test') - ->condition("$this->fieldName.target_id.entity.name", $this->terms[0]->name->value) - ->execute(); - $this->assertResults(array(0)); - // This returns the 1st and 2nd entity as those point to the 1st term. - $this->queryResults = $this->factory->get('entity_test') - ->condition("$this->fieldName.entity.name", $this->terms[0]->name->value, '<>') - ->execute(); - $this->assertResults(array(1, 2)); - } - - /** - * Assert the results. - * - * @param array $expected - * A list of indexes in the $this->entities array. - */ - protected function assertResults($expected) { - $this->assertEqual(count($this->queryResults), count($expected)); - foreach ($expected as $key) { - $id = $this->entities[$key]->id(); - $this->assertEqual($this->queryResults[$id], $id); - } - } -} diff --git a/core/modules/system/src/Tests/Entity/EntityQueryTest.php b/core/modules/system/src/Tests/Entity/EntityQueryTest.php deleted file mode 100644 index 932e5fb..0000000 --- a/core/modules/system/src/Tests/Entity/EntityQueryTest.php +++ /dev/null @@ -1,869 +0,0 @@ -installEntitySchema('entity_test_mulrev'); - - $this->installConfig(array('language')); - - $figures = Unicode::strtolower($this->randomMachineName()); - $greetings = Unicode::strtolower($this->randomMachineName()); - foreach (array($figures => 'shape', $greetings => 'text') as $field_name => $field_type) { - $field_storage = entity_create('field_storage_config', array( - 'field_name' => $field_name, - 'entity_type' => 'entity_test_mulrev', - 'type' => $field_type, - 'cardinality' => 2, - )); - $field_storage->save(); - $field_storages[] = $field_storage; - } - $bundles = array(); - for ($i = 0; $i < 2; $i++) { - // For the sake of tablesort, make sure the second bundle is higher than - // the first one. Beware: MySQL is not case sensitive. - do { - $bundle = $this->randomMachineName(); - } while ($bundles && strtolower($bundles[0]) >= strtolower($bundle)); - entity_test_create_bundle($bundle); - foreach ($field_storages as $field_storage) { - entity_create('field_config', array( - 'field_storage' => $field_storage, - 'bundle' => $bundle, - ))->save(); - } - $bundles[] = $bundle; - } - // Each unit is a list of field name, langcode and a column-value array. - $units[] = array($figures, 'en', array( - 'color' => 'red', - 'shape' => 'triangle', - )); - $units[] = array($figures, 'en', array( - 'color' => 'blue', - 'shape' => 'circle', - )); - // To make it easier to test sorting, the greetings get formats according - // to their langcode. - $units[] = array($greetings, 'tr', array( - 'value' => 'merhaba', - 'format' => 'format-tr' - )); - $units[] = array($greetings, 'pl', array( - 'value' => 'siema', - 'format' => 'format-pl' - )); - // Make these languages available to the greetings field. - ConfigurableLanguage::createFromLangcode('tr')->save(); - ConfigurableLanguage::createFromLangcode('pl')->save(); - // Calculate the cartesian product of the unit array by looking at the - // bits of $i and add the unit at the bits that are 1. For example, - // decimal 13 is binary 1101 so unit 3,2 and 0 will be added to the - // entity. - for ($i = 1; $i <= 15; $i++) { - $entity = EntityTestMulRev::create(array( - 'type' => $bundles[$i & 1], - 'name' => $this->randomMachineName(), - 'langcode' => 'en', - )); - // Make sure the name is set for every language that we might create. - foreach (array('tr', 'pl') as $langcode) { - $entity->addTranslation($langcode)->name = $this->randomMachineName(); - } - foreach (array_reverse(str_split(decbin($i))) as $key => $bit) { - if ($bit) { - list($field_name, $langcode, $values) = $units[$key]; - $entity->getTranslation($langcode)->{$field_name}[] = $values; - } - } - $entity->save(); - } - $this->bundles = $bundles; - $this->figures = $figures; - $this->greetings = $greetings; - $this->factory = \Drupal::service('entity.query'); - } - - /** - * Test basic functionality. - */ - function testEntityQuery() { - $greetings = $this->greetings; - $figures = $this->figures; - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->exists($greetings, 'tr') - ->condition("$figures.color", 'red') - ->sort('id') - ->execute(); - // As unit 0 was the red triangle and unit 2 was the turkish greeting, - // bit 0 and bit 2 needs to be set. - $this->assertResult(5, 7, 13, 15); - - $query = $this->factory->get('entity_test_mulrev', 'OR') - ->exists($greetings, 'tr') - ->condition("$figures.color", 'red') - ->sort('id'); - $count_query = clone $query; - $this->assertEqual(12, $count_query->count()->execute()); - $this->queryResults = $query->execute(); - // Now bit 0 (1, 3, 5, 7, 9, 11, 13, 15) or bit 2 (4, 5, 6, 7, 12, 13, 14, - // 15) needs to be set. - $this->assertResult(1, 3, 4, 5, 6, 7, 9, 11, 12, 13, 14, 15); - - // Test cloning of query conditions. - $query = $this->factory->get('entity_test_mulrev') - ->condition("$figures.color", 'red') - ->sort('id'); - $cloned_query = clone $query; - $cloned_query - ->condition("$figures.shape", 'circle'); - // Bit 0 (1, 3, 5, 7, 9, 11, 13, 15) needs to be set. - $this->queryResults = $query->execute(); - $this->assertResult(1, 3, 5, 7, 9, 11, 13, 15); - // No red color has a circle shape. - $this->queryResults = $cloned_query->execute(); - $this->assertResult(); - - $query = $this->factory->get('entity_test_mulrev'); - $group = $query->orConditionGroup() - ->exists($greetings, 'tr') - ->condition("$figures.color", 'red'); - $this->queryResults = $query - ->condition($group) - ->condition("$greetings.value", 'sie', 'STARTS_WITH') - ->sort('revision_id') - ->execute(); - // Bit 3 and (bit 0 or 2) -- the above 8 part of the above. - $this->assertResult(9, 11, 12, 13, 14, 15); - - // No figure has both the colors blue and red at the same time. - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->condition("$figures.color", 'blue') - ->condition("$figures.color", 'red') - ->sort('id') - ->execute(); - $this->assertResult(); - - // But an entity might have a red and a blue figure both. - $query = $this->factory->get('entity_test_mulrev'); - $group_blue = $query->andConditionGroup()->condition("$figures.color", 'blue'); - $group_red = $query->andConditionGroup()->condition("$figures.color", 'red'); - $this->queryResults = $query - ->condition($group_blue) - ->condition($group_red) - ->sort('revision_id') - ->execute(); - // Unit 0 and unit 1, so bits 0 1. - $this->assertResult(3, 7, 11, 15); - - // Do the same test but with IN operator. - $query = $this->factory->get('entity_test_mulrev'); - $group_blue = $query->andConditionGroup()->condition("$figures.color", array('blue'), 'IN'); - $group_red = $query->andConditionGroup()->condition("$figures.color", array('red'), 'IN'); - $this->queryResults = $query - ->condition($group_blue) - ->condition($group_red) - ->sort('id') - ->execute(); - // Unit 0 and unit 1, so bits 0 1. - $this->assertResult(3, 7, 11, 15); - - // An entity might have either red or blue figure. - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->condition("$figures.color", array('blue', 'red'), 'IN') - ->sort('id') - ->execute(); - // Bit 0 or 1 is on. - $this->assertResult(1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15); - - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->exists("$figures.color") - ->notExists("$greetings.value") - ->sort('id') - ->execute(); - // Bit 0 or 1 is on but 2 and 3 are not. - $this->assertResult(1, 2, 3); - // Now update the 'merhaba' string to xsiemax which is not a meaningful - // word but allows us to test revisions and string operations. - $ids = $this->factory->get('entity_test_mulrev') - ->condition("$greetings.value", 'merhaba') - ->sort('id') - ->execute(); - $entities = entity_load_multiple('entity_test_mulrev', $ids); - $first_entity = reset($entities); - $old_name = $first_entity->name->value; - foreach ($entities as $entity) { - $entity->setNewRevision(); - $entity->getTranslation('tr')->$greetings->value = 'xsiemax'; - $entity->name->value .= 'x'; - $entity->save(); - } - // We changed the entity names, so the current revision should not match. - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->condition('name.value', $old_name) - ->execute(); - $this->assertResult(); - // Only if all revisions are queried, we find the old revision. - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->condition('name.value', $old_name) - ->allRevisions() - ->sort('revision_id') - ->execute(); - $this->assertRevisionResult(array($first_entity->id()), array($first_entity->id())); - // When querying current revisions, this string is no longer found. - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->condition("$greetings.value", 'merhaba') - ->execute(); - $this->assertResult(); - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->condition("$greetings.value", 'merhaba') - ->allRevisions() - ->sort('revision_id') - ->execute(); - // The query only matches the original revisions. - $this->assertRevisionResult(array(4, 5, 6, 7, 12, 13, 14, 15), array(4, 5, 6, 7, 12, 13, 14, 15)); - $results = $this->factory->get('entity_test_mulrev') - ->condition("$greetings.value", 'siema', 'CONTAINS') - ->sort('id') - ->execute(); - // This matches both the original and new current revisions, multiple - // revisions are returned for some entities. - $assert = array(16 => '4', 17 => '5', 18 => '6', 19 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 20 => '12', 21 => '13', 22 => '14', 23 => '15'); - $this->assertIdentical($results, $assert); - $results = $this->factory->get('entity_test_mulrev') - ->condition("$greetings.value", 'siema', 'STARTS_WITH') - ->sort('revision_id') - ->execute(); - // Now we only get the ones that originally were siema, entity id 8 and - // above. - $this->assertIdentical($results, array_slice($assert, 4, 8, TRUE)); - $results = $this->factory->get('entity_test_mulrev') - ->condition("$greetings.value", 'a', 'ENDS_WITH') - ->sort('revision_id') - ->execute(); - // It is very important that we do not get the ones which only have - // xsiemax despite originally they were merhaba, ie. ended with a. - $this->assertIdentical($results, array_slice($assert, 4, 8, TRUE)); - $results = $this->factory->get('entity_test_mulrev') - ->condition("$greetings.value", 'a', 'ENDS_WITH') - ->allRevisions() - ->sort('id') - ->sort('revision_id') - ->execute(); - // Now we get everything. - $assert = array(4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 12 => '12', 20 => '12', 13 => '13', 21 => '13', 14 => '14', 22 => '14', 15 => '15', 23 => '15'); - $this->assertIdentical($results, $assert); - } - - /** - * Test sort(). - * - * Warning: this is complicated. - */ - function testSort() { - $greetings = $this->greetings; - $figures = $this->figures; - // Order up and down on a number. - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->sort('id') - ->execute(); - $this->assertResult(range(1, 15)); - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->sort('id', 'DESC') - ->execute(); - $this->assertResult(range(15, 1)); - $query = $this->factory->get('entity_test_mulrev') - ->sort("$figures.color") - ->sort("$greetings.format") - ->sort('id'); - // As we do not have any conditions, here are the possible colors and - // language codes, already in order, with the first occurrence of the - // entity id marked with *: - // 8 NULL pl * - // 12 NULL pl * - - // 4 NULL tr * - // 12 NULL tr - - // 2 blue NULL * - // 3 blue NULL * - - // 10 blue pl * - // 11 blue pl * - // 14 blue pl * - // 15 blue pl * - - // 6 blue tr * - // 7 blue tr * - // 14 blue tr - // 15 blue tr - - // 1 red NULL - // 3 red NULL - - // 9 red pl * - // 11 red pl - // 13 red pl * - // 15 red pl - - // 5 red tr * - // 7 red tr - // 13 red tr - // 15 red tr - $count_query = clone $query; - $this->assertEqual(15, $count_query->count()->execute()); - $this->queryResults = $query->execute(); - $this->assertResult(8, 12, 4, 2, 3, 10, 11, 14, 15, 6, 7, 1, 9, 13, 5); - - // Test the pager by setting element #1 to page 2 with a page size of 4. - // Results will be #8-12 from above. - $request = Request::createFromGlobals(); - $request->query->replace(array( - 'page' => '0,2', - )); - \Drupal::getContainer()->get('request_stack')->push($request); - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->sort("$figures.color") - ->sort("$greetings.format") - ->sort('id') - ->pager(4, 1) - ->execute(); - $this->assertResult(15, 6, 7, 1); - - // Now test the reversed order. - $query = $this->factory->get('entity_test_mulrev') - ->sort("$figures.color", 'DESC') - ->sort("$greetings.format", 'DESC') - ->sort('id', 'DESC'); - $count_query = clone $query; - $this->assertEqual(15, $count_query->count()->execute()); - $this->queryResults = $query->execute(); - $this->assertResult(15, 13, 7, 5, 11, 9, 3, 1, 14, 6, 10, 2, 12, 4, 8); - } - - /** - * Test tablesort(). - */ - public function testTableSort() { - // While ordering on bundles do not give us a definite order, we can still - // assert that all entities from one bundle are after the other as the - // order dictates. - $request = Request::createFromGlobals(); - $request->query->replace(array( - 'sort' => 'asc', - 'order' => 'Type', - )); - \Drupal::getContainer()->get('request_stack')->push($request); - - $header = array( - 'id' => array('data' => 'Id', 'specifier' => 'id'), - 'type' => array('data' => 'Type', 'specifier' => 'type'), - ); - - $this->queryResults = array_values($this->factory->get('entity_test_mulrev') - ->tableSort($header) - ->execute()); - $this->assertBundleOrder('asc'); - - $request->query->add(array( - 'sort' => 'desc', - )); - \Drupal::getContainer()->get('request_stack')->push($request); - - $header = array( - 'id' => array('data' => 'Id', 'specifier' => 'id'), - 'type' => array('data' => 'Type', 'specifier' => 'type'), - ); - $this->queryResults = array_values($this->factory->get('entity_test_mulrev') - ->tableSort($header) - ->execute()); - $this->assertBundleOrder('desc'); - - // Ordering on ID is definite, however. - $request->query->add(array( - 'order' => 'Id', - )); - \Drupal::getContainer()->get('request_stack')->push($request); - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->tableSort($header) - ->execute(); - $this->assertResult(range(15, 1)); - } - - /** - * Test that count queries are separated across entity types. - */ - public function testCount() { - // Create a field with the same name in a different entity type. - $field_name = $this->figures; - $field_storage = entity_create('field_storage_config', array( - 'field_name' => $field_name, - 'entity_type' => 'entity_test', - 'type' => 'shape', - 'cardinality' => 2, - 'translatable' => TRUE, - )); - $field_storage->save(); - $bundle = $this->randomMachineName(); - entity_create('field_config', array( - 'field_storage' => $field_storage, - 'bundle' => $bundle, - ))->save(); - - $entity = entity_create('entity_test', array( - 'id' => 1, - 'type' => $bundle, - )); - $entity->enforceIsNew(); - $entity->save(); - - // As the single entity of this type we just saved does not have a value - // in the color field, the result should be 0. - $count = $this->factory->get('entity_test') - ->exists("$field_name.color") - ->count() - ->execute(); - $this->assertFalse($count); - } - - /** - * Tests that nested condition groups work as expected. - */ - public function testNestedConditionGroups() { - // Query for all entities of the first bundle that have either a red - // triangle as a figure or the Turkish greeting as a greeting. - $query = $this->factory->get('entity_test_mulrev'); - - $first_and = $query->andConditionGroup() - ->condition($this->figures . '.color', 'red') - ->condition($this->figures . '.shape', 'triangle'); - $second_and = $query->andConditionGroup() - ->condition($this->greetings . '.value', 'merhaba') - ->condition($this->greetings . '.format', 'format-tr'); - - $or = $query->orConditionGroup() - ->condition($first_and) - ->condition($second_and); - - $this->queryResults = $query - ->condition($or) - ->condition('type', reset($this->bundles)) - ->sort('id') - ->execute(); - - $this->assertResult(6, 14); - } - - protected function assertResult() { - $assert = array(); - $expected = func_get_args(); - if ($expected && is_array($expected[0])) { - $expected = $expected[0]; - } - foreach ($expected as $binary) { - $assert[$binary] = strval($binary); - } - $this->assertIdentical($this->queryResults, $assert); - } - - protected function assertRevisionResult($keys, $expected) { - $assert = array(); - foreach ($expected as $key => $binary) { - $assert[$keys[$key]] = strval($binary); - } - $this->assertIdentical($this->queryResults, $assert); - return $assert; - } - - protected function assertBundleOrder($order) { - // This loop is for bundle1 entities. - for ($i = 1; $i <= 15; $i +=2) { - $ok = TRUE; - $index1 = array_search($i, $this->queryResults); - $this->assertNotIdentical($index1, FALSE, "$i found at $index1."); - // This loop is for bundle2 entities. - for ($j = 2; $j <= 15; $j += 2) { - if ($ok) { - if ($order == 'asc') { - $ok = $index1 > array_search($j, $this->queryResults); - } - else { - $ok = $index1 < array_search($j, $this->queryResults); - } - } - } - $this->assertTrue($ok, "$i is after all entities in bundle2"); - } - } - - /** - * Test adding a tag and metadata to the Entity query object. - * - * The tags and metadata should propagate to the SQL query object. - */ - public function testMetaData() { - $query = \Drupal::entityQuery('entity_test_mulrev'); - $query - ->addTag('efq_metadata_test') - ->addMetaData('foo', 'bar') - ->execute(); - - global $efq_test_metadata; - $this->assertEqual($efq_test_metadata, 'bar', 'Tag and metadata propagated to the SQL query object.'); - } - - /** - * Test case sensitive and in-sensitive query conditions. - */ - public function testCaseSensitivity() { - $bundle = $this->randomMachineName(); - - $field_storage = FieldStorageConfig::create(array( - 'field_name' => 'field_ci', - 'entity_type' => 'entity_test_mulrev', - 'type' => 'string', - 'cardinality' => 1, - 'translatable' => FALSE, - 'settings' => array( - 'case_sensitive' => FALSE, - ) - )); - $field_storage->save(); - - FieldConfig::create(array( - 'field_storage' => $field_storage, - 'bundle' => $bundle, - ))->save(); - - $field_storage = FieldStorageConfig::create(array( - 'field_name' => 'field_cs', - 'entity_type' => 'entity_test_mulrev', - 'type' => 'string', - 'cardinality' => 1, - 'translatable' => FALSE, - 'settings' => array( - 'case_sensitive' => TRUE, - ), - )); - $field_storage->save(); - - FieldConfig::create(array( - 'field_storage' => $field_storage, - 'bundle' => $bundle, - ))->save(); - - $fixtures = array(); - - for ($i = 0; $i < 2; $i++) { - // If the last 4 of the string are all numbers, then there is no - // difference between upper and lowercase and the case sensitive CONTAINS - // test will fail. Ensure that can not happen by appending a non-numeric - // character. See https://www.drupal.org/node/2397297. - $string = $this->randomMachineName(7) . 'a'; - $fixtures[] = array( - 'original' => $string, - 'uppercase' => Unicode::strtoupper($string), - 'lowercase' => Unicode::strtolower($string), - ); - } - - EntityTestMulRev::create(array( - 'type' => $bundle, - 'name' => $this->randomMachineName(), - 'langcode' => 'en', - 'field_ci' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], - 'field_cs' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'] - ))->save(); - - // Check the case insensitive field, = operator. - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_ci', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase'] - )->execute(); - $this->assertIdentical(count($result), 1, 'Case insensitive, lowercase'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase'] - )->execute(); - $this->assertIdentical(count($result), 1, 'Case insensitive, uppercase'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'] - )->execute(); - $this->assertIdentical(count($result), 1, 'Case insensitive, mixed.'); - - // Check the case sensitive field, = operator. - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_cs', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase'] - )->execute(); - $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase'] - )->execute(); - $this->assertIdentical(count($result), 0, 'Case sensitive, uppercase.'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'] - )->execute(); - $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.'); - - // Check the case insensitive field, IN operator. - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_ci', array($fixtures[0]['lowercase'] . $fixtures[1]['lowercase']), 'IN' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case insensitive, lowercase'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_ci', array($fixtures[0]['uppercase'] . $fixtures[1]['uppercase']), 'IN' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case insensitive, uppercase'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_ci', array($fixtures[0]['uppercase'] . $fixtures[1]['lowercase']), 'IN' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case insensitive, mixed'); - - // Check the case sensitive field, IN operator. - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_cs', array($fixtures[0]['lowercase'] . $fixtures[1]['lowercase']), 'IN' - )->execute(); - $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_cs', array($fixtures[0]['uppercase'] . $fixtures[1]['uppercase']), 'IN' - )->execute(); - $this->assertIdentical(count($result), 0, 'Case sensitive, uppercase'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_cs', array($fixtures[0]['uppercase'] . $fixtures[1]['lowercase']), 'IN' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case sensitive, mixed'); - - // Check the case insensitive field, STARTS_WITH operator. - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_ci', $fixtures[0]['lowercase'], 'STARTS_WITH' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_ci', $fixtures[0]['uppercase'], 'STARTS_WITH' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.'); - - // Check the case sensitive field, STARTS_WITH operator. - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_cs', $fixtures[0]['lowercase'], 'STARTS_WITH' - )->execute(); - $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_cs', $fixtures[0]['uppercase'], 'STARTS_WITH' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.'); - - - // Check the case insensitive field, ENDS_WITH operator. - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_ci', $fixtures[1]['lowercase'], 'ENDS_WITH' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_ci', $fixtures[1]['uppercase'], 'ENDS_WITH' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.'); - - // Check the case sensitive field, ENDS_WITH operator. - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_cs', $fixtures[1]['lowercase'], 'ENDS_WITH' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_cs', $fixtures[1]['uppercase'], 'ENDS_WITH' - )->execute(); - $this->assertIdentical(count($result), 0, 'Case sensitive, exact match.'); - - - // Check the case insensitive field, CONTAINS operator, use the inner 8 - // characters of the uppercase and lowercase strings. - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_ci', Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_ci', Unicode::strtolower(Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.'); - - // Check the case sensitive field, CONTAINS operator. - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_cs', Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS' - )->execute(); - $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.'); - - $result = \Drupal::entityQuery('entity_test_mulrev')->condition( - 'field_cs', Unicode::strtolower(Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS' - )->execute(); - $this->assertIdentical(count($result), 0, 'Case sensitive, exact match.'); - - } - - /** - * Test base fields with multiple columns. - */ - public function testBaseFieldMultipleColumns() { - $this->enableModules(['taxonomy']); - $this->installEntitySchema('taxonomy_term'); - - Vocabulary::create(['vid' => 'tags']); - - $term1 = Term::create([ - 'name' => $this->randomMachineName(), - 'vid' => 'tags', - 'description' => array( - 'value' => $this->randomString(), - 'format' => 'format1', - )]); - $term1->save(); - - $term2 = Term::create([ - 'name' => $this->randomMachineName(), - 'vid' => 'tags', - 'description' => array( - 'value' => $this->randomString(), - 'format' => 'format2', - )]); - $term2->save(); - - $ids = \Drupal::entityQuery('taxonomy_term') - ->condition('description.format', 'format1') - ->execute(); - - $this->assertEqual(count($ids), 1); - $this->assertEqual($term1->id(), reset($ids)); - } - - /** - * Test forward-revisions. - */ - public function testForwardRevisions() { - // Ensure entity 14 is returned. - $result = \Drupal::entityQuery('entity_test_mulrev') - ->condition('id', [14], 'IN') - ->execute(); - $this->assertEqual(count($result), 1); - - // Set a revision on entity 14 that isn't the current default. - $entity = EntityTestMulRev::load(14); - $current_values = $entity->{$this->figures}->getValue(); - - $entity->setNewRevision(TRUE); - $entity->isDefaultRevision(FALSE); - $entity->{$this->figures}->setValue([ - 'color' => 'red', - 'shape' => 'square' - ]); - $entity->save(); - - // Entity query should still return entity 14. - $result = \Drupal::entityQuery('entity_test_mulrev') - ->condition('id', [14], 'IN') - ->execute(); - $this->assertEqual(count($result), 1); - - // Verify that field conditions on the default and forward revision are - // work as expected. - $result = \Drupal::entityQuery('entity_test_mulrev') - ->condition('id', [14], 'IN') - ->condition("$this->figures.color", $current_values[0]['color']) - ->execute(); - $this->assertEqual($result, [14 => '14']); - $result = $this->factory->get('entity_test_mulrev') - ->condition('id', [14], 'IN') - ->condition("$this->figures.color", 'red') - ->allRevisions() - ->execute(); - $this->assertEqual($result, [16 => '14']); - } - - /** - * Test against SQL inject of condition field. This covers a - * database driver's EntityQuery\Condition class. - */ - public function testInjectionInCondition() { - try { - $this->queryResults = $this->factory->get('entity_test_mulrev') - ->condition('1 ; -- ', array(0, 1), 'IN') - ->sort('id') - ->execute(); - $this->fail('SQL Injection attempt in Entity Query condition in operator should result in an exception.'); - } - catch (\Exception $e) { - $this->pass('SQL Injection attempt in Entity Query condition in operator should result in an exception.'); - } - } -} diff --git a/core/modules/system/src/Tests/Entity/EntityReferenceFieldTest.php b/core/modules/system/src/Tests/Entity/EntityReferenceFieldTest.php deleted file mode 100644 index aa49b84..0000000 --- a/core/modules/system/src/Tests/Entity/EntityReferenceFieldTest.php +++ /dev/null @@ -1,441 +0,0 @@ -installEntitySchema('entity_test_rev'); - - // Create a field. - $this->createEntityReferenceField( - $this->entityType, - $this->bundle, - $this->fieldName, - 'Field test', - $this->referencedEntityType, - 'default', - array('target_bundles' => array($this->bundle)), - FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED - ); - - } - - /** - * Tests reference field validation. - */ - public function testEntityReferenceFieldValidation() { - // Test a valid reference. - $referenced_entity = entity_create($this->referencedEntityType, array('type' => $this->bundle)); - $referenced_entity->save(); - - $entity = entity_create($this->entityType, array('type' => $this->bundle)); - $entity->{$this->fieldName}->target_id = $referenced_entity->id(); - $violations = $entity->{$this->fieldName}->validate(); - $this->assertEqual($violations->count(), 0, 'Validation passes.'); - - // Test an invalid reference. - $entity->{$this->fieldName}->target_id = 9999; - $violations = $entity->{$this->fieldName}->validate(); - $this->assertEqual($violations->count(), 1, 'Validation throws a violation.'); - $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', array('%type' => $this->referencedEntityType, '%id' => 9999))); - - // Test a non-referenceable bundle. - entity_test_create_bundle('non_referenceable', NULL, $this->referencedEntityType); - $referenced_entity = entity_create($this->referencedEntityType, array('type' => 'non_referenceable')); - $referenced_entity->save(); - $entity->{$this->fieldName}->target_id = $referenced_entity->id(); - $violations = $entity->{$this->fieldName}->validate(); - $this->assertEqual($violations->count(), 1, 'Validation throws a violation.'); - $this->assertEqual($violations[0]->getMessage(), t('This entity (%type: %id) cannot be referenced.', array('%type' => $this->referencedEntityType, '%id' => $referenced_entity->id()))); - } - - /** - * Tests the multiple target entities loader. - */ - public function testReferencedEntitiesMultipleLoad() { - // Create the parent entity. - $entity = entity_create($this->entityType, array('type' => $this->bundle)); - - // Create three target entities and attach them to parent field. - $target_entities = array(); - $reference_field = array(); - for ($i = 0; $i < 3; $i++) { - $target_entity = entity_create($this->referencedEntityType, array('type' => $this->bundle)); - $target_entity->save(); - $target_entities[] = $target_entity; - $reference_field[]['target_id'] = $target_entity->id(); - } - - // Also attach a non-existent entity and a NULL target id. - $reference_field[3]['target_id'] = 99999; - $target_entities[3] = NULL; - $reference_field[4]['target_id'] = NULL; - $target_entities[4] = NULL; - - // Attach the first created target entity as the sixth item ($delta == 5) of - // the parent entity field. We want to test the case when the same target - // entity is referenced twice (or more times) in the same entity reference - // field. - $reference_field[5] = $reference_field[0]; - $target_entities[5] = $target_entities[0]; - - // Create a new target entity that is not saved, thus testing the - // "autocreate" feature. - $target_entity_unsaved = entity_create($this->referencedEntityType, array('type' => $this->bundle, 'name' => $this->randomString())); - $reference_field[6]['entity'] = $target_entity_unsaved; - $target_entities[6] = $target_entity_unsaved; - - // Set the field value. - $entity->{$this->fieldName}->setValue($reference_field); - - // Load the target entities using EntityReferenceField::referencedEntities(). - $entities = $entity->{$this->fieldName}->referencedEntities(); - - // Test returned entities: - // - Deltas must be preserved. - // - Non-existent entities must not be retrieved in target entities result. - foreach ($target_entities as $delta => $target_entity) { - if (!empty($target_entity)) { - if (!$target_entity->isNew()) { - // There must be an entity in the loaded set having the same id for - // the same delta. - $this->assertEqual($target_entity->id(), $entities[$delta]->id()); - } - else { - // For entities that were not yet saved, there must an entity in the - // loaded set having the same label for the same delta. - $this->assertEqual($target_entity->label(), $entities[$delta]->label()); - } - } - else { - // A non-existent or NULL entity target id must not return any item in - // the target entities set. - $this->assertFalse(isset($entities[$delta])); - } - } - } - - /** - * Tests referencing entities with string IDs. - */ - public function testReferencedEntitiesStringId() { - $field_name = 'entity_reference_string_id'; - $this->installEntitySchema('entity_test_string_id'); - $this->createEntityReferenceField( - $this->entityType, - $this->bundle, - $field_name, - 'Field test', - 'entity_test_string_id', - 'default', - array('target_bundles' => array($this->bundle)), - FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED - ); - // Create the parent entity. - $entity = entity_create($this->entityType, array('type' => $this->bundle)); - - // Create the default target entity. - $target_entity = entity_create('entity_test_string_id', array('id' => $this->randomString(), 'type' => $this->bundle)); - $target_entity->save(); - - // Set the field value. - $entity->{$field_name}->setValue(array(array('target_id' => $target_entity->id()))); - - // Load the target entities using EntityReferenceField::referencedEntities(). - $entities = $entity->{$field_name}->referencedEntities(); - $this->assertEqual($entities[0]->id(), $target_entity->id()); - - // Test that a string ID works as a default value and the field's config - // schema is correct. - $field = FieldConfig::loadByName($this->entityType, $this->bundle, $field_name); - $field->setDefaultValue($target_entity->id()); - $field->save(); - $this->assertConfigSchema(\Drupal::service('config.typed'), 'field.field.' . $field->id(), $field->toArray()); - - // Test that the default value works. - $entity = entity_create($this->entityType, array('type' => $this->bundle)); - $entities = $entity->{$field_name}->referencedEntities(); - $this->assertEqual($entities[0]->id(), $target_entity->id()); - } - - /** - * Tests all the possible ways to autocreate an entity via the API. - */ - function testAutocreateApi() { - $entity = $this->entityManager - ->getStorage($this->entityType) - ->create(array('name' => $this->randomString())); - - // Test content entity autocreation. - $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { - $entity->set('user_id', $user); - }); - $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { - $entity->set('user_id', $user, FALSE); - }); - $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { - $entity->user_id->setValue($user); - }); - $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { - $entity->user_id[0]->get('entity')->setValue($user); - }); - $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { - $entity->user_id->setValue(array('entity' => $user, 'target_id' => NULL)); - }); - try { - $message = 'Setting both the entity and an invalid target_id property fails.'; - $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { - $user->save(); - $entity->user_id->setValue(array('entity' => $user, 'target_id' => $this->generateRandomEntityId())); - }); - $this->fail($message); - } - catch (\InvalidArgumentException $e) { - $this->pass($message); - } - $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { - $entity->user_id = $user; - }); - $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { - $entity->user_id->entity = $user; - }); - - // Test config entity autocreation. - $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { - $entity->set('user_role', $role); - }); - $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { - $entity->set('user_role', $role, FALSE); - }); - $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { - $entity->user_role->setValue($role); - }); - $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { - $entity->user_role[0]->get('entity')->setValue($role); - }); - $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { - $entity->user_role->setValue(array('entity' => $role, 'target_id' => NULL)); - }); - try { - $message = 'Setting both the entity and an invalid target_id property fails.'; - $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { - $role->save(); - $entity->user_role->setValue(array('entity' => $role, 'target_id' => $this->generateRandomEntityId(TRUE))); - }); - $this->fail($message); - } - catch (\InvalidArgumentException $e) { - $this->pass($message); - } - $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { - $entity->user_role = $role; - }); - $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { - $entity->user_role->entity = $role; - }); - - // Test target entity saving after setting it as new. - $storage = $this->entityManager->getStorage('user'); - $user_id = $this->generateRandomEntityId(); - $user = $storage->create(array('uid' => $user_id, 'name' => $this->randomString())); - $entity->user_id = $user; - $user->save(); - $entity->save(); - $this->assertEqual($entity->user_id->target_id, $user->id()); - } - - /** - * Asserts that the setter callback performs autocreation for users. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The referencing entity. - * @param $setter_callback - * A callback setting the target entity on the referencing entity. - * - * @return bool - * TRUE if the user was autocreated, FALSE otherwise. - */ - protected function assertUserAutocreate(EntityInterface $entity, $setter_callback) { - $storage = $this->entityManager->getStorage('user'); - $user_id = $this->generateRandomEntityId(); - $user = $storage->create(array('uid' => $user_id, 'name' => $this->randomString())); - $setter_callback($entity, $user); - $entity->save(); - $storage->resetCache(); - $user = User::load($user_id); - return $this->assertEqual($entity->user_id->target_id, $user->id()); - } - - /** - * Asserts that the setter callback performs autocreation for user roles. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The referencing entity. - * @param $setter_callback - * A callback setting the target entity on the referencing entity. - * - * @return bool - * TRUE if the user was autocreated, FALSE otherwise. - */ - protected function assertUserRoleAutocreate(EntityInterface $entity, $setter_callback) { - $storage = $this->entityManager->getStorage('user_role'); - $role_id = $this->generateRandomEntityId(TRUE); - $role = $storage->create(array('id' => $role_id, 'label' => $this->randomString())); - $setter_callback($entity, $role); - $entity->save(); - $storage->resetCache(); - $role = Role::load($role_id); - return $this->assertEqual($entity->user_role->target_id, $role->id()); - } - - /** - * Tests that the target entity is not unnecessarily loaded. - */ - public function testTargetEntityNoLoad() { - // Setup a test entity type with an entity reference field to itself. We use - // a special storage class throwing exceptions when a load operation is - // triggered to be able to detect them. - $entity_type = clone $this->entityManager->getDefinition('entity_test_update'); - $entity_type->setHandlerClass('storage', '\Drupal\entity_test\EntityTestNoLoadStorage'); - $this->state->set('entity_test_update.entity_type', $entity_type); - $definitions = array( - 'target_reference' => BaseFieldDefinition::create('entity_reference') - ->setSetting('target_type', $entity_type->id()) - ->setSetting('handler', 'default') - ); - $this->state->set('entity_test_update.additional_base_field_definitions', $definitions); - $this->entityManager->clearCachedDefinitions(); - $this->installEntitySchema($entity_type->id()); - - // Create the target entity. - $storage = $this->entityManager->getStorage($entity_type->id()); - $target_id = $this->generateRandomEntityId(); - $target = $storage->create(array('id' => $target_id, 'name' => $this->randomString())); - $target->save(); - $this->assertEqual($target_id, $target->id(), 'The target entity has a random identifier.'); - - // Check that populating the reference with an existing target id does not - // trigger a load operation. - $message = 'The target entity was not loaded.'; - try { - $entity = $this->entityManager - ->getStorage($entity_type->id()) - ->create(array('name' => $this->randomString())); - $entity->target_reference = $target_id; - $this->pass($message); - } - catch (EntityStorageException $e) { - $this->fail($message); - } - - // Check that the storage actually triggers the expected exception when - // trying to load the target entity. - $message = 'An exception is thrown when trying to load the target entity'; - try { - $storage->load($target_id); - $this->fail($message); - } - catch (EntityStorageException $e) { - $this->pass($message); - } - } - - /** - * Tests the dependencies entity reference fields are created with. - */ - public function testEntityReferenceFieldDependencies() { - $field_name = 'user_reference_field'; - $entity_type = 'entity_test'; - - $field_storage = FieldStorageConfig::create([ - 'field_name' => $field_name, - 'type' => 'entity_reference', - 'entity_type' => $entity_type, - 'settings' => [ - 'target_type' => 'user', - ], - ]); - $field_storage->save(); - $this->assertEqual(['module' => ['entity_test', 'user']], $field_storage->getDependencies()); - - $field = FieldConfig::create([ - 'field_name' => $field_name, - 'entity_type' => $entity_type, - 'bundle' => 'entity_test', - 'label' => $field_name, - 'settings' => [ - 'handler' => 'default', - ], - ]); - $field->save(); - $this->assertEqual(['config' => ['field.storage.entity_test.user_reference_field'], 'module' => ['entity_test']], $field->getDependencies()); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityReferenceSelection/EntityReferenceSelectionAccessTest.php b/core/modules/system/src/Tests/Entity/EntityReferenceSelection/EntityReferenceSelectionAccessTest.php index 46cc952..b3fe06f 100644 --- a/core/modules/system/src/Tests/Entity/EntityReferenceSelection/EntityReferenceSelectionAccessTest.php +++ b/core/modules/system/src/Tests/Entity/EntityReferenceSelection/EntityReferenceSelectionAccessTest.php @@ -11,8 +11,10 @@ use Drupal\Component\Utility\Html; use Drupal\Core\Language\LanguageInterface; use Drupal\comment\CommentInterface; +use Drupal\node\Entity\Node; use Drupal\simpletest\WebTestBase; use Drupal\user\Entity\User; +use Drupal\comment\Entity\Comment; /** * Tests for the base handlers provided by Entity Reference. @@ -107,7 +109,7 @@ public function testNodeHandler() { $nodes = array(); $node_labels = array(); foreach ($node_values as $key => $values) { - $node = entity_create('node', $values); + $node = Node::create($values); $node->save(); $nodes[$key] = $node; $node_labels[$key] = Html::escape($node->label()); @@ -234,7 +236,7 @@ public function testUserHandler() { $user_labels = array(); foreach ($user_values as $key => $values) { if (is_array($values)) { - $account = entity_create('user', $values); + $account = User::create($values); $account->save(); } else { @@ -366,7 +368,7 @@ public function testCommentHandler() { ); $nodes = array(); foreach ($node_values as $key => $values) { - $node = entity_create('node', $values); + $node = Node::create($values); $node->save(); $nodes[$key] = $node; } @@ -413,7 +415,7 @@ public function testCommentHandler() { $comments = array(); $comment_labels = array(); foreach ($comment_values as $key => $values) { - $comment = entity_create('comment', $values); + $comment = Comment::create($values); $comment->save(); $comments[$key] = $comment; $comment_labels[$key] = Html::escape($comment->label()); diff --git a/core/modules/system/src/Tests/Entity/EntityReferenceSelection/EntityReferenceSelectionSortTest.php b/core/modules/system/src/Tests/Entity/EntityReferenceSelection/EntityReferenceSelectionSortTest.php deleted file mode 100644 index 3d0d4d1..0000000 --- a/core/modules/system/src/Tests/Entity/EntityReferenceSelection/EntityReferenceSelectionSortTest.php +++ /dev/null @@ -1,136 +0,0 @@ - 'article', - )); - $article->save(); - - // Test as a non-admin. - $normal_user = $this->createUser(array(), array('access content')); - \Drupal::currentUser()->setAccount($normal_user); - } - - /** - * Assert sorting by field and property. - */ - public function testSort() { - // Add text field to entity, to sort by. - entity_create('field_storage_config', array( - 'field_name' => 'field_text', - 'entity_type' => 'node', - 'type' => 'text', - 'entity_types' => array('node'), - ))->save(); - - entity_create('field_config', array( - 'label' => 'Text Field', - 'field_name' => 'field_text', - 'entity_type' => 'node', - 'bundle' => 'article', - 'settings' => array(), - 'required' => FALSE, - ))->save(); - - // Build a set of test data. - $node_values = array( - 'published1' => array( - 'type' => 'article', - 'status' => 1, - 'title' => 'Node published1 (<&>)', - 'uid' => 1, - 'field_text' => array( - array( - 'value' => 1, - ), - ), - ), - 'published2' => array( - 'type' => 'article', - 'status' => 1, - 'title' => 'Node published2 (<&>)', - 'uid' => 1, - 'field_text' => array( - array( - 'value' => 2, - ), - ), - ), - ); - - $nodes = array(); - $node_labels = array(); - foreach ($node_values as $key => $values) { - $node = Node::create($values); - $node->save(); - $nodes[$key] = $node; - $node_labels[$key] = Html::escape($node->label()); - } - - $selection_options = array( - 'target_type' => 'node', - 'handler' => 'default', - 'handler_settings' => array( - 'target_bundles' => NULL, - // Add sorting. - 'sort' => array( - 'field' => 'field_text.value', - 'direction' => 'DESC', - ), - ), - ); - $handler = $this->container->get('plugin.manager.entity_reference_selection')->getInstance($selection_options); - - // Not only assert the result, but make sure the keys are sorted as - // expected. - $result = $handler->getReferenceableEntities(); - $expected_result = array( - $nodes['published2']->id() => $node_labels['published2'], - $nodes['published1']->id() => $node_labels['published1'], - ); - $this->assertIdentical($result['article'], $expected_result, 'Query sorted by field returned expected values.'); - - // Assert sort by base field. - $selection_options['handler_settings']['sort'] = array( - 'field' => 'nid', - 'direction' => 'ASC', - ); - $handler = $this->container->get('plugin.manager.entity_reference_selection')->getInstance($selection_options); - $result = $handler->getReferenceableEntities(); - $expected_result = array( - $nodes['published1']->id() => $node_labels['published1'], - $nodes['published2']->id() => $node_labels['published2'], - ); - $this->assertIdentical($result['article'], $expected_result, 'Query sorted by property returned expected values.'); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityRevisionTranslationTest.php b/core/modules/system/src/Tests/Entity/EntityRevisionTranslationTest.php deleted file mode 100644 index 0250d96..0000000 --- a/core/modules/system/src/Tests/Entity/EntityRevisionTranslationTest.php +++ /dev/null @@ -1,95 +0,0 @@ -save(); - - $this->installEntitySchema('entity_test_mulrev'); - } - - /** - * Tests if the translation object has the right revision id after new revision. - */ - public function testNewRevisionAfterTranslation() { - $user = $this->createUser(); - - // Create a test entity. - $entity = EntityTestMulRev::create([ - 'name' => $this->randomString(), - 'user_id' => $user->id(), - 'language' => 'en', - ]); - $entity->save(); - $old_rev_id = $entity->getRevisionId(); - - $translation = $entity->addTranslation('de'); - $translation->setNewRevision(); - $translation->save(); - - $this->assertTrue($translation->getRevisionId() > $old_rev_id, 'The saved translation in new revision has a newer revision id.'); - $this->assertTrue($this->reloadEntity($entity)->getRevisionId() > $old_rev_id, 'The entity from the storage has a newer revision id.'); - } - - /** - * Tests if the translation object has the right revision id after new revision. - */ - public function testRevertRevisionAfterTranslation() { - $user = $this->createUser(); - $storage = $this->entityManager->getStorage('entity_test_mulrev'); - - // Create a test entity. - $entity = EntityTestMulRev::create([ - 'name' => $this->randomString(), - 'user_id' => $user->id(), - 'language' => 'en', - ]); - $entity->save(); - $old_rev_id = $entity->getRevisionId(); - - $translation = $entity->addTranslation('de'); - $translation->setNewRevision(); - $translation->save(); - - $entity = $this->reloadEntity($entity); - - $this->assertTrue($entity->hasTranslation('de')); - - $entity = $storage->loadRevision($old_rev_id); - - $entity->setNewRevision(); - $entity->isDefaultRevision(TRUE); - $entity->save(); - - $entity = $this->reloadEntity($entity); - - $this->assertFalse($entity->hasTranslation('de')); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityRevisionsTest.php b/core/modules/system/src/Tests/Entity/EntityRevisionsTest.php index 39f04c3..057d23a 100644 --- a/core/modules/system/src/Tests/Entity/EntityRevisionsTest.php +++ b/core/modules/system/src/Tests/Entity/EntityRevisionsTest.php @@ -62,10 +62,12 @@ public function testRevisions() { protected function runRevisionsTests($entity_type) { // Create initial entity. - $entity = entity_create($entity_type, array( - 'name' => 'foo', - 'user_id' => $this->webUser->id(), - )); + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + 'name' => 'foo', + 'user_id' => $this->webUser->id(), + )); $entity->field_test_text->value = 'bar'; $entity->save(); diff --git a/core/modules/system/src/Tests/Entity/EntitySchemaTest.php b/core/modules/system/src/Tests/Entity/EntitySchemaTest.php deleted file mode 100644 index 08395ff..0000000 --- a/core/modules/system/src/Tests/Entity/EntitySchemaTest.php +++ /dev/null @@ -1,197 +0,0 @@ -installSchema('user', array('users_data')); - $this->installSchema('system', array('router')); - $this->database = $this->container->get('database'); - } - - /** - * Tests the custom bundle field creation and deletion. - */ - public function testCustomFieldCreateDelete() { - // Install the module which adds the field. - $this->installModule('entity_schema_test'); - $this->entityManager->clearCachedDefinitions(); - $storage_definitions = $this->entityManager->getFieldStorageDefinitions('entity_test'); - $this->assertNotNull($storage_definitions['custom_base_field'], 'Base field definition found.'); - $this->assertNotNull($storage_definitions['custom_bundle_field'], 'Bundle field definition found.'); - - // Make sure the field schema can be created. - $this->entityManager->onFieldStorageDefinitionCreate($storage_definitions['custom_base_field']); - $this->entityManager->onFieldStorageDefinitionCreate($storage_definitions['custom_bundle_field']); - /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ - $table_mapping = $this->entityManager->getStorage('entity_test')->getTableMapping(); - $base_table = current($table_mapping->getTableNames()); - $base_column = current($table_mapping->getColumnNames('custom_base_field')); - $this->assertTrue($this->database->schema()->fieldExists($base_table, $base_column), 'Table column created'); - $table = $table_mapping->getDedicatedDataTableName($storage_definitions['custom_bundle_field']); - $this->assertTrue($this->database->schema()->tableExists($table), 'Table created'); - - // Make sure the field schema can be deleted. - $this->entityManager->onFieldStorageDefinitionDelete($storage_definitions['custom_base_field']); - $this->entityManager->onFieldStorageDefinitionDelete($storage_definitions['custom_bundle_field']); - $this->assertFalse($this->database->schema()->fieldExists($base_table, $base_column), 'Table column dropped'); - $this->assertFalse($this->database->schema()->tableExists($table), 'Table dropped'); - } - - /** - * Updates the entity type definition. - * - * @param bool $alter - * Whether the original definition should be altered or not. - */ - protected function updateEntityType($alter) { - $entity_test_id = 'entity_test'; - $original = $this->entityManager->getDefinition($entity_test_id); - $this->entityManager->clearCachedDefinitions(); - $this->state->set('entity_schema_update', $alter); - $entity_type = $this->entityManager->getDefinition($entity_test_id); - $this->entityManager->onEntityTypeUpdate($entity_type, $original); - } - - /** - * Tests that entity schema responds to changes in the entity type definition. - */ - public function testEntitySchemaUpdate() { - $this->installModule('entity_schema_test'); - $storage_definitions = $this->entityManager->getFieldStorageDefinitions('entity_test'); - $this->entityManager->onFieldStorageDefinitionCreate($storage_definitions['custom_base_field']); - $this->entityManager->onFieldStorageDefinitionCreate($storage_definitions['custom_bundle_field']); - $schema_handler = $this->database->schema(); - $tables = array('entity_test', 'entity_test_revision', 'entity_test_field_data', 'entity_test_field_revision'); - $dedicated_tables = array('entity_test__custom_bundle_field', 'entity_test_revision__custom_bundle_field'); - - // Initially only the base table and the dedicated field data table should - // exist. - foreach ($tables as $index => $table) { - $this->assertEqual($schema_handler->tableExists($table), !$index, SafeMarkup::format('Entity schema correct for the @table table.', array('@table' => $table))); - } - $this->assertTrue($schema_handler->tableExists($dedicated_tables[0]), SafeMarkup::format('Field schema correct for the @table table.', array('@table' => $table))); - - // Update the entity type definition and check that the entity schema now - // supports translations and revisions. - $this->updateEntityType(TRUE); - foreach ($tables as $table) { - $this->assertTrue($schema_handler->tableExists($table), SafeMarkup::format('Entity schema correct for the @table table.', array('@table' => $table))); - } - foreach ($dedicated_tables as $table) { - $this->assertTrue($schema_handler->tableExists($table), SafeMarkup::format('Field schema correct for the @table table.', array('@table' => $table))); - } - - // Revert changes and check that the entity schema now does not support - // neither translations nor revisions. - $this->updateEntityType(FALSE); - foreach ($tables as $index => $table) { - $this->assertEqual($schema_handler->tableExists($table), !$index, SafeMarkup::format('Entity schema correct for the @table table.', array('@table' => $table))); - } - $this->assertTrue($schema_handler->tableExists($dedicated_tables[0]), SafeMarkup::format('Field schema correct for the @table table.', array('@table' => $table))); - } - - /** - * {@inheritdoc} - */ - protected function refreshServices() { - parent::refreshServices(); - $this->database = $this->container->get('database'); - } - - /** - * Tests that modifying the UUID field for a translatable entity works. - */ - public function testModifyingTranslatableColumnSchema() { - $this->installModule('entity_schema_test'); - $this->updateEntityType(TRUE); - $fields = ['revision_log', 'uuid']; - foreach ($fields as $field_name) { - $original_definition = $this->entityManager->getBaseFieldDefinitions('entity_test')[$field_name]; - $new_definition = clone $original_definition; - $new_definition->setLabel($original_definition->getLabel() . ', the other one'); - $this->assertTrue($this->entityManager->getStorage('entity_test') - ->requiresFieldDataMigration($new_definition, $original_definition)); - } - } - - /** - * Tests fields from an uninstalled module are removed from the schema. - */ - public function testCleanUpStorageDefinition() { - // Find all the entity types provided by the entity_test module and install - // the schema for them. - $entity_type_ids = []; - $entities = \Drupal::entityManager()->getDefinitions(); - foreach ($entities as $entity_type_id => $definition) { - if ($definition->getProvider() == 'entity_test') { - $this->installEntitySchema($entity_type_id); - $entity_type_ids[] = $entity_type_id; - }; - } - - // Get a list of all the entities in the schema. - $key_value_store = \Drupal::keyValue('entity.storage_schema.sql'); - $schema = $key_value_store->getAll(); - - // Count the storage definitions provided by the entity_test module, so that - // after uninstall we can be sure there were some to be deleted. - $entity_type_id_count = 0; - - foreach (array_keys($schema) as $storage_definition_name) { - list($entity_type_id, ,) = explode('.', $storage_definition_name); - if (in_array($entity_type_id, $entity_type_ids)) { - $entity_type_id_count++; - } - } - - // Ensure that there are storage definitions from the entity_test module. - $this->assertNotEqual($entity_type_id_count, 0, 'There are storage definitions provided by the entity_test module in the schema.'); - - // Uninstall the entity_test module. - $this->container->get('module_installer')->uninstall(array('entity_test')); - - // Get a list of all the entities in the schema. - $key_value_store = \Drupal::keyValue('entity.storage_schema.sql'); - $schema = $key_value_store->getAll(); - - // Count the storage definitions that come from entity types provided by - // the entity_test module. - $entity_type_id_count = 0; - - foreach (array_keys($schema) as $storage_definition_name) { - list($entity_type_id, ,) = explode('.', $storage_definition_name); - if (in_array($entity_type_id, $entity_type_ids)) { - $entity_type_id_count++; - } - } - - // Ensure that all storage definitions have been removed from the schema. - $this->assertEqual($entity_type_id_count, 0, 'After uninstalling entity_test module the schema should not contains fields from entities provided by the module.'); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php b/core/modules/system/src/Tests/Entity/EntityTranslationTest.php deleted file mode 100644 index de62ba7..0000000 --- a/core/modules/system/src/Tests/Entity/EntityTranslationTest.php +++ /dev/null @@ -1,925 +0,0 @@ -doTestEntityLanguageMethods($entity_type); - } - } - - /** - * Executes the entity language method tests for the given entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function doTestEntityLanguageMethods($entity_type) { - $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); - $entity = entity_create($entity_type, array( - 'name' => 'test', - 'user_id' => $this->container->get('current_user')->id(), - )); - $this->assertEqual($entity->language()->getId(), $this->languageManager->getDefaultLanguage()->getId(), format_string('%entity_type: Entity created with API has default language.', array('%entity_type' => $entity_type))); - $entity = entity_create($entity_type, array( - 'name' => 'test', - 'user_id' => \Drupal::currentUser()->id(), - $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED, - )); - $this->assertEqual($entity->language()->getId(), LanguageInterface::LANGCODE_NOT_SPECIFIED, format_string('%entity_type: Entity language not specified.', array('%entity_type' => $entity_type))); - $this->assertFalse($entity->getTranslationLanguages(FALSE), format_string('%entity_type: No translations are available', array('%entity_type' => $entity_type))); - - // Set the value in default language. - $entity->set($this->fieldName, array(0 => array('value' => 'default value'))); - // Get the value. - $field = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT)->get($this->fieldName); - $this->assertEqual($field->value, 'default value', format_string('%entity_type: Untranslated value retrieved.', array('%entity_type' => $entity_type))); - $this->assertEqual($field->getLangcode(), LanguageInterface::LANGCODE_NOT_SPECIFIED, format_string('%entity_type: Field object has the expected langcode.', array('%entity_type' => $entity_type))); - - // Try to get add a translation to language neutral entity. - $message = 'Adding a translation to a language-neutral entity results in an error.'; - try { - $entity->addTranslation($this->langcodes[1]); - $this->fail($message); - } - catch (\InvalidArgumentException $e) { - $this->pass($message); - } - - // Now, make the entity language-specific by assigning a language and test - // translating it. - $default_langcode = $this->langcodes[0]; - $entity->{$langcode_key}->value = $default_langcode; - $entity->{$this->fieldName} = array(); - $this->assertEqual($entity->language(), \Drupal::languageManager()->getLanguage($this->langcodes[0]), format_string('%entity_type: Entity language retrieved.', array('%entity_type' => $entity_type))); - $this->assertFalse($entity->getTranslationLanguages(FALSE), format_string('%entity_type: No translations are available', array('%entity_type' => $entity_type))); - - // Set the value in default language. - $entity->set($this->fieldName, array(0 => array('value' => 'default value'))); - // Get the value. - $field = $entity->get($this->fieldName); - $this->assertEqual($field->value, 'default value', format_string('%entity_type: Untranslated value retrieved.', array('%entity_type' => $entity_type))); - $this->assertEqual($field->getLangcode(), $default_langcode, format_string('%entity_type: Field object has the expected langcode.', array('%entity_type' => $entity_type))); - - // Set a translation. - $entity->addTranslation($this->langcodes[1])->set($this->fieldName, array(0 => array('value' => 'translation 1'))); - $field = $entity->getTranslation($this->langcodes[1])->{$this->fieldName}; - $this->assertEqual($field->value, 'translation 1', format_string('%entity_type: Translated value set.', array('%entity_type' => $entity_type))); - $this->assertEqual($field->getLangcode(), $this->langcodes[1], format_string('%entity_type: Field object has the expected langcode.', array('%entity_type' => $entity_type))); - - // Make sure the untranslated value stays. - $field = $entity->get($this->fieldName); - $this->assertEqual($field->value, 'default value', 'Untranslated value stays.'); - $this->assertEqual($field->getLangcode(), $default_langcode, 'Untranslated value has the expected langcode.'); - - $translations[$this->langcodes[1]] = \Drupal::languageManager()->getLanguage($this->langcodes[1]); - $this->assertEqual($entity->getTranslationLanguages(FALSE), $translations, 'Translations retrieved.'); - - // Try to get a value using a language code for a non-existing translation. - $message = 'Getting a non existing translation results in an error.'; - try { - $entity->getTranslation($this->langcodes[2])->get($this->fieldName)->value; - $this->fail($message); - } - catch (\InvalidArgumentException $e) { - $this->pass($message); - } - - // Try to get a not available translation. - $this->assertNull($entity->addTranslation($this->langcodes[2])->get($this->fieldName)->value, format_string('%entity_type: A translation that is not available is NULL.', array('%entity_type' => $entity_type))); - - // Try to get a value using an invalid language code. - $message = 'Getting an invalid translation results in an error.'; - try { - $entity->getTranslation('invalid')->get($this->fieldName)->value; - $this->fail($message); - } - catch (\InvalidArgumentException $e) { - $this->pass($message); - } - - // Try to set a value using an invalid language code. - try { - $entity->getTranslation('invalid')->set($this->fieldName, NULL); - $this->fail(format_string('%entity_type: Setting a translation for an invalid language throws an exception.', array('%entity_type' => $entity_type))); - } - catch (\InvalidArgumentException $e) { - $this->pass(format_string('%entity_type: Setting a translation for an invalid language throws an exception.', array('%entity_type' => $entity_type))); - } - - // Set the value in default language. - $field_name = 'field_test_text'; - $entity->getTranslation($this->langcodes[1])->set($field_name, array(0 => array('value' => 'default value2'))); - // Get the value. - $field = $entity->get($field_name); - $this->assertEqual($field->value, 'default value2', format_string('%entity_type: Untranslated value set into a translation in non-strict mode.', array('%entity_type' => $entity_type))); - $this->assertEqual($field->getLangcode(), $default_langcode, format_string('%entity_type: Field object has the expected langcode.', array('%entity_type' => $entity_type))); - } - - /** - * Tests multilingual properties. - */ - public function testMultilingualProperties() { - // Test all entity variations with data table support. - foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) { - $this->doTestMultilingualProperties($entity_type); - } - } - - /** - * Executes the multilingual property tests for the given entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function doTestMultilingualProperties($entity_type) { - $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); - $default_langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('default_langcode'); - $name = $this->randomMachineName(); - $uid = mt_rand(0, 127); - $langcode = $this->langcodes[0]; - - // Create a language neutral entity and check that properties are stored - // as language neutral. - $entity = entity_create($entity_type, array('name' => $name, 'user_id' => $uid, $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED)); - $entity->save(); - $entity = entity_load($entity_type, $entity->id()); - $default_langcode = $entity->language()->getId(); - $this->assertEqual($default_langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED, format_string('%entity_type: Entity created as language neutral.', array('%entity_type' => $entity_type))); - $field = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT)->get('name'); - $this->assertEqual($name, $field->value, format_string('%entity_type: The entity name has been correctly stored as language neutral.', array('%entity_type' => $entity_type))); - $this->assertEqual($default_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expect langcode.', array('%entity_type' => $entity_type))); - $this->assertEqual($uid, $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT)->get('user_id')->target_id, format_string('%entity_type: The entity author has been correctly stored as language neutral.', array('%entity_type' => $entity_type))); - - $translation = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT); - $field = $translation->get('name'); - $this->assertEqual($name, $field->value, format_string('%entity_type: The entity name defaults to neutral language.', array('%entity_type' => $entity_type))); - $this->assertEqual($default_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expect langcode.', array('%entity_type' => $entity_type))); - $this->assertEqual($uid, $translation->get('user_id')->target_id, format_string('%entity_type: The entity author defaults to neutral language.', array('%entity_type' => $entity_type))); - $field = $entity->get('name'); - $this->assertEqual($name, $field->value, format_string('%entity_type: The entity name can be retrieved without specifying a language.', array('%entity_type' => $entity_type))); - $this->assertEqual($default_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expect langcode.', array('%entity_type' => $entity_type))); - $this->assertEqual($uid, $entity->get('user_id')->target_id, format_string('%entity_type: The entity author can be retrieved without specifying a language.', array('%entity_type' => $entity_type))); - - // Create a language-aware entity and check that properties are stored - // as language-aware. - $entity = entity_create($entity_type, array('name' => $name, 'user_id' => $uid, $langcode_key => $langcode)); - $entity->save(); - $entity = entity_load($entity_type, $entity->id()); - $default_langcode = $entity->language()->getId(); - $this->assertEqual($default_langcode, $langcode, format_string('%entity_type: Entity created as language specific.', array('%entity_type' => $entity_type))); - $field = $entity->getTranslation($langcode)->get('name'); - $this->assertEqual($name, $field->value, format_string('%entity_type: The entity name has been correctly stored as a language-aware property.', array('%entity_type' => $entity_type))); - $this->assertEqual($default_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expect langcode.', array('%entity_type' => $entity_type))); - $this->assertEqual($uid, $entity->getTranslation($langcode)->get('user_id')->target_id, format_string('%entity_type: The entity author has been correctly stored as a language-aware property.', array('%entity_type' => $entity_type))); - - // Create property translations. - $properties = array(); - $default_langcode = $langcode; - foreach ($this->langcodes as $langcode) { - if ($langcode != $default_langcode) { - $properties[$langcode] = array( - 'name' => array(0 => $this->randomMachineName()), - 'user_id' => array(0 => mt_rand(128, 256)), - ); - } - else { - $properties[$langcode] = array( - 'name' => array(0 => $name), - 'user_id' => array(0 => $uid), - ); - } - $translation = $entity->hasTranslation($langcode) ? $entity->getTranslation($langcode) : $entity->addTranslation($langcode); - foreach ($properties[$langcode] as $field_name => $values) { - $translation->set($field_name, $values); - } - } - $entity->save(); - - // Check that property translation were correctly stored. - $entity = entity_load($entity_type, $entity->id()); - foreach ($this->langcodes as $langcode) { - $args = array( - '%entity_type' => $entity_type, - '%langcode' => $langcode, - ); - $field = $entity->getTranslation($langcode)->get('name'); - $this->assertEqual($properties[$langcode]['name'][0], $field->value, format_string('%entity_type: The entity name has been correctly stored for language %langcode.', $args)); - $field_langcode = ($langcode == $entity->language()->getId()) ? $default_langcode : $langcode; - $this->assertEqual($field_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expected langcode %langcode.', $args)); - $this->assertEqual($properties[$langcode]['user_id'][0], $entity->getTranslation($langcode)->get('user_id')->target_id, format_string('%entity_type: The entity author has been correctly stored for language %langcode.', $args)); - } - - // Test query conditions (cache is reset at each call). - $translated_id = $entity->id(); - // Create an additional entity with only the uid set. The uid for the - // original language is the same of one used for a translation. - $langcode = $this->langcodes[1]; - entity_create($entity_type, array( - 'user_id' => $properties[$langcode]['user_id'], - 'name' => 'some name', - $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED, - ))->save(); - - $entities = entity_load_multiple($entity_type); - $this->assertEqual(count($entities), 3, format_string('%entity_type: Three entities were created.', array('%entity_type' => $entity_type))); - $entities = entity_load_multiple($entity_type, array($translated_id)); - $this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by id.', array('%entity_type' => $entity_type))); - $entities = entity_load_multiple_by_properties($entity_type, array('name' => $name)); - $this->assertEqual(count($entities), 2, format_string('%entity_type: Two entities correctly loaded by name.', array('%entity_type' => $entity_type))); - // @todo The default language condition should go away in favor of an - // explicit parameter. - $entities = entity_load_multiple_by_properties($entity_type, array('name' => $properties[$langcode]['name'][0], $default_langcode_key => 0)); - $this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by name translation.', array('%entity_type' => $entity_type))); - $entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $default_langcode, 'name' => $name)); - $this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by name and language.', array('%entity_type' => $entity_type))); - - $entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0])); - $this->assertEqual(count($entities), 0, format_string('%entity_type: No entity loaded by name translation specifying the translation language.', array('%entity_type' => $entity_type))); - $entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0], $default_langcode_key => 0)); - $this->assertEqual(count($entities), 1, format_string('%entity_type: One entity loaded by name translation and language specifying to look for translations.', array('%entity_type' => $entity_type))); - $entities = entity_load_multiple_by_properties($entity_type, array('user_id' => $properties[$langcode]['user_id'][0], $default_langcode_key => NULL)); - $this->assertEqual(count($entities), 2, format_string('%entity_type: Two entities loaded by uid without caring about property translatability.', array('%entity_type' => $entity_type))); - - // Test property conditions and orders with multiple languages in the same - // query. - $query = \Drupal::entityQuery($entity_type); - $group = $query->andConditionGroup() - ->condition('user_id', $properties[$default_langcode]['user_id'][0], '=', $default_langcode) - ->condition('name', $properties[$default_langcode]['name'][0], '=', $default_langcode); - $result = $query - ->condition($group) - ->condition('name', $properties[$langcode]['name'][0], '=', $langcode) - ->execute(); - $this->assertEqual(count($result), 1, format_string('%entity_type: One entity loaded by name and uid using different language meta conditions.', array('%entity_type' => $entity_type))); - - // Test mixed property and field conditions. - $entity = entity_load($entity_type, reset($result), TRUE); - $field_value = $this->randomString(); - $entity->getTranslation($langcode)->set($this->fieldName, array(array('value' => $field_value))); - $entity->save(); - $query = \Drupal::entityQuery($entity_type); - $default_langcode_group = $query->andConditionGroup() - ->condition('user_id', $properties[$default_langcode]['user_id'][0], '=', $default_langcode) - ->condition('name', $properties[$default_langcode]['name'][0], '=', $default_langcode); - $langcode_group = $query->andConditionGroup() - ->condition('name', $properties[$langcode]['name'][0], '=', $langcode) - ->condition("$this->fieldName.value", $field_value, '=', $langcode); - $result = $query - ->condition($langcode_key, $default_langcode) - ->condition($default_langcode_group) - ->condition($langcode_group) - ->execute(); - $this->assertEqual(count($result), 1, format_string('%entity_type: One entity loaded by name, uid and field value using different language meta conditions.', array('%entity_type' => $entity_type))); - } - - /** - * Tests the Entity Translation API behavior. - */ - function testEntityTranslationAPI() { - // Test all entity variations with data table support. - foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) { - $this->doTestEntityTranslationAPI($entity_type); - } - } - - /** - * Executes the Entity Translation API tests for the given entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function doTestEntityTranslationAPI($entity_type) { - $default_langcode = $this->langcodes[0]; - $langcode = $this->langcodes[1]; - $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); - $default_langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('default_langcode'); - - /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ - $entity = $this->entityManager - ->getStorage($entity_type) - ->create(array('name' => $this->randomMachineName(), $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED)); - - $entity->save(); - $hooks = $this->getHooksInfo(); - $this->assertFalse($hooks, 'No entity translation hooks are fired when creating an entity.'); - - // Verify that we obtain the entity object itself when we attempt to - // retrieve a translation referring to it. - $translation = $entity->getTranslation(LanguageInterface::LANGCODE_NOT_SPECIFIED); - $this->assertFalse($translation->isNewTranslation(), 'Existing translations are not marked as new.'); - $this->assertIdentical($entity, $translation, 'The translation object corresponding to a non-default language is the entity object itself when the entity is language-neutral.'); - $entity->{$langcode_key}->value = $default_langcode; - $translation = $entity->getTranslation($default_langcode); - $this->assertIdentical($entity, $translation, 'The translation object corresponding to the default language (explicit) is the entity object itself.'); - $translation = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT); - $this->assertIdentical($entity, $translation, 'The translation object corresponding to the default language (implicit) is the entity object itself.'); - $this->assertTrue($entity->{$default_langcode_key}->value, 'The translation object is the default one.'); - - // Verify that trying to retrieve a translation for a locked language when - // the entity is language-aware causes an exception to be thrown. - $message = 'A language-neutral translation cannot be retrieved.'; - try { - $entity->getTranslation(LanguageInterface::LANGCODE_NOT_SPECIFIED); - $this->fail($message); - } - catch (\LogicException $e) { - $this->pass($message); - } - - // Create a translation and verify that the translation object and the - // original object behave independently. - $name = $default_langcode . '_' . $this->randomMachineName(); - $entity->name->value = $name; - $name_translated = $langcode . '_' . $this->randomMachineName(); - $translation = $entity->addTranslation($langcode); - $this->assertTrue($translation->isNewTranslation(), 'Newly added translations are marked as new.'); - $this->assertNotIdentical($entity, $translation, 'The entity and the translation object differ from one another.'); - $this->assertTrue($entity->hasTranslation($langcode), 'The new translation exists.'); - $this->assertEqual($translation->language()->getId(), $langcode, 'The translation language matches the specified one.'); - $this->assertEqual($translation->{$langcode_key}->value, $langcode, 'The translation field language value matches the specified one.'); - $this->assertFalse($translation->{$default_langcode_key}->value, 'The translation object is not the default one.'); - $this->assertEqual($translation->getUntranslated()->language()->getId(), $default_langcode, 'The original language can still be retrieved.'); - $translation->name->value = $name_translated; - $this->assertEqual($entity->name->value, $name, 'The original name is retained after setting a translated value.'); - $entity->name->value = $name; - $this->assertEqual($translation->name->value, $name_translated, 'The translated name is retained after setting the original value.'); - - // Save the translation and check that the expected hooks are fired. - $translation->save(); - $hooks = $this->getHooksInfo(); - - $this->assertEqual($hooks['entity_translation_create'], $langcode, 'The generic entity translation creation hook has fired.'); - $this->assertEqual($hooks[$entity_type . '_translation_create'], $langcode, 'The entity-type-specific entity translation creation hook has fired.'); - - $this->assertEqual($hooks['entity_translation_insert'], $langcode, 'The generic entity translation insertion hook has fired.'); - $this->assertEqual($hooks[$entity_type . '_translation_insert'], $langcode, 'The entity-type-specific entity translation insertion hook has fired.'); - - // Verify that changing translation language causes an exception to be - // thrown. - $message = 'The translation language cannot be changed.'; - try { - $translation->{$langcode_key}->value = $this->langcodes[2]; - $this->fail($message); - } - catch (\LogicException $e) { - $this->pass($message); - } - - // Verify that reassigning the same translation language is allowed. - $message = 'The translation language can be reassigned the same value.'; - try { - $translation->{$langcode_key}->value = $langcode; - $this->pass($message); - } - catch (\LogicException $e) { - $this->fail($message); - } - - // Verify that changing the default translation flag causes an exception to - // be thrown. - foreach ($entity->getTranslationLanguages() as $t_langcode => $language) { - $translation = $entity->getTranslation($t_langcode); - $default = $translation->isDefaultTranslation(); - - $message = 'The default translation flag can be reassigned the same value.'; - try { - $translation->{$default_langcode_key}->value = $default; - $this->pass($message); - } - catch (\LogicException $e) { - $this->fail($message); - } - - $message = 'The default translation flag cannot be changed.'; - try { - $translation->{$default_langcode_key}->value = !$default; - $this->fail($message); - } - catch (\LogicException $e) { - $this->pass($message); - } - - $this->assertEqual($translation->{$default_langcode_key}->value, $default); - } - - // Check that after loading an entity the language is the default one. - $entity = $this->reloadEntity($entity); - $this->assertEqual($entity->language()->getId(), $default_langcode, 'The loaded entity is the original one.'); - - // Add another translation and check that everything works as expected. A - // new translation object can be obtained also by just specifying a valid - // language. - $langcode2 = $this->langcodes[2]; - $translation = $entity->addTranslation($langcode2); - $value = $entity !== $translation && $translation->language()->getId() == $langcode2 && $entity->hasTranslation($langcode2); - $this->assertTrue($value, 'A new translation object can be obtained also by specifying a valid language.'); - $this->assertEqual($entity->language()->getId(), $default_langcode, 'The original language has been preserved.'); - $translation->save(); - $hooks = $this->getHooksInfo(); - - $this->assertEqual($hooks['entity_translation_create'], $langcode2, 'The generic entity translation creation hook has fired.'); - $this->assertEqual($hooks[$entity_type . '_translation_create'], $langcode2, 'The entity-type-specific entity translation creation hook has fired.'); - - $this->assertEqual($hooks['entity_translation_insert'], $langcode2, 'The generic entity translation insertion hook has fired.'); - $this->assertEqual($hooks[$entity_type . '_translation_insert'], $langcode2, 'The entity-type-specific entity translation insertion hook has fired.'); - - // Verify that trying to manipulate a translation object referring to a - // removed translation results in exceptions being thrown. - $entity = $this->reloadEntity($entity); - $translation = $entity->getTranslation($langcode2); - $entity->removeTranslation($langcode2); - foreach (array('get', 'set', '__get', '__set', 'createDuplicate') as $method) { - $message = format_string('The @method method raises an exception when trying to manipulate a removed translation.', array('@method' => $method)); - try { - $translation->{$method}('name', $this->randomMachineName()); - $this->fail($message); - } - catch (\Exception $e) { - $this->pass($message); - } - } - - // Verify that deletion hooks are fired when saving an entity with a removed - // translation. - $entity->save(); - $hooks = $this->getHooksInfo(); - $this->assertEqual($hooks['entity_translation_delete'], $langcode2, 'The generic entity translation deletion hook has fired.'); - $this->assertEqual($hooks[$entity_type . '_translation_delete'], $langcode2, 'The entity-type-specific entity translation deletion hook has fired.'); - $entity = $this->reloadEntity($entity); - $this->assertFalse($entity->hasTranslation($langcode2), 'The translation does not appear among available translations after saving the entity.'); - - // Check that removing an invalid translation causes an exception to be - // thrown. - foreach (array($default_langcode, LanguageInterface::LANGCODE_DEFAULT, $this->randomMachineName()) as $invalid_langcode) { - $message = format_string('Removing an invalid translation (@langcode) causes an exception to be thrown.', array('@langcode' => $invalid_langcode)); - try { - $entity->removeTranslation($invalid_langcode); - $this->fail($message); - } - catch (\Exception $e) { - $this->pass($message); - } - } - - // Check that hooks are fired only when actually storing data. - $entity = $this->reloadEntity($entity); - $entity->addTranslation($langcode2); - $entity->removeTranslation($langcode2); - $entity->save(); - $hooks = $this->getHooksInfo(); - - $this->assertTrue(isset($hooks['entity_translation_create']), 'The generic entity translation creation hook is run when adding and removing a translation without storing it.'); - unset($hooks['entity_translation_create']); - $this->assertTrue(isset($hooks[$entity_type . '_translation_create']), 'The entity-type-specific entity translation creation hook is run when adding and removing a translation without storing it.'); - unset($hooks[$entity_type . '_translation_create']); - - $this->assertFalse($hooks, 'No other hooks beyond the entity translation creation hooks are run when adding and removing a translation without storing it.'); - - // Check that hooks are fired only when actually storing data. - $entity = $this->reloadEntity($entity); - $entity->addTranslation($langcode2); - $entity->save(); - $entity = $this->reloadEntity($entity); - $this->assertTrue($entity->hasTranslation($langcode2), 'Entity has translation after adding one and saving.'); - $entity->removeTranslation($langcode2); - $entity->save(); - $entity = $this->reloadEntity($entity); - $this->assertFalse($entity->hasTranslation($langcode2), 'Entity does not have translation after removing it and saving.'); - // Reset hook firing information. - $this->getHooksInfo(); - - // Verify that entity serialization does not cause stale references to be - // left around. - $entity = $this->reloadEntity($entity); - $translation = $entity->getTranslation($langcode); - $entity = unserialize(serialize($entity)); - $entity->name->value = $this->randomMachineName(); - $name = $default_langcode . '_' . $this->randomMachineName(); - $entity->getTranslation($default_langcode)->name->value = $name; - $this->assertEqual($entity->name->value, $name, 'No stale reference for the translation object corresponding to the original language.'); - $translation2 = $entity->getTranslation($langcode); - $translation2->name->value .= $this->randomMachineName(); - $this->assertNotEqual($translation->name->value, $translation2->name->value, 'No stale reference for the actual translation object.'); - $this->assertEqual($entity, $translation2->getUntranslated(), 'No stale reference in the actual translation object.'); - - // Verify that deep-cloning is still available when we are not instantiating - // a translation object, which instead relies on shallow cloning. - $entity = $this->reloadEntity($entity); - $entity->getTranslation($langcode); - $cloned = clone $entity; - $translation = $cloned->getTranslation($langcode); - $this->assertNotIdentical($entity, $translation->getUntranslated(), 'A cloned entity object has no reference to the original one.'); - $entity->removeTranslation($langcode); - $this->assertFalse($entity->hasTranslation($langcode)); - $this->assertTrue($cloned->hasTranslation($langcode)); - - // Check that untranslatable field references keep working after serializing - // and cloning the entity. - $entity = $this->reloadEntity($entity); - $type = $this->randomMachineName(); - $entity->getTranslation($langcode)->type->value = $type; - $entity = unserialize(serialize($entity)); - $cloned = clone $entity; - $translation = $cloned->getTranslation($langcode); - $translation->type->value = strrev($type); - $this->assertEqual($cloned->type->value, $translation->type->value, 'Untranslatable field references keep working after serializing and cloning the entity.'); - - // Check that per-language defaults are properly populated. The - // 'entity_test_mul_default_value' entity type is translatable and uses - // entity_test_field_default_value() as a "default value callback" for its - // 'description' field. - $entity = $this->entityManager - ->getStorage('entity_test_mul_default_value') - ->create(['name' => $this->randomMachineName(), 'langcode' => $langcode]); - $translation = $entity->addTranslation($langcode2); - $expected = array( - array( - 'shape' => "shape:0:description_$langcode2", - 'color' => "color:0:description_$langcode2", - ), - array( - 'shape' => "shape:1:description_$langcode2", - 'color' => "color:1:description_$langcode2", - ), - ); - $this->assertEqual($translation->description->getValue(), $expected, 'Language-aware default values correctly populated.'); - $this->assertEqual($translation->description->getLangcode(), $langcode2, 'Field object has the expected langcode.'); - - // Reset hook firing information. - $this->getHooksInfo(); - } - - /** - * Tests language fallback applied to field and entity translations. - */ - function testLanguageFallback() { - // Test all entity variations with data table support. - foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) { - $this->doTestLanguageFallback($entity_type); - } - } - - /** - * Executes the language fallback test for the given entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function doTestLanguageFallback($entity_type) { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = $this->container->get('renderer'); - - $current_langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); - $this->langcodes[] = $current_langcode; - - $values = array(); - foreach ($this->langcodes as $langcode) { - $values[$langcode]['name'] = $this->randomMachineName(); - $values[$langcode]['user_id'] = mt_rand(0, 127); - } - - $default_langcode = $this->langcodes[0]; - $langcode = $this->langcodes[1]; - $langcode2 = $this->langcodes[2]; - $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); - $languages = $this->languageManager->getLanguages(); - $language = ConfigurableLanguage::load($languages[$langcode]->getId()); - $language->set('weight', 1); - $language->save(); - $this->languageManager->reset(); - - $controller = $this->entityManager->getStorage($entity_type); - $entity = $controller->create(array($langcode_key => $default_langcode) + $values[$default_langcode]); - $entity->save(); - - $entity->addTranslation($langcode, $values[$langcode]); - $entity->save(); - - // Check that retrieving the current translation works as expected. - $entity = $this->reloadEntity($entity); - $translation = $this->entityManager->getTranslationFromContext($entity, $langcode2); - $this->assertEqual($translation->language()->getId(), $default_langcode, 'The current translation language matches the expected one.'); - - // Check that language fallback respects language weight by default. - $language = ConfigurableLanguage::load($languages[$langcode]->getId()); - $language->set('weight', -1); - $language->save(); - $translation = $this->entityManager->getTranslationFromContext($entity, $langcode2); - $this->assertEqual($translation->language()->getId(), $langcode, 'The current translation language matches the expected one.'); - - // Check that the current translation is properly returned. - $translation = $this->entityManager->getTranslationFromContext($entity); - $this->assertEqual($langcode, $translation->language()->getId(), 'The current translation language matches the topmost language fallback candidate.'); - $entity->addTranslation($current_langcode, $values[$current_langcode]); - $translation = $this->entityManager->getTranslationFromContext($entity); - $this->assertEqual($current_langcode, $translation->language()->getId(), 'The current translation language matches the current language.'); - - // Check that if the entity has no translation no fallback is applied. - $entity2 = $controller->create(array($langcode_key => $default_langcode)); - // Get an view builder. - $controller = $this->entityManager->getViewBuilder($entity_type); - $entity2_build = $controller->view($entity2); - $entity2_output = (string) $renderer->renderRoot($entity2_build); - $translation = $this->entityManager->getTranslationFromContext($entity2, $default_langcode); - $translation_build = $controller->view($translation); - $translation_output = (string) $renderer->renderRoot($translation_build); - $this->assertIdentical($entity2_output, $translation_output, 'When the entity has no translation no fallback is applied.'); - - // Checks that entity translations are rendered properly. - $controller = $this->entityManager->getViewBuilder($entity_type); - $build = $controller->view($entity); - $renderer->renderRoot($build); - $this->assertEqual($build['label']['#markup'], $values[$current_langcode]['name'], 'By default the entity is rendered in the current language.'); - - $langcodes = array_combine($this->langcodes, $this->langcodes); - // We have no translation for the $langcode2 language, hence the expected - // result is the topmost existing translation, that is $langcode. - $langcodes[$langcode2] = $langcode; - foreach ($langcodes as $desired => $expected) { - $build = $controller->view($entity, 'full', $desired); - // Unset the #cache key so that a fresh render is produced with each pass, - // making the renderable array keys available to compare. - unset($build['#cache']); - $renderer->renderRoot($build); - $this->assertEqual($build['label']['#markup'], $values[$expected]['name'], 'The entity is rendered in the expected language.'); - } - } - - /** - * Check that field translatability is handled properly. - */ - function testFieldDefinitions() { - // Check that field translatability can be altered to be enabled or disabled - // in field definitions. - $entity_type = 'entity_test_mulrev'; - $this->state->set('entity_test.field_definitions.translatable', array('name' => FALSE)); - $this->entityManager->clearCachedFieldDefinitions(); - $definitions = $this->entityManager->getBaseFieldDefinitions($entity_type); - $this->assertFalse($definitions['name']->isTranslatable(), 'Field translatability can be disabled programmatically.'); - - $this->state->set('entity_test.field_definitions.translatable', array('name' => TRUE)); - $this->entityManager->clearCachedFieldDefinitions(); - $definitions = $this->entityManager->getBaseFieldDefinitions($entity_type); - $this->assertTrue($definitions['name']->isTranslatable(), 'Field translatability can be enabled programmatically.'); - - // Check that field translatability is disabled by default. - $base_field_definitions = EntityTestMulRev::baseFieldDefinitions($this->entityManager->getDefinition($entity_type)); - $this->assertTrue(!isset($base_field_definitions['id']->translatable), 'Translatability for the id field is not defined.'); - $this->assertFalse($definitions['id']->isTranslatable(), 'Field translatability is disabled by default.'); - - // Check that entity id keys have the expect translatability. - $translatable_fields = array( - 'id' => TRUE, - 'uuid' => TRUE, - 'revision_id' => TRUE, - 'type' => TRUE, - 'langcode' => FALSE, - ); - foreach ($translatable_fields as $name => $translatable) { - $this->state->set('entity_test.field_definitions.translatable', array($name => $translatable)); - $this->entityManager->clearCachedFieldDefinitions(); - $message = format_string('Field %field cannot be translatable.', array('%field' => $name)); - - try { - $this->entityManager->getBaseFieldDefinitions($entity_type); - $this->fail($message); - } - catch (\LogicException $e) { - $this->pass($message); - } - } - } - - /** - * Tests that changing entity language does not break field language. - */ - public function testLanguageChange() { - // Test all entity variations with data table support. - foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) { - $this->doTestLanguageChange($entity_type); - } - } - - /** - * Executes the entity language change test for the given entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function doTestLanguageChange($entity_type) { - $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); - $controller = $this->entityManager->getStorage($entity_type); - $langcode = $this->langcodes[0]; - - // check that field languages match entity language regardless of field - // translatability. - $values = array( - $langcode_key => $langcode, - $this->fieldName => $this->randomMachineName(), - $this->untranslatableFieldName => $this->randomMachineName(), - ); - $entity = $controller->create($values); - foreach (array($this->fieldName, $this->untranslatableFieldName) as $field_name) { - $this->assertEqual($entity->get($field_name)->getLangcode(), $langcode, 'Field language works as expected.'); - } - - // Check that field languages keep matching entity language even after - // changing it. - $langcode = $this->langcodes[1]; - $entity->{$langcode_key}->value = $langcode; - foreach (array($this->fieldName, $this->untranslatableFieldName) as $field_name) { - $this->assertEqual($entity->get($field_name)->getLangcode(), $langcode, 'Field language works as expected after changing entity language.'); - } - - // Check that entity translation does not affect the language of original - // field values and untranslatable ones. - $langcode = $this->langcodes[0]; - $entity->addTranslation($this->langcodes[2], array($this->fieldName => $this->randomMachineName())); - $entity->{$langcode_key}->value = $langcode; - foreach (array($this->fieldName, $this->untranslatableFieldName) as $field_name) { - $this->assertEqual($entity->get($field_name)->getLangcode(), $langcode, 'Field language works as expected after translating the entity and changing language.'); - } - - // Check that setting the default language to an existing translation - // language causes an exception to be thrown. - $message = 'An exception is thrown when setting the default language to an existing translation language'; - try { - $entity->{$langcode_key}->value = $this->langcodes[2]; - $this->fail($message); - } - catch (\InvalidArgumentException $e) { - $this->pass($message); - } - } - - /** - * Tests how entity adapters work with translations. - */ - function testEntityAdapter() { - $entity_type = 'entity_test'; - $default_langcode = 'en'; - $values[$default_langcode] = array('name' => $this->randomString()); - $controller = $this->entityManager->getStorage($entity_type); - /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ - $entity = $controller->create($values[$default_langcode]); - - foreach ($this->langcodes as $langcode) { - $values[$langcode] = array('name' => $this->randomString()); - $entity->addTranslation($langcode, $values[$langcode]); - } - - $langcodes = array_merge(array($default_langcode), $this->langcodes); - foreach ($langcodes as $langcode) { - $adapter = $entity->getTranslation($langcode)->getTypedData(); - $name = $adapter->get('name')->value; - $this->assertEqual($name, $values[$langcode]['name'], SafeMarkup::format('Name correctly retrieved from "@langcode" adapter', array('@langcode' => $langcode))); - } - } - - /** - * Tests if entity references are correct after adding a new translation. - */ - public function testFieldEntityReference() { - $entity_type = 'entity_test_mul'; - $controller = $this->entityManager->getStorage($entity_type); - /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ - $entity = $controller->create(); - - foreach ($this->langcodes as $langcode) { - $entity->addTranslation($langcode); - } - - $default_langcode = $entity->getUntranslated()->language()->getId(); - foreach (array_keys($entity->getTranslationLanguages()) as $langcode) { - $translation = $entity->getTranslation($langcode); - foreach ($translation->getFields() as $field_name => $field) { - if ($field->getFieldDefinition()->isTranslatable()) { - $args = ['%field_name' => $field_name, '%langcode' => $langcode]; - $this->assertEqual($langcode, $field->getEntity()->language()->getId(), format_string('Translatable field %field_name on translation %langcode has correct entity reference in translation %langcode.', $args)); - } - else { - $args = ['%field_name' => $field_name, '%langcode' => $langcode, '%default_langcode' => $default_langcode]; - $this->assertEqual($default_langcode, $field->getEntity()->language()->getId(), format_string('Non translatable field %field_name on translation %langcode has correct entity reference in the default translation %default_langcode.', $args)); - } - } - } - } - - /** - * Tests if entity translation statuses are correct after removing two - * translation. - */ - public function testDeleteEntityTranslation() { - $entity_type = 'entity_test_mul'; - $controller = $this->entityManager->getStorage($entity_type); - - // Create a translatable test field. - $field_storage = FieldStorageConfig::create([ - 'entity_type' => $entity_type, - 'field_name' => 'translatable_test_field', - 'type' => 'field_test', - ]); - $field_storage->save(); - - $field = FieldConfig::create([ - 'field_storage' => $field_storage, - 'label' => $this->randomMachineName(), - 'bundle' => $entity_type, - ]); - $field->save(); - - // Create an untranslatable test field. - $field_storage = FieldStorageConfig::create([ - 'entity_type' => $entity_type, - 'field_name' => 'untranslatable_test_field', - 'type' => 'field_test', - 'translatable' => FALSE, - ]); - $field_storage->save(); - - $field = FieldConfig::create([ - 'field_storage' => $field_storage, - 'label' => $this->randomMachineName(), - 'bundle' => $entity_type, - ]); - $field->save(); - - // Create an entity with both translatable and untranslatable test fields. - $values = array( - 'name' => $this->randomString(), - 'translatable_test_field' => $this->randomString(), - 'untranslatable_test_field' => $this->randomString(), - ); - - /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ - $entity = $controller->create($values); - - foreach ($this->langcodes as $langcode) { - $entity->addTranslation($langcode, $values); - } - $entity->save(); - - // Assert there are no deleted languages in the lists yet. - $this->assertNull(\Drupal::state()->get('entity_test.delete.translatable_test_field')); - $this->assertNull(\Drupal::state()->get('entity_test.delete.untranslatable_test_field')); - - // Remove the second and third langcodes from the entity. - $entity->removeTranslation('l1'); - $entity->removeTranslation('l2'); - $entity->save(); - - // Ensure that for the translatable test field the second and third - // langcodes are in the deleted languages list. - $actual = \Drupal::state()->get('entity_test.delete.translatable_test_field'); - $expected_translatable = ['l1', 'l2']; - sort($actual); - sort($expected_translatable); - $this->assertEqual($actual, $expected_translatable); - // Ensure that the untranslatable test field is untouched. - $this->assertNull(\Drupal::state()->get('entity_test.delete.untranslatable_test_field')); - - // Delete the entity, which removes all remaining translations. - $entity->delete(); - - // All languages have been deleted now. - $actual = \Drupal::state()->get('entity_test.delete.translatable_test_field'); - $expected_translatable[] = 'en'; - $expected_translatable[] = 'l0'; - sort($actual); - sort($expected_translatable); - $this->assertEqual($actual, $expected_translatable); - - // The untranslatable field is shared and only deleted once, for the - // default langcode. - $actual = \Drupal::state()->get('entity_test.delete.untranslatable_test_field'); - $expected_untranslatable = ['en']; - sort($actual); - sort($expected_untranslatable); - $this->assertEqual($actual, $expected_untranslatable); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityTypeConstraintValidatorTest.php b/core/modules/system/src/Tests/Entity/EntityTypeConstraintValidatorTest.php deleted file mode 100644 index b9ebee2..0000000 --- a/core/modules/system/src/Tests/Entity/EntityTypeConstraintValidatorTest.php +++ /dev/null @@ -1,66 +0,0 @@ -typedData = $this->container->get('typed_data_manager'); - } - - /** - * Tests the EntityTypeConstraintValidator. - */ - public function testValidation() { - // Create a typed data definition with an EntityType constraint. - $entity_type = 'node'; - $definition = DataDefinition::create('entity_reference') - ->setConstraints(array( - 'EntityType' => $entity_type, - ) - ); - - // Test the validation. - $node = $this->container->get('entity.manager')->getStorage('node')->create(array('type' => 'page')); - $typed_data = $this->typedData->create($definition, $node); - $violations = $typed_data->validate(); - $this->assertEqual($violations->count(), 0, 'Validation passed for correct value.'); - - // Test the validation when an invalid value (in this case a user entity) - // is passed. - $account = $this->createUser(); - - $typed_data = $this->typedData->create($definition, $account); - $violations = $typed_data->validate(); - $this->assertEqual($violations->count(), 1, 'Validation failed for incorrect value.'); - - // Make sure the information provided by a violation is correct. - $violation = $violations[0]; - $this->assertEqual($violation->getMessage(), t('The entity must be of type %type.', array('%type' => $entity_type)), 'The message for invalid value is correct.'); - $this->assertEqual($violation->getRoot(), $typed_data, 'Violation root is correct.'); - $this->assertEqual($violation->getInvalidValue(), $account, 'The invalid value is set correctly in the violation.'); - } -} diff --git a/core/modules/system/src/Tests/Entity/EntityTypeConstraintsTest.php b/core/modules/system/src/Tests/Entity/EntityTypeConstraintsTest.php deleted file mode 100644 index 688b96c..0000000 --- a/core/modules/system/src/Tests/Entity/EntityTypeConstraintsTest.php +++ /dev/null @@ -1,76 +0,0 @@ -installEntitySchema('entity_test_constraints'); - } - - /** - * Tests defining entity constraints via entity type annotations and hooks. - */ - public function testConstraintDefinition() { - // Test reading the annotation. There should be two constraints, the defined - // constraint and the automatically added EntityChanged constraint. - $entity_type = $this->entityManager->getDefinition('entity_test_constraints'); - $default_constraints = ['NotNull' => [], 'EntityChanged' => NULL]; - $this->assertEqual($default_constraints, $entity_type->getConstraints()); - - // Enable our test module and test extending constraints. - $this->enableModules(array_merge(static::$modules, ['entity_test_constraints'])); - $this->container->get('module_handler')->resetImplementations(); - - $extra_constraints = ['Test' => []]; - $this->state->set('entity_test_constraints.build', $extra_constraints); - // Re-fetch the entity manager from the new container built after the new - // modules were enabled. - $this->entityManager = $this->container->get('entity.manager'); - $this->entityManager->clearCachedDefinitions(); - $entity_type = $this->entityManager->getDefinition('entity_test_constraints'); - $this->assertEqual($default_constraints + $extra_constraints, $entity_type->getConstraints()); - - // Test altering constraints. - $altered_constraints = ['Test' => [ 'some_setting' => TRUE]]; - $this->state->set('entity_test_constraints.alter', $altered_constraints); - // Clear the cache in state instance in the Drupal container, so it can pick - // up the modified value. - \Drupal::state()->resetCache(); - $this->entityManager->clearCachedDefinitions(); - $entity_type = $this->entityManager->getDefinition('entity_test_constraints'); - $this->assertEqual($altered_constraints, $entity_type->getConstraints()); - } - - /** - * Tests entity constraints are validated. - */ - public function testConstraintValidation() { - $entity = $this->entityManager->getStorage('entity_test_constraints')->create(); - $entity->user_id->target_id = 0; - $violations = $entity->validate(); - $this->assertEqual($violations->count(), 0, 'Validation passed.'); - $entity->save(); - $entity->changed->value = REQUEST_TIME - 86400; - $violations = $entity->validate(); - $this->assertEqual($violations->count(), 1, 'Validation failed.'); - $this->assertEqual($violations[0]->getMessage(), t('The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.')); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityTypedDataDefinitionTest.php b/core/modules/system/src/Tests/Entity/EntityTypedDataDefinitionTest.php deleted file mode 100644 index d71ca15..0000000 --- a/core/modules/system/src/Tests/Entity/EntityTypedDataDefinitionTest.php +++ /dev/null @@ -1,134 +0,0 @@ -typedDataManager = $this->container->get('typed_data_manager'); - } - - /** - * Tests deriving metadata about fields. - */ - public function testFields() { - $field_definition = BaseFieldDefinition::create('integer'); - // Fields are lists of complex data. - $this->assertTrue($field_definition instanceof ListDataDefinitionInterface); - $this->assertFalse($field_definition instanceof ComplexDataDefinitionInterface); - $field_item_definition = $field_definition->getItemDefinition(); - $this->assertFalse($field_item_definition instanceof ListDataDefinitionInterface); - $this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface); - - // Derive metadata about field item properties. - $this->assertEqual(array_keys($field_item_definition->getPropertyDefinitions()), array('value')); - $this->assertEqual($field_item_definition->getPropertyDefinition('value')->getDataType(), 'integer'); - $this->assertEqual($field_item_definition->getMainPropertyName(), 'value'); - $this->assertNull($field_item_definition->getPropertyDefinition('invalid')); - - // Test accessing field item property metadata via the field definition. - $this->assertTrue($field_definition instanceof FieldDefinitionInterface); - $this->assertEqual(array_keys($field_definition->getPropertyDefinitions()), array('value')); - $this->assertEqual($field_definition->getPropertyDefinition('value')->getDataType(), 'integer'); - $this->assertEqual($field_definition->getMainPropertyName(), 'value'); - $this->assertNull($field_definition->getPropertyDefinition('invalid')); - - // Test using the definition factory for field item lists and field items. - $field_item = $this->typedDataManager->createDataDefinition('field_item:integer'); - $this->assertFalse($field_item instanceof ListDataDefinitionInterface); - $this->assertTrue($field_item instanceof ComplexDataDefinitionInterface); - // Comparison should ignore the internal static cache, so compare the - // serialized objects instead. - $this->assertEqual(serialize($field_item_definition), serialize($field_item)); - - $field_definition2 = $this->typedDataManager->createListDataDefinition('field_item:integer'); - $this->assertTrue($field_definition2 instanceof ListDataDefinitionInterface); - $this->assertFalse($field_definition2 instanceof ComplexDataDefinitionInterface); - $this->assertEqual(serialize($field_definition), serialize($field_definition2)); - } - - /** - * Tests deriving metadata about entities. - */ - public function testEntities() { - $entity_definition = EntityDataDefinition::create('node'); - // Entities are complex data. - $this->assertFalse($entity_definition instanceof ListDataDefinitionInterface); - $this->assertTrue($entity_definition instanceof ComplexDataDefinitionInterface); - - $field_definitions = $entity_definition->getPropertyDefinitions(); - // Comparison should ignore the internal static cache, so compare the - // serialized objects instead. - $this->assertEqual(serialize($field_definitions), serialize(\Drupal::entityManager()->getBaseFieldDefinitions('node'))); - $this->assertEqual($entity_definition->getPropertyDefinition('title')->getItemDefinition()->getDataType(), 'field_item:string'); - $this->assertNull($entity_definition->getMainPropertyName()); - $this->assertNull($entity_definition->getPropertyDefinition('invalid')); - - $entity_definition2 = $this->typedDataManager->createDataDefinition('entity:node'); - $this->assertFalse($entity_definition2 instanceof ListDataDefinitionInterface); - $this->assertTrue($entity_definition2 instanceof ComplexDataDefinitionInterface); - $this->assertEqual(serialize($entity_definition), serialize($entity_definition2)); - - // Test that the definition factory creates the right definitions for all - // entity data types variants. - $this->assertEqual($this->typedDataManager->createDataDefinition('entity'), EntityDataDefinition::create()); - $this->assertEqual($this->typedDataManager->createDataDefinition('entity:node'), EntityDataDefinition::create('node')); - - // Config entities don't support typed data. - $entity_definition = EntityDataDefinition::create('node_type'); - $this->assertEqual(array(), $entity_definition->getPropertyDefinitions()); - } - - /** - * Tests deriving metadata from entity references. - */ - public function testEntityReferences() { - $reference_definition = DataReferenceDefinition::create('entity'); - $this->assertTrue($reference_definition instanceof DataReferenceDefinitionInterface); - - // Test retrieving metadata about the referenced data. - $this->assertEqual($reference_definition->getTargetDefinition()->getDataType(), 'entity'); - $this->assertTrue($reference_definition->getTargetDefinition() instanceof EntityDataDefinitionInterface); - - // Test that the definition factory creates the right definition object. - $reference_definition2 = $this->typedDataManager->createDataDefinition('entity_reference'); - $this->assertTrue($reference_definition2 instanceof DataReferenceDefinitionInterface); - $this->assertEqual($reference_definition2, $reference_definition); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityUUIDTest.php b/core/modules/system/src/Tests/Entity/EntityUUIDTest.php deleted file mode 100644 index 48a6e4e..0000000 --- a/core/modules/system/src/Tests/Entity/EntityUUIDTest.php +++ /dev/null @@ -1,104 +0,0 @@ -installEntitySchema($entity_type_id); - } - } - } - - /** - * Tests UUID generation in entity CRUD operations. - */ - function testCRUD() { - // All entity variations have to have the same results. - foreach (entity_test_entity_types() as $entity_type) { - $this->assertCRUD($entity_type); - } - } - - /** - * Executes the UUID CRUD tests for the given entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function assertCRUD($entity_type) { - // Verify that no UUID is auto-generated when passing one for creation. - $uuid_service = $this->container->get('uuid'); - $uuid = $uuid_service->generate(); - $custom_entity = entity_create($entity_type, array( - 'name' => $this->randomMachineName(), - 'uuid' => $uuid, - )); - $this->assertIdentical($custom_entity->uuid(), $uuid); - // Save this entity, so we have more than one later. - $custom_entity->save(); - - // Verify that a new UUID is generated upon creating an entity. - $entity = entity_create($entity_type, array('name' => $this->randomMachineName())); - $uuid = $entity->uuid(); - $this->assertTrue($uuid); - - // Verify that the new UUID is different. - $this->assertNotEqual($custom_entity->uuid(), $uuid); - - // Verify that the UUID is retained upon saving. - $entity->save(); - $this->assertIdentical($entity->uuid(), $uuid); - - // Verify that the UUID is retained upon loading. - $entity_loaded = entity_load($entity_type, $entity->id(), TRUE); - $this->assertIdentical($entity_loaded->uuid(), $uuid); - - // Verify that \Drupal::entityManager()->loadEntityByUuid() loads the same entity. - $entity_loaded_by_uuid = \Drupal::entityManager()->loadEntityByUuid($entity_type, $uuid, TRUE); - $this->assertIdentical($entity_loaded_by_uuid->uuid(), $uuid); - $this->assertEqual($entity_loaded_by_uuid->id(), $entity_loaded->id()); - - // Creating a duplicate needs to result in a new UUID. - $entity_duplicate = $entity->createDuplicate(); - foreach ($entity->getFields() as $property => $value) { - switch($property) { - case 'uuid': - $this->assertNotNull($entity_duplicate->uuid()); - $this->assertNotNull($entity->uuid()); - $this->assertNotEqual($entity_duplicate->uuid(), $entity->uuid()); - break; - case 'id': - $this->assertNull($entity_duplicate->id()); - $this->assertNotNull($entity->id()); - $this->assertNotEqual($entity_duplicate->id(), $entity->id()); - break; - case 'revision_id': - $this->assertNull($entity_duplicate->getRevisionId()); - $this->assertNotNull($entity->getRevisionId()); - $this->assertNotEqual($entity_duplicate->getRevisionId(), $entity->getRevisionId()); - $this->assertNotEqual($entity_duplicate->{$property}->getValue(), $entity->{$property}->getValue()); - break; - default: - $this->assertEqual($entity_duplicate->{$property}->getValue(), $entity->{$property}->getValue()); - } - } - $entity_duplicate->save(); - $this->assertNotEqual($entity->id(), $entity_duplicate->id()); - } -} diff --git a/core/modules/system/src/Tests/Entity/EntityUnitTestBase.php b/core/modules/system/src/Tests/Entity/EntityUnitTestBase.php index c691ad6..dbeb858 100644 --- a/core/modules/system/src/Tests/Entity/EntityUnitTestBase.php +++ b/core/modules/system/src/Tests/Entity/EntityUnitTestBase.php @@ -9,6 +9,8 @@ use Drupal\simpletest\KernelTestBase; use Drupal\Core\Entity\EntityInterface; +use Drupal\user\Entity\Role; +use Drupal\user\Entity\User; /** * Defines an abstract test base for entity unit tests. @@ -102,7 +104,7 @@ protected function setUp() { protected function createUser($values = array(), $permissions = array()) { if ($permissions) { // Create a new role and apply permissions to it. - $role = entity_create('user_role', array( + $role = Role::create(array( 'id' => strtolower($this->randomMachineName(8)), 'label' => $this->randomMachineName(8), )); @@ -111,10 +113,10 @@ protected function createUser($values = array(), $permissions = array()) { $values['roles'][] = $role->id(); } - $account = entity_create('user', $values + array( + $account = User::create($values + [ 'name' => $this->randomMachineName(), 'status' => 1, - )); + ]); $account->enforceIsNew(); $account->save(); return $account; diff --git a/core/modules/system/src/Tests/Entity/EntityValidationTest.php b/core/modules/system/src/Tests/Entity/EntityValidationTest.php deleted file mode 100644 index aa85fa7..0000000 --- a/core/modules/system/src/Tests/Entity/EntityValidationTest.php +++ /dev/null @@ -1,206 +0,0 @@ -installConfig(array('system', 'filter')); - } - - /** - * Creates a test entity. - * - * @param string $entity_type - * An entity type. - * - * @return \Drupal\Core\Entity\EntityInterface - * The created test entity. - */ - protected function createTestEntity($entity_type) { - $this->entityName = $this->randomMachineName(); - $this->entityUser = $this->createUser(); - - // Pass in the value of the name field when creating. With the user - // field we test setting a field after creation. - $entity = entity_create($entity_type); - $entity->user_id->target_id = $this->entityUser->id(); - $entity->name->value = $this->entityName; - - // Set a value for the test field. - if ($entity->hasField('field_test_text')) { - $this->entityFieldText = $this->randomMachineName(); - $entity->field_test_text->value = $this->entityFieldText; - } - - return $entity; - } - - /** - * Tests validating test entity types. - */ - public function testValidation() { - // Ensure that the constraint manager is marked as cached cleared. - - // Use the protected property on the cache_clearer first to check whether - // the constraint manager is added there. - - // Ensure that the proxy class is initialized, which has the necessary - // method calls attached. - \Drupal::service('plugin.cache_clearer'); - $plugin_cache_clearer = \Drupal::service('drupal.proxy_original_service.plugin.cache_clearer'); - $get_cached_discoveries = function () { - return $this->cachedDiscoveries; - }; - $get_cached_discoveries = $get_cached_discoveries->bindTo($plugin_cache_clearer, $plugin_cache_clearer); - $cached_discoveries = $get_cached_discoveries(); - $cached_discovery_classes = []; - foreach ($cached_discoveries as $cached_discovery) { - $cached_discovery_classes[] = get_class($cached_discovery); - } - $this->assertTrue(in_array('Drupal\Core\Validation\ConstraintManager', $cached_discovery_classes)); - - // All entity variations have to have the same results. - foreach (entity_test_entity_types() as $entity_type) { - $this->checkValidation($entity_type); - } - } - - /** - * Executes the validation test set for a defined entity type. - * - * @param string $entity_type - * The entity type to run the tests with. - */ - protected function checkValidation($entity_type) { - $entity = $this->createTestEntity($entity_type); - $violations = $entity->validate(); - $this->assertEqual($violations->count(), 0, 'Validation passes.'); - - // Test triggering a fail for each of the constraints specified. - $test_entity = clone $entity; - $test_entity->id->value = -1; - $violations = $test_entity->validate(); - $this->assertEqual($violations->count(), 1, 'Validation failed.'); - $this->assertEqual($violations[0]->getMessage(), t('%name: The integer must be larger or equal to %min.', array('%name' => 'ID', '%min' => 0))); - - $test_entity = clone $entity; - $test_entity->uuid->value = $this->randomString(129); - $violations = $test_entity->validate(); - $this->assertEqual($violations->count(), 1, 'Validation failed.'); - $this->assertEqual($violations[0]->getMessage(), t('%name: may not be longer than @max characters.', array('%name' => 'UUID', '@max' => 128))); - - $test_entity = clone $entity; - $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); - $test_entity->{$langcode_key}->value = $this->randomString(13); - $violations = $test_entity->validate(); - // This should fail on AllowedValues and Length constraints. - $this->assertEqual($violations->count(), 2, 'Validation failed.'); - $this->assertEqual($violations[0]->getMessage(), t('This value is too long. It should have %limit characters or less.', array('%limit' => '12'))); - $this->assertEqual($violations[1]->getMessage(), t('The value you selected is not a valid choice.')); - - $test_entity = clone $entity; - $test_entity->type->value = NULL; - $violations = $test_entity->validate(); - $this->assertEqual($violations->count(), 1, 'Validation failed.'); - $this->assertEqual($violations[0]->getMessage(), t('This value should not be null.')); - - $test_entity = clone $entity; - $test_entity->name->value = $this->randomString(33); - $violations = $test_entity->validate(); - $this->assertEqual($violations->count(), 1, 'Validation failed.'); - $this->assertEqual($violations[0]->getMessage(), t('%name: may not be longer than @max characters.', array('%name' => 'Name', '@max' => 32))); - - // Make sure the information provided by a violation is correct. - $violation = $violations[0]; - $this->assertEqual($violation->getRoot()->getValue(), $test_entity, 'Violation root is entity.'); - $this->assertEqual($violation->getPropertyPath(), 'name.0.value', 'Violation property path is correct.'); - $this->assertEqual($violation->getInvalidValue(), $test_entity->name->value, 'Violation contains invalid value.'); - - $test_entity = clone $entity; - $test_entity->set('user_id', 9999); - $violations = $test_entity->validate(); - $this->assertEqual($violations->count(), 1, 'Validation failed.'); - $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', array('%type' => 'user', '%id' => 9999))); - - $test_entity = clone $entity; - $test_entity->field_test_text->format = $this->randomString(33); - $violations = $test_entity->validate(); - $this->assertEqual($violations->count(), 1, 'Validation failed.'); - $this->assertEqual($violations[0]->getMessage(), t('The value you selected is not a valid choice.')); - - // Make sure the information provided by a violation is correct. - $violation = $violations[0]; - $this->assertEqual($violation->getRoot()->getValue(), $test_entity, 'Violation root is entity.'); - $this->assertEqual($violation->getPropertyPath(), 'field_test_text.0.format', 'Violation property path is correct.'); - $this->assertEqual($violation->getInvalidValue(), $test_entity->field_test_text->format, 'Violation contains invalid value.'); - } - - /** - * Tests composite constraints. - */ - public function testCompositeConstraintValidation() { - $entity = $this->createTestEntity('entity_test_composite_constraint'); - $violations = $entity->validate(); - $this->assertEqual($violations->count(), 0); - - // Trigger violation condition. - $entity->name->value = 'test'; - $entity->type->value = 'test2'; - $violations = $entity->validate(); - $this->assertEqual($violations->count(), 1); - - // Make sure we can determine this is composite constraint. - $constraint = $violations[0]->getConstraint(); - $this->assertTrue($constraint instanceof CompositeConstraintBase, 'Constraint is composite constraint.'); - $this->assertEqual('type', $violations[0]->getPropertyPath()); - - /** @var CompositeConstraintBase $constraint */ - $this->assertEqual($constraint->coversFields(), ['name', 'type'], 'Information about covered fields can be retrieved.'); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php b/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php deleted file mode 100644 index a3e6cd1..0000000 --- a/core/modules/system/src/Tests/Entity/EntityViewBuilderTest.php +++ /dev/null @@ -1,217 +0,0 @@ -installConfig(array('user', 'entity_test')); - - // Give anonymous users permission to view test entities. - Role::load(RoleInterface::ANONYMOUS_ID) - ->grantPermission('view test entity') - ->save(); - } - - /** - * Tests entity render cache handling. - */ - public function testEntityViewBuilderCache() { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = $this->container->get('renderer'); - $cache_contexts_manager = \Drupal::service("cache_contexts_manager"); - $cache = \Drupal::cache(); - - // Force a request via GET so we can get drupal_render() cache working. - $request = \Drupal::request(); - $request_method = $request->server->get('REQUEST_METHOD'); - $request->setMethod('GET'); - - $entity_test = $this->createTestEntity('entity_test'); - - // Test that new entities (before they are saved for the first time) do not - // generate a cache entry. - $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); - $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age'], 'The render array element of new (unsaved) entities is not cached, but does have cache tags set.'); - - // Get a fully built entity view render array. - $entity_test->save(); - $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); - $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys()); - $cid = implode(':', $cid_parts); - $bin = $build['#cache']['bin']; - - // Mock the build array to not require the theme registry. - unset($build['#theme']); - $build['#markup'] = 'entity_render_test'; - - // Test that a cache entry is created. - $renderer->renderRoot($build); - $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.'); - - // Re-save the entity and check that the cache entry has been deleted. - $cache->set('kittens', 'Kitten data', Cache::PERMANENT, $build['#cache']['tags']); - $entity_test->save(); - $this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was saved.'); - $this->assertFalse($cache->get('kittens'), 'The entity saving has invalidated cache tags.'); - - // Rebuild the render array (creating a new cache entry in the process) and - // delete the entity to check the cache entry is deleted. - unset($build['#printed']); - $renderer->renderRoot($build); - $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.'); - $entity_test->delete(); - $this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was deleted.'); - - // Restore the previous request method. - $request->setMethod($request_method); - } - - /** - * Tests entity render cache with references. - */ - public function testEntityViewBuilderCacheWithReferences() { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = $this->container->get('renderer'); - $cache_contexts_manager = \Drupal::service("cache_contexts_manager"); - - // Force a request via GET so we can get drupal_render() cache working. - $request = \Drupal::request(); - $request_method = $request->server->get('REQUEST_METHOD'); - $request->setMethod('GET'); - - // Create an entity reference field and an entity that will be referenced. - $this->createEntityReferenceField('entity_test', 'entity_test', 'reference_field', 'Reference', 'entity_test'); - entity_get_display('entity_test', 'entity_test', 'full')->setComponent('reference_field', [ - 'type' => 'entity_reference_entity_view', - 'settings' => ['link' => FALSE], - ])->save(); - $entity_test_reference = $this->createTestEntity('entity_test'); - $entity_test_reference->save(); - - // Get a fully built entity view render array for the referenced entity. - $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test_reference, 'full'); - $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys()); - $cid_reference = implode(':', $cid_parts); - $bin_reference = $build['#cache']['bin']; - - // Mock the build array to not require the theme registry. - unset($build['#theme']); - $build['#markup'] = 'entity_render_test'; - $renderer->renderRoot($build); - - // Test that a cache entry was created for the referenced entity. - $this->assertTrue($this->container->get('cache.' . $bin_reference)->get($cid_reference), 'The entity render element for the referenced entity has been cached.'); - - // Create another entity that references the first one. - $entity_test = $this->createTestEntity('entity_test'); - $entity_test->reference_field->entity = $entity_test_reference; - $entity_test->save(); - - // Get a fully built entity view render array. - $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); - $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys()); - $cid = implode(':', $cid_parts); - $bin = $build['#cache']['bin']; - - // Mock the build array to not require the theme registry. - unset($build['#theme']); - $build['#markup'] = 'entity_render_test'; - $renderer->renderRoot($build); - - // Test that a cache entry is created. - $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.'); - - // Save the entity and verify that both cache entries have been deleted. - $entity_test_reference->save(); - $this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was deleted.'); - $this->assertFalse($this->container->get('cache.' . $bin_reference)->get($cid_reference), 'The entity render cache for the referenced entity has been cleared when the entity was deleted.'); - - // Restore the previous request method. - $request->setMethod($request_method); - } - - /** - * Tests entity render cache toggling. - */ - public function testEntityViewBuilderCacheToggling() { - $entity_test = $this->createTestEntity('entity_test'); - $entity_test->save(); - - // Test a view mode in default conditions: render caching is enabled for - // the entity type and the view mode. - $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); - $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age', 'keys', 'bin'] , 'A view mode with render cache enabled has the correct output (cache tags, keys, contexts, max-age and bin).'); - - // Test that a view mode can opt out of render caching. - $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'test'); - $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age'], 'A view mode with render cache disabled has the correct output (only cache tags, contexts and max-age).'); - - // Test that an entity type can opt out of render caching completely. - $entity_test_no_cache = $this->createTestEntity('entity_test_label'); - $entity_test_no_cache->save(); - $build = $this->container->get('entity.manager')->getViewBuilder('entity_test_label')->view($entity_test_no_cache, 'full'); - $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age'], 'An entity type can opt out of render caching regardless of view mode configuration, but always has cache tags, contexts and max-age set.'); - } - - /** - * Tests weighting of display components. - */ - public function testEntityViewBuilderWeight() { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = $this->container->get('renderer'); - - // Set a weight for the label component. - entity_get_display('entity_test', 'entity_test', 'full') - ->setComponent('label', array('weight' => 20)) - ->save(); - - // Create and build a test entity. - $entity_test = $this->createTestEntity('entity_test'); - $view = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); - $renderer->renderRoot($view); - - // Check that the weight is respected. - $this->assertEqual($view['label']['#weight'], 20, 'The weight of a display component is respected.'); - } - - /** - * Creates an entity for testing. - * - * @param string $entity_type - * The entity type. - * - * @return \Drupal\Core\Entity\EntityInterface - * The created entity. - */ - protected function createTestEntity($entity_type) { - $data = array( - 'bundle' => $entity_type, - 'name' => $this->randomMachineName(), - ); - return $this->container->get('entity.manager')->getStorage($entity_type)->create($data); - } - -} diff --git a/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php b/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php index 5e8e5fa..882c61d 100644 --- a/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php +++ b/core/modules/system/src/Tests/Entity/EntityViewControllerTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Entity; +use Drupal\entity_test\Entity\EntityTest; use Drupal\simpletest\WebTestBase; /** @@ -67,6 +68,17 @@ function testEntityViewController() { $this->assertRaw('full'); } + // Test viewing a revisionable entity. + $entity_test_rev = $this->createTestEntity('entity_test_rev'); + $entity_test_rev->save(); + $entity_test_rev->name->value = 'rev 2'; + $entity_test_rev->setNewRevision(TRUE); + $entity_test_rev->isDefaultRevision(TRUE); + $entity_test_rev->save(); + $this->drupalGet('entity_test_rev/' . $entity_test_rev->id() . '/revision/' . $entity_test_rev->revision_id->value . '/view'); + $this->assertRaw($entity_test_rev->label()); + $this->assertRaw($get_label_markup($entity_test_rev->label())); + // As entity_test IDs must be integers, make sure requests for non-integer // IDs return a page not found error. $this->drupalGet('entity_test/invalid'); @@ -84,7 +96,7 @@ public function testFieldItemAttributes() { // Create an entity and save test value in field_test_text. $test_value = $this->randomMachineName(); - $entity = entity_create('entity_test'); + $entity = EntityTest::create(); $entity->field_test_text = $test_value; $entity->save(); diff --git a/core/modules/system/src/Tests/Entity/FieldAccessTest.php b/core/modules/system/src/Tests/Entity/FieldAccessTest.php deleted file mode 100644 index 70fc629..0000000 --- a/core/modules/system/src/Tests/Entity/FieldAccessTest.php +++ /dev/null @@ -1,81 +0,0 @@ -installConfig(array('field')); - // The users table is needed for creating dummy user accounts. - $this->installEntitySchema('user'); - // Register entity_test text field. - module_load_install('entity_test'); - entity_test_install(); - } - - /** - * Tests hook_entity_field_access() and hook_entity_field_access_alter(). - * - * @see entity_test_entity_field_access() - * @see entity_test_entity_field_access_alter() - */ - function testFieldAccess() { - $values = array( - 'name' => $this->randomMachineName(), - 'user_id' => 1, - 'field_test_text' => array( - 'value' => 'no access value', - 'format' => 'full_html', - ), - ); - $entity = entity_create('entity_test', $values); - - // Create a dummy user account for testing access with. - $values = array('name' => 'test'); - $account = entity_create('user', $values); - - $this->assertFalse($entity->field_test_text->access('view', $account), 'Access to the field was denied.'); - $expected = AccessResult::forbidden()->cacheUntilEntityChanges($entity); - $this->assertEqual($expected, $entity->field_test_text->access('view', $account, TRUE), 'Access to the field was denied.'); - - $entity->field_test_text = 'access alter value'; - $this->assertFalse($entity->field_test_text->access('view', $account), 'Access to the field was denied.'); - $this->assertEqual($expected, $entity->field_test_text->access('view', $account, TRUE), 'Access to the field was denied.'); - - $entity->field_test_text = 'standard value'; - $this->assertTrue($entity->field_test_text->access('view', $account), 'Access to the field was granted.'); - $this->assertEqual(AccessResult::allowed(), $entity->field_test_text->access('view', $account, TRUE), 'Access to the field was granted.'); - } -} diff --git a/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php b/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php deleted file mode 100644 index 67b0e94..0000000 --- a/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php +++ /dev/null @@ -1,553 +0,0 @@ -installEntitySchema('entity_test_rev'); - $entity_type = 'entity_test_rev'; - - $this->fieldName = strtolower($this->randomMachineName()); - $this->fieldCardinality = 4; - $this->fieldStorage = entity_create('field_storage_config', array( - 'field_name' => $this->fieldName, - 'entity_type' => $entity_type, - 'type' => 'test_field', - 'cardinality' => $this->fieldCardinality, - )); - $this->fieldStorage->save(); - $this->field = entity_create('field_config', array( - 'field_storage' => $this->fieldStorage, - 'bundle' => $entity_type - )); - $this->field->save(); - - /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ - $table_mapping = \Drupal::entityManager()->getStorage($entity_type)->getTableMapping(); - $this->tableMapping = $table_mapping; - $this->table = $table_mapping->getDedicatedDataTableName($this->fieldStorage); - $this->revisionTable = $table_mapping->getDedicatedRevisionTableName($this->fieldStorage); - } - - /** - * Tests field loading works correctly by inserting directly in the tables. - */ - function testFieldLoad() { - $entity_type = $bundle = 'entity_test_rev'; - $storage = $this->container->get('entity.manager')->getStorage($entity_type); - - $columns = array('bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'langcode', $this->tableMapping->getFieldColumnName($this->fieldStorage, 'value')); - - // Create an entity with four revisions. - $revision_ids = array(); - $entity = entity_create($entity_type); - $entity->save(); - $revision_ids[] = $entity->getRevisionId(); - for ($i = 0; $i < 4; $i++) { - $entity->setNewRevision(); - $entity->save(); - $revision_ids[] = $entity->getRevisionId(); - } - - // Generate values and insert them directly in the storage tables. - $values = array(); - $query = db_insert($this->revisionTable)->fields($columns); - foreach ($revision_ids as $revision_id) { - // Put one value too many. - for ($delta = 0; $delta <= $this->fieldCardinality; $delta++) { - $value = mt_rand(1, 127); - $values[$revision_id][] = $value; - $query->values(array($bundle, 0, $entity->id(), $revision_id, $delta, $entity->language()->getId(), $value)); - } - $query->execute(); - } - $query = db_insert($this->table)->fields($columns); - foreach ($values[$revision_id] as $delta => $value) { - $query->values(array($bundle, 0, $entity->id(), $revision_id, $delta, $entity->language()->getId(), $value)); - } - $query->execute(); - - // Load every revision and check the values. - foreach ($revision_ids as $revision_id) { - $entity = $storage->loadRevision($revision_id); - foreach ($values[$revision_id] as $delta => $value) { - if ($delta < $this->fieldCardinality) { - $this->assertEqual($entity->{$this->fieldName}[$delta]->value, $value); - } - else { - $this->assertFalse(array_key_exists($delta, $entity->{$this->fieldName})); - } - } - } - - // Load the "current revision" and check the values. - $entity = $storage->load($entity->id()); - foreach ($values[$revision_id] as $delta => $value) { - if ($delta < $this->fieldCardinality) { - $this->assertEqual($entity->{$this->fieldName}[$delta]->value, $value); - } - else { - $this->assertFalse(array_key_exists($delta, $entity->{$this->fieldName})); - } - } - - // Add a translation in an unavailable language code and verify it is not - // loaded. - $unavailable_langcode = 'xx'; - $values = array($bundle, 0, $entity->id(), $entity->getRevisionId(), 0, $unavailable_langcode, mt_rand(1, 127)); - db_insert($this->table)->fields($columns)->values($values)->execute(); - db_insert($this->revisionTable)->fields($columns)->values($values)->execute(); - $entity = $storage->load($entity->id()); - $this->assertFalse(array_key_exists($unavailable_langcode, $entity->{$this->fieldName})); - } - - /** - * Tests field saving works correctly by reading directly from the tables. - */ - function testFieldWrite() { - $entity_type = $bundle = 'entity_test_rev'; - $entity = entity_create($entity_type); - - $revision_values = array(); - - // Check insert. Add one value too many. - $values = array(); - for ($delta = 0; $delta <= $this->fieldCardinality; $delta++) { - $values[$delta]['value'] = mt_rand(1, 127); - } - $entity->{$this->fieldName} = $values; - $entity->save(); - - // Read the tables and check the correct values have been stored. - $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', \PDO::FETCH_ASSOC); - $this->assertEqual(count($rows), $this->fieldCardinality); - foreach ($rows as $delta => $row) { - $expected = array( - 'bundle' => $bundle, - 'deleted' => 0, - 'entity_id' => $entity->id(), - 'revision_id' => $entity->getRevisionId(), - 'langcode' => $entity->language()->getId(), - 'delta' => $delta, - $this->fieldName . '_value' => $values[$delta]['value'], - ); - $this->assertEqual($row, $expected, "Row $delta was stored as expected."); - } - - // Test update. Add less values and check that the previous values did not - // persist. - $values = array(); - for ($delta = 0; $delta <= $this->fieldCardinality - 2; $delta++) { - $values[$delta]['value'] = mt_rand(1, 127); - } - $entity->{$this->fieldName} = $values; - $entity->save(); - $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', \PDO::FETCH_ASSOC); - $this->assertEqual(count($rows), count($values)); - foreach ($rows as $delta => $row) { - $expected = array( - 'bundle' => $bundle, - 'deleted' => 0, - 'entity_id' => $entity->id(), - 'revision_id' => $entity->getRevisionId(), - 'langcode' => $entity->language()->getId(), - 'delta' => $delta, - $this->fieldName . '_value' => $values[$delta]['value'], - ); - $this->assertEqual($row, $expected, "Row $delta was stored as expected."); - } - - // Create a new revision. - $revision_values[$entity->getRevisionId()] = $values; - $values = array(); - for ($delta = 0; $delta < $this->fieldCardinality; $delta++) { - $values[$delta]['value'] = mt_rand(1, 127); - } - $entity->{$this->fieldName} = $values; - $entity->setNewRevision(); - $entity->save(); - $revision_values[$entity->getRevisionId()] = $values; - - // Check that data for both revisions are in the revision table. - foreach ($revision_values as $revision_id => $values) { - $rows = db_select($this->revisionTable, 't')->fields('t')->condition('revision_id', $revision_id)->execute()->fetchAllAssoc('delta', \PDO::FETCH_ASSOC); - $this->assertEqual(count($rows), min(count($values), $this->fieldCardinality)); - foreach ($rows as $delta => $row) { - $expected = array( - 'bundle' => $bundle, - 'deleted' => 0, - 'entity_id' => $entity->id(), - 'revision_id' => $revision_id, - 'langcode' => $entity->language()->getId(), - 'delta' => $delta, - $this->fieldName . '_value' => $values[$delta]['value'], - ); - $this->assertEqual($row, $expected, "Row $delta was stored as expected."); - } - } - - // Test emptying the field. - $entity->{$this->fieldName} = NULL; - $entity->save(); - $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', \PDO::FETCH_ASSOC); - $this->assertEqual(count($rows), 0); - } - - /** - * Tests that long entity type and field names do not break. - */ - function testLongNames() { - // Use one of the longest entity_type names in core. - $entity_type = $bundle = 'entity_test_label_callback'; - $storage = $this->container->get('entity.manager')->getStorage($entity_type); - - // Create two fields and generate random values. - $name_base = Unicode::strtolower($this->randomMachineName(FieldStorageConfig::NAME_MAX_LENGTH - 1)); - $field_names = array(); - $values = array(); - for ($i = 0; $i < 2; $i++) { - $field_names[$i] = $name_base . $i; - entity_create('field_storage_config', array( - 'field_name' => $field_names[$i], - 'entity_type' => $entity_type, - 'type' => 'test_field', - ))->save(); - entity_create('field_config', array( - 'field_name' => $field_names[$i], - 'entity_type' => $entity_type, - 'bundle' => $bundle, - ))->save(); - $values[$field_names[$i]] = mt_rand(1, 127); - } - - // Save an entity with values. - $entity = entity_create($entity_type, $values); - $entity->save(); - - // Load the entity back and check the values. - $entity = $storage->load($entity->id()); - foreach ($field_names as $field_name) { - $this->assertEqual($entity->get($field_name)->value, $values[$field_name]); - } - } - - /** - * Test trying to update a field with data. - */ - function testUpdateFieldSchemaWithData() { - $entity_type = 'entity_test_rev'; - // Create a decimal 5.2 field and add some data. - $field_storage = entity_create('field_storage_config', array( - 'field_name' => 'decimal52', - 'entity_type' => $entity_type, - 'type' => 'decimal', - 'settings' => array('precision' => 5, 'scale' => 2), - )); - $field_storage->save(); - $field = entity_create('field_config', array( - 'field_storage' => $field_storage, - 'bundle' => $entity_type, - )); - $field->save(); - $entity = entity_create($entity_type, array( - 'id' => 0, - 'revision_id' => 0, - )); - $entity->decimal52->value = '1.235'; - $entity->save(); - - // Attempt to update the field in a way that would work without data. - $field_storage->setSetting('scale', 3); - try { - $field_storage->save(); - $this->fail(t('Cannot update field schema with data.')); - } - catch (FieldStorageDefinitionUpdateForbiddenException $e) { - $this->pass(t('Cannot update field schema with data.')); - } - } - - /** - * Test that failure to create fields is handled gracefully. - */ - function testFieldUpdateFailure() { - // Create a text field. - $field_storage = entity_create('field_storage_config', array( - 'field_name' => 'test_text', - 'entity_type' => 'entity_test_rev', - 'type' => 'text', - 'settings' => array('max_length' => 255), - )); - $field_storage->save(); - - // Attempt to update the field in a way that would break the storage. The - // parenthesis suffix is needed because SQLite has *very* relaxed rules for - // data types, so we actually need to provide an invalid SQL syntax in order - // to break it. - // @see https://www.sqlite.org/datatype3.html - $prior_field_storage = $field_storage; - $field_storage->setSetting('max_length', '-1)'); - try { - $field_storage->save(); - $this->fail(t('Update succeeded.')); - } - catch (\Exception $e) { - $this->pass(t('Update properly failed.')); - } - - // Ensure that the field tables are still there. - $tables = array( - $this->tableMapping->getDedicatedDataTableName($prior_field_storage), - $this->tableMapping->getDedicatedRevisionTableName($prior_field_storage), - ); - foreach ($tables as $table_name) { - $this->assertTrue(db_table_exists($table_name), t('Table %table exists.', array('%table' => $table_name))); - } - } - - /** - * Test adding and removing indexes while data is present. - */ - function testFieldUpdateIndexesWithData() { - // Create a decimal field. - $field_name = 'testfield'; - $entity_type = 'entity_test_rev'; - $field_storage = entity_create('field_storage_config', array( - 'field_name' => $field_name, - 'entity_type' => $entity_type, - 'type' => 'text', - )); - $field_storage->save(); - $field = entity_create('field_config', array( - 'field_storage' => $field_storage, - 'bundle' => $entity_type, - )); - $field->save(); - $tables = array($this->tableMapping->getDedicatedDataTableName($field_storage), $this->tableMapping->getDedicatedRevisionTableName($field_storage)); - - // Verify the indexes we will create do not exist yet. - foreach ($tables as $table) { - $this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value'), t("No index named value exists in @table", array('@table' => $table))); - $this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value_format'), t("No index named value_format exists in @table", array('@table' => $table))); - } - - // Add data so the table cannot be dropped. - $entity = entity_create($entity_type, array( - 'id' => 1, - 'revision_id' => 1, - )); - $entity->$field_name->value = 'field data'; - $entity->enforceIsNew(); - $entity->save(); - - // Add an index. - $field_storage->setIndexes(['value' => [['value', 255]]]); - $field_storage->save(); - foreach ($tables as $table) { - $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value created in @table", array('@table' => $table))); - } - - // Add a different index, removing the existing custom one. - $field_storage->setIndexes(['value_format' => [['value', 127], ['format', 127]]]); - $field_storage->save(); - foreach ($tables as $table) { - $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value_format"), t("Index on value_format created in @table", array('@table' => $table))); - $this->assertFalse(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value removed in @table", array('@table' => $table))); - } - - // Verify that the tables were not dropped in the process. - $entity = $this->container->get('entity.manager')->getStorage($entity_type)->load(1); - $this->assertEqual($entity->$field_name->value, 'field data', t("Index changes performed without dropping the tables")); - } - - /** - * Test foreign key support. - */ - function testFieldSqlStorageForeignKeys() { - // Create a 'shape' field, with a configurable foreign key (see - // field_test_field_schema()). - $field_name = 'testfield'; - $foreign_key_name = 'shape'; - $field_storage = entity_create('field_storage_config', array( - 'field_name' => $field_name, - 'entity_type' => 'entity_test', - 'type' => 'shape', - 'settings' => array('foreign_key_name' => $foreign_key_name), - )); - $field_storage->save(); - // Get the field schema. - $schema = $field_storage->getSchema(); - - // Retrieve the field definition and check that the foreign key is in place. - $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name preserved through CRUD'); - $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name preserved through CRUD'); - - // Update the field settings, it should update the foreign key definition too. - $foreign_key_name = 'color'; - $field_storage->setSetting('foreign_key_name', $foreign_key_name); - $field_storage->save(); - // Reload the field schema after the update. - $schema = $field_storage->getSchema(); - - // Check that the foreign key is in place. - $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name modified after update'); - $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update'); - } - - /** - * Tests table name generation. - */ - public function testTableNames() { - // Note: we need to test entity types with long names. We therefore use - // fields on imaginary entity types (works as long as we don't actually save - // them), and just check the generated table names. - - // Short entity type and field name. - $entity_type = 'short_entity_type'; - $field_name = 'short_field_name'; - $field_storage = entity_create('field_storage_config', array( - 'entity_type' => $entity_type, - 'field_name' => $field_name, - 'type' => 'test_field', - )); - $expected = 'short_entity_type__short_field_name'; - $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $expected); - $expected = 'short_entity_type_revision__short_field_name'; - $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $expected); - - // Short entity type, long field name - $entity_type = 'short_entity_type'; - $field_name = 'long_field_name_abcdefghijklmnopqrstuvwxyz'; - $field_storage = entity_create('field_storage_config', array( - 'entity_type' => $entity_type, - 'field_name' => $field_name, - 'type' => 'test_field', - )); - $expected = 'short_entity_type__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $expected); - $expected = 'short_entity_type_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $expected); - - // Long entity type, short field name - $entity_type = 'long_entity_type_abcdefghijklmnopqrstuvwxyz'; - $field_name = 'short_field_name'; - $field_storage = entity_create('field_storage_config', array( - 'entity_type' => $entity_type, - 'field_name' => $field_name, - 'type' => 'test_field', - )); - $expected = 'long_entity_type_abcdefghijklmnopq__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $expected); - $expected = 'long_entity_type_abcdefghijklmnopq_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $expected); - - // Long entity type and field name. - $entity_type = 'long_entity_type_abcdefghijklmnopqrstuvwxyz'; - $field_name = 'long_field_name_abcdefghijklmnopqrstuvwxyz'; - $field_storage = entity_create('field_storage_config', array( - 'entity_type' => $entity_type, - 'field_name' => $field_name, - 'type' => 'test_field', - )); - $expected = 'long_entity_type_abcdefghijklmnopq__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $expected); - $expected = 'long_entity_type_abcdefghijklmnopq_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $expected); - // Try creating a second field and check there are no clashes. - $field_storage2 = entity_create('field_storage_config', array( - 'entity_type' => $entity_type, - 'field_name' => $field_name . '2', - 'type' => 'test_field', - )); - $this->assertNotEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $this->tableMapping->getDedicatedDataTableName($field_storage2)); - $this->assertNotEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $this->tableMapping->getDedicatedRevisionTableName($field_storage2)); - - // Deleted field. - $field_storage = entity_create('field_storage_config', array( - 'entity_type' => 'some_entity_type', - 'field_name' => 'some_field_name', - 'type' => 'test_field', - 'deleted' => TRUE, - )); - $expected = 'field_deleted_data_' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage, TRUE), $expected); - $expected = 'field_deleted_revision_' . substr(hash('sha256', $field_storage->uuid()), 0, 10); - $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage, TRUE), $expected); - } - -} diff --git a/core/modules/system/src/Tests/Entity/FieldTranslationSqlStorageTest.php b/core/modules/system/src/Tests/Entity/FieldTranslationSqlStorageTest.php deleted file mode 100644 index 989f00b..0000000 --- a/core/modules/system/src/Tests/Entity/FieldTranslationSqlStorageTest.php +++ /dev/null @@ -1,109 +0,0 @@ -entityManager->getStorage($entity_type); - $values = array( - $this->fieldName => $this->randomMachineName(), - $this->untranslatableFieldName => $this->randomMachineName(), - ); - $entity = $controller->create($values); - $entity->save(); - - // Tests that when changing language field language codes are still correct. - $langcode = $this->langcodes[0]; - $entity->langcode->value = $langcode; - $entity->save(); - $this->assertFieldStorageLangcode($entity, 'Field language successfully changed from language neutral.'); - $langcode = $this->langcodes[1]; - $entity->langcode->value = $langcode; - $entity->save(); - $this->assertFieldStorageLangcode($entity, 'Field language successfully changed.'); - $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED; - $entity->langcode->value = $langcode; - $entity->save(); - $this->assertFieldStorageLangcode($entity, 'Field language successfully changed to language neutral.'); - - // Test that after switching field translatability things keep working as - // before. - $this->toggleFieldTranslatability($entity_type, $entity_type); - $entity = $this->reloadEntity($entity); - foreach (array($this->fieldName, $this->untranslatableFieldName) as $field_name) { - $this->assertEqual($entity->get($field_name)->value, $values[$field_name], 'Field language works as expected after switching translatability.'); - } - - // Test that after disabling field translatability translated values are not - // loaded. - $this->toggleFieldTranslatability($entity_type, $entity_type); - $entity = $this->reloadEntity($entity); - $entity->langcode->value = $this->langcodes[0]; - $translation = $entity->addTranslation($this->langcodes[1]); - $translated_value = $this->randomMachineName(); - $translation->get($this->fieldName)->value = $translated_value; - $translation->save(); - $this->toggleFieldTranslatability($entity_type, $entity_type); - $entity = $this->reloadEntity($entity); - $this->assertEqual($entity->getTranslation($this->langcodes[1])->get($this->fieldName)->value, $values[$this->fieldName], 'Existing field translations are not loaded for untranslatable fields.'); - } - - /** - * Checks whether field languages are correctly stored for the given entity. - * - * @param \Drupal\Core\Entity\FieldableEntityInterface $entity - * The entity fields are attached to. - * @param string $message - * (optional) A message to display with the assertion. - */ - protected function assertFieldStorageLangcode(FieldableEntityInterface $entity, $message = '') { - $status = TRUE; - $entity_type = $entity->getEntityTypeId(); - $id = $entity->id(); - $langcode = $entity->getUntranslated()->language()->getId(); - $fields = array($this->fieldName, $this->untranslatableFieldName); - /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ - $table_mapping = \Drupal::entityManager()->getStorage($entity_type)->getTableMapping(); - - foreach ($fields as $field_name) { - $field_storage = FieldStorageConfig::loadByName($entity_type, $field_name); - $table = $table_mapping->getDedicatedDataTableName($field_storage); - - $record = \Drupal::database() - ->select($table, 'f') - ->fields('f') - ->condition('f.entity_id', $id) - ->condition('f.revision_id', $id) - ->execute() - ->fetchObject(); - - if ($record->langcode != $langcode) { - $status = FALSE; - break; - } - } - - return $this->assertTrue($status, $message); - } - -} diff --git a/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php b/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php deleted file mode 100644 index 4f223e3..0000000 --- a/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php +++ /dev/null @@ -1,158 +0,0 @@ -installSchema('system', ['router', 'key_value']); - $this->container->get('router.builder')->rebuild(); - - $this->installEntitySchema('user'); - $this->installEntitySchema('entity_test_composite_constraint'); - } - - /** - * Tests widget constraint validation. - */ - public function testValidation() { - $entity_type = 'entity_test_constraint_violation'; - $entity = entity_create($entity_type, array('id' => 1, 'revision_id' => 1)); - $display = entity_get_form_display($entity_type, $entity_type, 'default'); - $form = array(); - $form_state = new FormState(); - $display->buildForm($entity, $form, $form_state); - - // Pretend the form has been built. - $form_state->setFormObject(\Drupal::entityManager()->getFormObject($entity_type, 'default')); - \Drupal::formBuilder()->prepareForm('field_test_entity_form', $form, $form_state); - \Drupal::formBuilder()->processForm('field_test_entity_form', $form, $form_state); - - // Validate the field constraint. - $form_state->getFormObject()->setEntity($entity)->setFormDisplay($display, $form_state); - $entity = $form_state->getFormObject()->buildEntity($form, $form_state); - $display->validateFormValues($entity, $form, $form_state); - - $errors = $form_state->getErrors(); - $this->assertEqual($errors['name'], 'Widget constraint has failed.', 'Constraint violation is generated correctly'); - } - - /** - * Gets the form errors for a given entity. - * - * @param \Drupal\Core\Entity\EntityInterface $entity - * The entity - * @param array $hidden_fields - * (optional) A list of hidden fields. - * - * @return array - * The form errors. - */ - protected function getErrorsForEntity(EntityInterface $entity, $hidden_fields = []) { - $entity_type_id = 'entity_test_composite_constraint'; - $display = entity_get_form_display($entity_type_id, $entity_type_id, 'default'); - - foreach ($hidden_fields as $hidden_field) { - $display->removeComponent($hidden_field); - } - - $form = []; - $form_state = new FormState(); - $display->buildForm($entity, $form, $form_state); - - $form_state->setFormObject(\Drupal::entityManager()->getFormObject($entity_type_id, 'default')); - \Drupal::formBuilder()->prepareForm('field_test_entity_form', $form, $form_state); - \Drupal::formBuilder()->processForm('field_test_entity_form', $form, $form_state); - - // Validate the field constraint. - /** @var \Drupal\Core\Entity\ContentEntityFormInterface $form_object */ - $form_object = $form_state->getFormObject(); - $form_object - ->setEntity($entity) - ->setFormDisplay($display, $form_state) - ->validateForm($form, $form_state); - - return $form_state->getErrors(); - } - - /** - * Tests widget constraint validation with composite constraints. - */ - public function testValidationWithCompositeConstraint() { - // First provide a valid value, this should cause no validation. - $entity = EntityTestCompositeConstraint::create([ - 'name' => 'valid-value', - ]); - $entity->save(); - - $errors = $this->getErrorsForEntity($entity); - $this->assertFalse(isset($errors['name'])); - $this->assertFalse(isset($errors['type'])); - - // Provide an invalid value for the name field. - $entity = EntityTestCompositeConstraint::create([ - 'name' => 'failure-field-name', - ]); - $errors = $this->getErrorsForEntity($entity); - $this->assertTrue(isset($errors['name'])); - $this->assertFalse(isset($errors['type'])); - - // Hide the second field (type) and ensure the validation still happens. The - // error message appears on the first field (name). - $entity = EntityTestCompositeConstraint::create([ - 'name' => 'failure-field-name', - ]); - $errors = $this->getErrorsForEntity($entity, ['type']); - $this->assertTrue(isset($errors['name'])); - $this->assertFalse(isset($errors['type'])); - - // Provide a violation again, but this time hide the first field (name). - // Ensure that the validation still happens and the error message is moved - // from the field to the second field and have a custom error message. - $entity = EntityTestCompositeConstraint::create([ - 'name' => 'failure-field-name', - ]); - $errors = $this->getErrorsForEntity($entity, ['name']); - $this->assertFalse(isset($errors['name'])); - $this->assertTrue(isset($errors['type'])); - $this->assertEqual($errors['type'], SafeMarkup::format('The validation failed because the value conflicts with the value in %field_name, which you cannot access.', ['%field_name' => 'name'])); - } - - /** - * Tests entity level constraint validation. - */ - public function testEntityLevelConstraintValidation() { - $entity = EntityTestCompositeConstraint::create([ - 'name' => 'entity-level-violation' - ]); - $entity->save(); - - $errors = $this->getErrorsForEntity($entity); - $this->assertEqual($errors[''], 'Entity level validation'); - } - -} diff --git a/core/modules/system/src/Tests/Entity/ValidReferenceConstraintValidatorTest.php b/core/modules/system/src/Tests/Entity/ValidReferenceConstraintValidatorTest.php deleted file mode 100644 index 69a7acb..0000000 --- a/core/modules/system/src/Tests/Entity/ValidReferenceConstraintValidatorTest.php +++ /dev/null @@ -1,75 +0,0 @@ -installSchema('user', array('users_data')); - $this->typedData = $this->container->get('typed_data_manager'); - } - - /** - * Tests the ValidReferenceConstraintValidator. - */ - public function testValidation() { - // Create a test entity to be referenced. - $entity = $this->createUser(); - // By default entity references already have the ValidReference constraint. - $definition = BaseFieldDefinition::create('entity_reference') - ->setSettings(array('target_type' => 'user')); - - $typed_data = $this->typedData->create($definition, array('target_id' => $entity->id())); - $violations = $typed_data->validate(); - $this->assertFalse($violations->count(), 'Validation passed for correct value.'); - - // NULL is also considered a valid reference. - $typed_data = $this->typedData->create($definition, array('target_id' => NULL)); - $violations = $typed_data->validate(); - $this->assertFalse($violations->count(), 'Validation passed for correct value.'); - - $typed_data = $this->typedData->create($definition, array('target_id' => $entity->id())); - // Delete the referenced entity. - $entity->delete(); - $violations = $typed_data->validate(); - $this->assertTrue($violations->count(), 'Validation failed for incorrect value.'); - - // Make sure the information provided by a violation is correct. - $violation = $violations[0]; - $this->assertEqual($violation->getMessage(), t('The referenced entity (%type: %id) does not exist.', array( - '%type' => 'user', - '%id' => $entity->id(), - )), 'The message for invalid value is correct.'); - $this->assertEqual($violation->getRoot(), $typed_data, 'Violation root is correct.'); - } - -} diff --git a/core/modules/system/src/Tests/Field/FieldItemTest.php b/core/modules/system/src/Tests/Field/FieldItemTest.php deleted file mode 100644 index 0bb09ea..0000000 --- a/core/modules/system/src/Tests/Field/FieldItemTest.php +++ /dev/null @@ -1,110 +0,0 @@ -container->get('state')->set('entity_test.field_test_item', TRUE); - $this->entityManager->clearCachedDefinitions(); - - $entity_type_id = 'entity_test_mulrev'; - $this->installEntitySchema($entity_type_id); - - $this->fieldName = Unicode::strtolower($this->randomMachineName()); - - /** @var \Drupal\field\Entity\FieldStorageConfig $field_storage */ - FieldStorageConfig::create([ - 'field_name' => $this->fieldName, - 'type' => 'field_test', - 'entity_type' => $entity_type_id, - 'cardinality' => 1, - ])->save(); - - FieldConfig::create([ - 'entity_type' => $entity_type_id, - 'field_name' => $this->fieldName, - 'bundle' => $entity_type_id, - 'label' => 'Test field', - ])->save(); - - $this->entityManager->clearCachedDefinitions(); - $definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id); - $this->assertTrue(!empty($definitions[$this->fieldName])); - } - - /** - * Tests the field item save workflow. - */ - public function testSaveWorkflow() { - $entity = EntityTestMulRev::create([ - 'name' => $this->randomString(), - 'field_test_item' => $this->randomString(), - $this->fieldName => $this->randomString(), - ]); - - // Save a new entity and verify that the initial field value is overwritten - // with a value containing the entity id, which implies a resave. Check that - // the entity data structure and the stored values match. - $this->assertSavedFieldItemValue($entity, "field_test:{$this->fieldName}:1:1"); - - // Update the entity and verify that the field value is overwritten on - // presave if it is not resaved. - $this->assertSavedFieldItemValue($entity, 'overwritten'); - - // Flag the field value as needing to be resaved and verify it actually is. - $entity->field_test_item->value = $entity->{$this->fieldName}->value = 'resave'; - $this->assertSavedFieldItemValue($entity, "field_test:{$this->fieldName}:1:3"); - } - - /** - * Checks that the saved field item value matches the expected one. - * - * @param \Drupal\entity_test\Entity\EntityTest $entity - * The test entity. - * @param $expected_value - * The expected field item value. - * - * @return bool - * TRUE if the item value matches expectations, FALSE otherwise. - */ - protected function assertSavedFieldItemValue(EntityTest $entity, $expected_value) { - $entity->setNewRevision(TRUE); - $entity->save(); - $base_field_expected_value = str_replace($this->fieldName, 'field_test_item', $expected_value); - $result = $this->assertEqual($entity->field_test_item->value, $base_field_expected_value); - $result = $result && $this->assertEqual($entity->{$this->fieldName}->value, $expected_value); - $entity = $this->reloadEntity($entity); - $result = $result && $this->assertEqual($entity->field_test_item->value, $base_field_expected_value); - $result = $result && $this->assertEqual($entity->{$this->fieldName}->value, $expected_value); - return $result; - } - -} diff --git a/core/modules/system/src/Tests/Field/FieldModuleUninstallValidatorTest.php b/core/modules/system/src/Tests/Field/FieldModuleUninstallValidatorTest.php deleted file mode 100644 index 2d7c160..0000000 --- a/core/modules/system/src/Tests/Field/FieldModuleUninstallValidatorTest.php +++ /dev/null @@ -1,142 +0,0 @@ -installSchema('user', 'users_data'); - $this->entityDefinitionUpdateManager = $this->container->get('entity.definition_update_manager'); - - // Setup some fields for entity_test_extra to create. - $definitions['extra_base_field'] = BaseFieldDefinition::create('string') - ->setName('extra_base_field') - ->setTargetEntityTypeId('entity_test') - ->setTargetBundle('entity_test'); - $this->state->set('entity_test.additional_base_field_definitions', $definitions); - $definitions['extra_bundle_field'] = FieldStorageDefinition::create('string') - ->setName('extra_bundle_field') - ->setTargetEntityTypeId('entity_test') - ->setTargetBundle('entity_test'); - $this->state->set('entity_test.additional_field_storage_definitions', $definitions); - $this->state->set('entity_test.entity_test.additional_bundle_field_definitions', $definitions); - $this->entityManager->clearCachedDefinitions(); - } - - /** - * Tests uninstall entity_test module with and without content for the field. - */ - public function testUninstallingModule() { - // Test uninstall works fine without content. - $this->assertModuleInstallUninstall('entity_test_extra'); - - // Test uninstalling works fine with content having no field values. - $entity = $this->entityManager->getStorage('entity_test')->create([ - 'name' => $this->randomString(), - ]); - $entity->save(); - $this->assertModuleInstallUninstall('entity_test_extra'); - $entity->delete(); - - // Verify uninstall works fine without content again. - $this->assertModuleInstallUninstall('entity_test_extra'); - // Verify uninstalling entity_test is not possible when there is content for - // the base field. - $this->enableModules(['entity_test_extra']); - $this->entityDefinitionUpdateManager->applyUpdates(); - $entity = $this->entityManager->getStorage('entity_test')->create([ - 'name' => $this->randomString(), - 'extra_base_field' => $this->randomString(), - ]); - $entity->save(); - - try { - $message = 'Module uninstallation fails as the module provides a base field which has content.'; - $this->getModuleInstaller()->uninstall(array('entity_test_extra')); - $this->fail($message); - } - catch (ModuleUninstallValidatorException $e) { - $this->pass($message); - $this->assertEqual($e->getMessage(), 'The following reasons prevent the modules from being uninstalled: There is data for the field extra_base_field on entity type Test entity'); - } - - // Verify uninstalling entity_test is not possible when there is content for - // the bundle field. - $entity->delete(); - $this->assertModuleInstallUninstall('entity_test_extra'); - $this->enableModules(['entity_test_extra']); - $this->entityDefinitionUpdateManager->applyUpdates(); - $entity = $this->entityManager->getStorage('entity_test')->create([ - 'name' => $this->randomString(), - 'extra_bundle_field' => $this->randomString(), - ]); - $entity->save(); - try { - $this->getModuleInstaller()->uninstall(array('entity_test_extra')); - $this->fail('Module uninstallation fails as the module provides a bundle field which has content.'); - } - catch (ModuleUninstallValidatorException $e) { - $this->pass('Module uninstallation fails as the module provides a bundle field which has content.'); - } - } - - /** - * Asserts the given module can be installed and uninstalled. - * - * @param string $module_name - * The module to install and uninstall. - */ - protected function assertModuleInstallUninstall($module_name) { - $this->enableModules([$module_name]); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertTrue($this->getModuleHandler()->moduleExists($module_name), $module_name .' module is enabled.'); - $this->getModuleInstaller()->uninstall([$module_name]); - $this->entityDefinitionUpdateManager->applyUpdates(); - $this->assertFalse($this->getModuleHandler()->moduleExists($module_name), $module_name . ' module is disabled.'); - } - - /** - * Returns the ModuleHandler. - * - * @return \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected function getModuleHandler() { - return $this->container->get('module_handler'); - } - - /** - * Returns the ModuleInstaller. - * - * @return \Drupal\Core\Extension\ModuleInstallerInterface - */ - protected function getModuleInstaller() { - return $this->container->get('module_installer'); - } - -} diff --git a/core/modules/system/src/Tests/Field/FieldSettingsTest.php b/core/modules/system/src/Tests/Field/FieldSettingsTest.php deleted file mode 100644 index a996f8e..0000000 --- a/core/modules/system/src/Tests/Field/FieldSettingsTest.php +++ /dev/null @@ -1,120 +0,0 @@ - 'dummy test string', - 'changeable' => 'a changeable field storage setting', - 'unchangeable' => 'an unchangeable field storage setting', - 'translatable_storage_setting' => 'a translatable field storage setting', - 'test_field_setting' => 'dummy test string', - 'translatable_field_setting' => 'a translatable field setting', - ]; - $this->assertEqual($base_field->getSettings(), $expected_settings); - - // Change one single setting using setSettings(), and check that the other - // expected settings are still present. - $expected_settings['test_field_setting'] = 'another test string'; - $base_field->setSettings(['test_field_setting' => $expected_settings['test_field_setting']]); - $this->assertEqual($base_field->getSettings(), $expected_settings); - } - - /** - * @covers \Drupal\field\Entity\FieldStorageConfig::getSettings() - * @covers \Drupal\field\Entity\FieldStorageConfig::setSettings() - */ - public function testConfigurableFieldStorageSettings() { - $field_storage = FieldStorageConfig::create([ - 'field_name' => 'test_field', - 'entity_type' => 'entity_test', - 'type' => 'test_field' - ]); - - // Check that the default settings have been populated. - $expected_settings = [ - 'test_field_storage_setting' => 'dummy test string', - 'changeable' => 'a changeable field storage setting', - 'unchangeable' => 'an unchangeable field storage setting', - 'translatable_storage_setting' => 'a translatable field storage setting', - ]; - $this->assertEqual($field_storage->getSettings(), $expected_settings); - - // Change one single setting using setSettings(), and check that the other - // expected settings are still present. - $expected_settings['test_field_storage_setting'] = 'another test string'; - $field_storage->setSettings(['test_field_storage_setting' => $expected_settings['test_field_storage_setting']]); - $this->assertEqual($field_storage->getSettings(), $expected_settings); - } - - /** - * @covers \Drupal\field\Entity\FieldStorageConfig::getSettings() - * @covers \Drupal\field\Entity\FieldStorageConfig::setSettings() - */ - public function testConfigurableFieldSettings() { - $field_storage = FieldStorageConfig::create([ - 'field_name' => 'test_field', - 'entity_type' => 'entity_test', - 'type' => 'test_field' - ]); - $field = FieldConfig::create([ - 'field_storage' => $field_storage, - 'bundle' => 'entity_test' - ]); - // Note: FieldConfig does not populate default settings until the config - // is saved. - // @todo Remove once https://www.drupal.org/node/2327883 is fixed. - $field->save(); - - // Check that the default settings have been populated. Note: getSettings() - // returns both storage and field settings. - $expected_settings = [ - 'test_field_storage_setting' => 'dummy test string', - 'changeable' => 'a changeable field storage setting', - 'unchangeable' => 'an unchangeable field storage setting', - 'translatable_storage_setting' => 'a translatable field storage setting', - 'test_field_setting' => 'dummy test string', - 'translatable_field_setting' => 'a translatable field setting', - 'field_setting_from_config_data' => TRUE, - ]; - $this->assertEqual($field->getSettings(), $expected_settings); - - // Change one single setting using setSettings(), and check that the other - // expected settings are still present. - $expected_settings['test_field_setting'] = 'another test string'; - $field->setSettings(['test_field_setting' => $expected_settings['test_field_setting']]); - $this->assertEqual($field->getSettings(), $expected_settings); - } - -} diff --git a/core/modules/system/src/Tests/File/StreamWrapperTest.php b/core/modules/system/src/Tests/File/StreamWrapperTest.php index 343cd83..5816861 100644 --- a/core/modules/system/src/Tests/File/StreamWrapperTest.php +++ b/core/modules/system/src/Tests/File/StreamWrapperTest.php @@ -102,7 +102,6 @@ function testUriFunctions() { // Test file_create_url() // TemporaryStream::getExternalUrl() uses Url::fromRoute(), which needs // route information to work. - $this->installSchema('system', 'router'); $this->container->get('router.builder')->rebuild(); $this->assertTrue(strpos(file_create_url('temporary://test.txt'), 'system/temporary?file=test.txt'), 'Temporary external URL correctly built.'); $this->assertTrue(strpos(file_create_url('public://test.txt'), Settings::get('file_public_path') . '/test.txt'), 'Public external URL correctly built.'); diff --git a/core/modules/system/src/Tests/File/UrlRewritingTest.php b/core/modules/system/src/Tests/File/UrlRewritingTest.php index 5239fc9..dd37222 100644 --- a/core/modules/system/src/Tests/File/UrlRewritingTest.php +++ b/core/modules/system/src/Tests/File/UrlRewritingTest.php @@ -61,13 +61,13 @@ function testShippedFileURL() { \Drupal::state()->delete('file_test.hook_file_url_alter'); $filepath = 'core/misc/favicon.ico'; $url = file_create_url($filepath . '?foo'); - $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '?foo=', $url, 'Correctly generated url. The query string is present.'); + $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '?foo=', $url, 'Correctly generated URL. The query string is present.'); $url = file_create_url($filepath . '?foo=bar'); - $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '?foo=bar', $url, 'Correctly generated url. The query string is present.'); + $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '?foo=bar', $url, 'Correctly generated URL. The query string is present.'); $url = file_create_url($filepath . '#v1.2'); - $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '#v1.2', $url, 'Correctly generated url. The fragment is present.'); + $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '#v1.2', $url, 'Correctly generated URL. The fragment is present.'); $url = file_create_url($filepath . '?foo=bar#v1.2'); - $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '?foo=bar#v1.2', $url, 'Correctly generated url. The query string amd fragment is present.'); + $this->assertEqual($GLOBALS['base_url'] . '/' . $filepath . '?foo=bar#v1.2', $url, 'Correctly generated URL. The query string amd fragment is present.'); } /** diff --git a/core/modules/system/src/Tests/Form/ArbitraryRebuildTest.php b/core/modules/system/src/Tests/Form/ArbitraryRebuildTest.php index 09eba7b..7cee618 100644 --- a/core/modules/system/src/Tests/Form/ArbitraryRebuildTest.php +++ b/core/modules/system/src/Tests/Form/ArbitraryRebuildTest.php @@ -7,7 +7,9 @@ namespace Drupal\system\Tests\Form; +use Drupal\field\Entity\FieldConfig; use Drupal\simpletest\WebTestBase; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests altering forms to be rebuilt so there are multiple steps. @@ -27,19 +29,19 @@ protected function setUp() { parent::setUp(); // Auto-create a field for testing. - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'entity_type' => 'user', 'field_name' => 'test_multiple', 'type' => 'text', 'cardinality' => -1, 'translatable' => FALSE, ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'entity_type' => 'user', 'field_name' => 'test_multiple', 'bundle' => 'user', 'label' => 'Test a multiple valued field', - ))->save(); + ])->save(); entity_get_form_display('user', 'user', 'register') ->setComponent('test_multiple', array( 'type' => 'text_textfield', diff --git a/core/modules/system/src/Tests/Form/ConfirmFormTest.php b/core/modules/system/src/Tests/Form/ConfirmFormTest.php index 92a5aa9..301cf78 100644 --- a/core/modules/system/src/Tests/Form/ConfirmFormTest.php +++ b/core/modules/system/src/Tests/Form/ConfirmFormTest.php @@ -84,7 +84,7 @@ public function testConfirmFormWithExternalDestination() { */ public function assertCancelLinkUrl(Url $url, $message = '', $group = 'Other') { $links = $this->xpath('//a[@href=:url]', [':url' => $url->toString()]); - $message = ($message ? $message : SafeMarkup::format('Cancel link with url %url found.', ['%url' => $url->toString()])); + $message = ($message ? $message : SafeMarkup::format('Cancel link with URL %url found.', ['%url' => $url->toString()])); return $this->assertTrue(isset($links[0]), $message, $group); } diff --git a/core/modules/system/src/Tests/Form/ElementsLabelsTest.php b/core/modules/system/src/Tests/Form/ElementsLabelsTest.php index 9a09442..360cb68 100644 --- a/core/modules/system/src/Tests/Form/ElementsLabelsTest.php +++ b/core/modules/system/src/Tests/Form/ElementsLabelsTest.php @@ -93,6 +93,12 @@ function testFormLabels() { $this->assertEqual($elements[0]['title'], 'Checkboxes test' . ' (' . t('Required') . ')', 'Title attribute found.'); $elements = $this->xpath('//div[@id="edit-form-radios-title-attribute"]'); $this->assertEqual($elements[0]['title'], 'Radios test' . ' (' . t('Required') . ')', 'Title attribute found.'); + + $elements = $this->xpath('//fieldset[@id="edit-form-checkboxes-title-invisible--wrapper"]/legend/span[contains(@class, "visually-hidden")]'); + $this->assertTrue(!empty($elements), "Title/Label not displayed when 'visually-hidden' attribute is set in checkboxes."); + + $elements = $this->xpath('//fieldset[@id="edit-form-radios-title-invisible--wrapper"]/legend/span[contains(@class, "visually-hidden")]'); + $this->assertTrue(!empty($elements), "Title/Label not displayed when 'visually-hidden' attribute is set in radios."); } /** @@ -122,4 +128,27 @@ function testFormDescriptions() { $this->assertTrue(isset($elements[0]), t('Properly renders the #description element visually-hidden.')); } + /** + * Test forms in theme-less environments. + */ + function testFormsInThemeLessEnvironments() { + $form = $this->getFormWithLimitedProperties(); + $render_service = $this->container->get('renderer'); + // This should not throw any notices. + $render_service->renderPlain($form); + } + + /** + * Return a form with element with not all properties defined. + */ + protected function getFormWithLimitedProperties() { + $form = array(); + + $form['fieldset'] = array( + '#type' => 'fieldset', + '#title' => 'Fieldset', + ); + + return $form; + } } diff --git a/core/modules/system/src/Tests/Form/ElementsTableSelectTest.php b/core/modules/system/src/Tests/Form/ElementsTableSelectTest.php index f9ce925..b400f3d 100644 --- a/core/modules/system/src/Tests/Form/ElementsTableSelectTest.php +++ b/core/modules/system/src/Tests/Form/ElementsTableSelectTest.php @@ -43,6 +43,27 @@ function testMultipleTrue() { } /** + * Test the presence of ajax functionality for all options. + */ + function testAjax() { + $rows = array('row1', 'row2', 'row3'); + // Test checkboxes (#multiple == TRUE). + foreach ($rows as $row) { + $element = 'tableselect[' . $row . ']'; + $edit = array($element => TRUE); + $result = $this->drupalPostAjaxForm('form_test/tableselect/multiple-true', $edit, $element); + $this->assertFalse(empty($result), t('Ajax triggers on checkbox for @row.', array('@row' => $row))); + } + // Test radios (#multiple == FALSE). + $element = 'tableselect'; + foreach ($rows as $row) { + $edit = array($element => $row); + $result = $this->drupalPostAjaxForm('form_test/tableselect/multiple-false', $edit, $element); + $this->assertFalse(empty($result), t('Ajax triggers on radio for @row.', array('@row' => $row))); + } + } + + /** * Test the display of radios when #multiple is FALSE. */ function testMultipleFalse() { diff --git a/core/modules/system/src/Tests/Form/FormTest.php b/core/modules/system/src/Tests/Form/FormTest.php index f0e5af0..3e6821f 100644 --- a/core/modules/system/src/Tests/Form/FormTest.php +++ b/core/modules/system/src/Tests/Form/FormTest.php @@ -16,6 +16,7 @@ use Drupal\form_test\Form\FormTestDisabledElementsForm; use Drupal\simpletest\WebTestBase; use Drupal\user\RoleInterface; +use Drupal\filter\Entity\FilterFormat; /** * Tests various form element validation mechanisms. @@ -34,7 +35,7 @@ class FormTest extends WebTestBase { protected function setUp() { parent::setUp(); - $filtered_html_format = entity_create('filter_format', array( + $filtered_html_format = FilterFormat::create(array( 'format' => 'filtered_html', 'name' => 'Filtered HTML', )); diff --git a/core/modules/system/src/Tests/Form/RebuildTest.php b/core/modules/system/src/Tests/Form/RebuildTest.php index 7674241..277b477 100644 --- a/core/modules/system/src/Tests/Form/RebuildTest.php +++ b/core/modules/system/src/Tests/Form/RebuildTest.php @@ -9,7 +9,9 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Url; +use Drupal\field\Entity\FieldConfig; use Drupal\simpletest\WebTestBase; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests functionality of \Drupal\Core\Form\FormBuilderInterface::rebuildForm(). @@ -73,17 +75,17 @@ function testRebuildPreservesValues() { function testPreserveFormActionAfterAJAX() { // Create a multi-valued field for 'page' nodes to use for Ajax testing. $field_name = 'field_ajax_test'; - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'field_name' => $field_name, 'entity_type' => 'node', 'type' => 'text', 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_name' => $field_name, 'entity_type' => 'node', 'bundle' => 'page', - ))->save(); + ])->save(); entity_get_form_display('node', 'page', 'default') ->setComponent($field_name, array('type' => 'text_textfield')) ->save(); diff --git a/core/modules/system/src/Tests/Form/RedirectTest.php b/core/modules/system/src/Tests/Form/RedirectTest.php index 6e70174..5dcc701 100644 --- a/core/modules/system/src/Tests/Form/RedirectTest.php +++ b/core/modules/system/src/Tests/Form/RedirectTest.php @@ -97,7 +97,7 @@ public function testRedirectFromErrorPages() { $this->assertResponse(404); $this->drupalPostForm(NULL, array(), t('Submit')); $this->assertResponse(200); - $this->assertUrl($expected, [], 'Redirected to correct url/query.'); + $this->assertUrl($expected, [], 'Redirected to correct URL/query.'); // Visit the block admin page (403 page) and submit the form. Verify it // ends up at the right URL. @@ -105,6 +105,6 @@ public function testRedirectFromErrorPages() { $this->assertResponse(403); $this->drupalPostForm(NULL, array(), t('Submit')); $this->assertResponse(200); - $this->assertUrl($expected, [], 'Redirected to correct url/query.'); + $this->assertUrl($expected, [], 'Redirected to correct URL/query.'); } } diff --git a/core/modules/system/src/Tests/HttpKernel/StackKernelIntegrationTest.php b/core/modules/system/src/Tests/HttpKernel/StackKernelIntegrationTest.php index 3096310..d976b37 100644 --- a/core/modules/system/src/Tests/HttpKernel/StackKernelIntegrationTest.php +++ b/core/modules/system/src/Tests/HttpKernel/StackKernelIntegrationTest.php @@ -31,8 +31,6 @@ class StackKernelIntegrationTest extends KernelTestBase { */ protected function setUp() { parent::setUp(); - - $this->installSchema('system', 'router'); \Drupal::service('router.builder')->rebuild(); } diff --git a/core/modules/system/src/Tests/Image/ToolkitGdTest.php b/core/modules/system/src/Tests/Image/ToolkitGdTest.php index a731f5b..1c61235 100644 --- a/core/modules/system/src/Tests/Image/ToolkitGdTest.php +++ b/core/modules/system/src/Tests/Image/ToolkitGdTest.php @@ -203,8 +203,8 @@ function testManipulations() { 'rotate_5' => array( 'function' => 'rotate', 'arguments' => array('degrees' => 5, 'background' => '#FF00FF'), // Fuchsia background. - 'width' => 42, - 'height' => 24, + 'width' => 41, + 'height' => 23, 'corners' => array_fill(0, 4, $this->fuchsia), ), 'rotate_90' => array( @@ -217,8 +217,8 @@ function testManipulations() { 'rotate_transparent_5' => array( 'function' => 'rotate', 'arguments' => array('degrees' => 5), - 'width' => 42, - 'height' => 24, + 'width' => 41, + 'height' => 23, 'corners' => array_fill(0, 4, $this->rotateTransparent), ), 'rotate_transparent_90' => array( @@ -289,20 +289,6 @@ function testManipulations() { $correct_dimensions_real = TRUE; $correct_dimensions_object = TRUE; - // PHP 5.5 GD bug: https://bugs.php.net/bug.php?id=65148. PHP 5.5 GD - // rotates differently then it did in PHP 5.4 resulting in different - // dimensions then what math teaches us. For the test images, the - // dimensions will be 1 pixel smaller in both dimensions (though other - // tests have shown a difference of 0 to 3 pixels in both dimensions. - // @todo: if and when the PHP bug gets solved, add an upper limit - // version check. - // @todo: in [#1551686] the dimension calculations for rotation are - // reworked. That issue should also check if these tests can be made - // more robust. - if (version_compare(PHP_VERSION, '5.5', '>=') && $values['function'] === 'rotate' && $values['arguments']['degrees'] % 90 != 0) { - $values['height']--; - $values['width']--; - } if (imagesy($toolkit->getResource()) != $values['height'] || imagesx($toolkit->getResource()) != $values['width']) { $correct_dimensions_real = FALSE; } diff --git a/core/modules/system/src/Tests/Image/ToolkitTest.php b/core/modules/system/src/Tests/Image/ToolkitTest.php index ea5bd5f..363fe08 100644 --- a/core/modules/system/src/Tests/Image/ToolkitTest.php +++ b/core/modules/system/src/Tests/Image/ToolkitTest.php @@ -21,6 +21,7 @@ function testGetAvailableToolkits() { $manager = $this->container->get('image.toolkit.manager'); $toolkits = $manager->getAvailableToolkits(); $this->assertTrue(isset($toolkits['test']), 'The working toolkit was returned.'); + $this->assertTrue(isset($toolkits['test:derived_toolkit']), 'The derived toolkit was returned.'); $this->assertFalse(isset($toolkits['broken']), 'The toolkit marked unavailable was not returned'); $this->assertToolkitOperationsCalled(array()); } @@ -71,4 +72,22 @@ function testApplyNoParameters() { $this->assertEqual($calls['apply'][0][0], 'my_operation', "'my_operation' was passed correctly as operation"); $this->assertEqual($calls['apply'][0][1], array(), 'passing no parameters was handled correctly'); } + + /** + * Tests image toolkit operations inheritance by derivative toolkits. + */ + public function testDerivative() { + $toolkit_manager = $this->container->get('image.toolkit.manager'); + $operation_manager = $this->container->get('image.toolkit.operation.manager'); + + $toolkit = $toolkit_manager->createInstance('test:derived_toolkit'); + + // Load an overwritten and an inherited operation. + $blur = $operation_manager->getToolkitOperation($toolkit, 'blur'); + $invert = $operation_manager->getToolkitOperation($toolkit, 'invert'); + + $this->assertIdentical('foo_derived', $blur->getPluginId(), "'Blur' operation overwritten by derivative."); + $this->assertIdentical('bar', $invert->getPluginId(), '"Invert" operation inherited from base plugin.'); + } + } diff --git a/core/modules/system/src/Tests/Installer/DistributionProfileTranslationQueryTest.php b/core/modules/system/src/Tests/Installer/DistributionProfileTranslationQueryTest.php new file mode 100644 index 0000000..9f68995 --- /dev/null +++ b/core/modules/system/src/Tests/Installer/DistributionProfileTranslationQueryTest.php @@ -0,0 +1,139 @@ +info = array( + 'type' => 'profile', + 'core' => \Drupal::CORE_COMPATIBILITY, + 'name' => 'Distribution profile', + 'distribution' => array( + 'name' => 'My Distribution', + 'langcode' => $this->langcode, + 'install' => array( + 'theme' => 'bartik', + ), + ), + ); + // File API functions are not available yet. + $path = $this->siteDirectory . '/profiles/mydistro'; + mkdir($path, 0777, TRUE); + file_put_contents("$path/mydistro.info.yml", Yaml::encode($this->info)); + + parent::setUp(); + } + + /** + * {@inheritdoc} + */ + protected function visitInstaller() { + // Place a custom local translation in the translations directory. + mkdir(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE); + file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', $this->getPo('de')); + file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.fr.po', $this->getPo('fr')); + + // Pass a different language code than the one set in the distribution + // profile. This distribution language should still be used. + // The unrouted URL assembler does not exist at this point, so we build the + // URL ourselves. + $this->drupalGet($GLOBALS['base_url'] . '/core/install.php' . '?langcode=fr'); + // The language should have been automatically detected, all following + // screens should be translated already. + $elements = $this->xpath('//input[@type="submit"]/@value'); + $this->assertEqual((string) current($elements), 'Save and continue de'); + $this->translations['Save and continue'] = 'Save and continue de'; + + // Check the language direction. + $direction = (string) current($this->xpath('/html/@dir')); + $this->assertEqual($direction, 'ltr'); + + // Verify that the distribution name appears. + $this->assertRaw($this->info['distribution']['name']); + // Verify that the requested theme is used. + $this->assertRaw($this->info['distribution']['install']['theme']); + // Verify that the "Choose profile" step does not appear. + $this->assertNoText('profile'); + } + + /** + * {@inheritdoc} + */ + protected function setUpLanguage() { + // This step is skipped, because the distribution profile uses a fixed + // language. + } + + /** + * {@inheritdoc} + */ + protected function setUpProfile() { + // This step is skipped, because there is a distribution profile. + } + + /** + * Confirms that the installation succeeded. + */ + public function testInstalled() { + $this->assertUrl('user/1'); + $this->assertResponse(200); + + // Confirm that we are logged-in after installation. + $this->assertText($this->rootUser->getDisplayName()); + + // Verify German was configured but not English. + $this->drupalGet('admin/config/regional/language'); + $this->assertText('German'); + $this->assertNoText('English'); + } + + /** + * Returns the string for the test .po file. + * + * @param string $langcode + * The language code. + * @return string + * Contents for the test .po file. + */ + protected function getPo($langcode) { + return <<info = array( + 'type' => 'profile', + 'core' => \Drupal::CORE_COMPATIBILITY, + 'name' => 'Distribution profile', + 'distribution' => array( + 'name' => 'My Distribution', + 'langcode' => $this->langcode, + 'install' => array( + 'theme' => 'bartik', + ), + ), + ); + // File API functions are not available yet. + $path = $this->siteDirectory . '/profiles/mydistro'; + mkdir($path, 0777, TRUE); + file_put_contents("$path/mydistro.info.yml", Yaml::encode($this->info)); + + parent::setUp(); + } + + /** + * {@inheritdoc} + */ + protected function visitInstaller() { + // Place a custom local translation in the translations directory. + mkdir(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE); + file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', $this->getPo('de')); + + parent::visitInstaller(); + + // The language should have been automatically detected, all following + // screens should be translated already. + $elements = $this->xpath('//input[@type="submit"]/@value'); + $this->assertEqual((string) current($elements), 'Save and continue de'); + $this->translations['Save and continue'] = 'Save and continue de'; + + // Check the language direction. + $direction = (string) current($this->xpath('/html/@dir')); + $this->assertEqual($direction, 'ltr'); + + // Verify that the distribution name appears. + $this->assertRaw($this->info['distribution']['name']); + // Verify that the requested theme is used. + $this->assertRaw($this->info['distribution']['install']['theme']); + // Verify that the "Choose profile" step does not appear. + $this->assertNoText('profile'); + } + + /** + * {@inheritdoc} + */ + protected function setUpLanguage() { + // This step is skipped, because the distribution profile uses a fixed + // language. + } + + /** + * {@inheritdoc} + */ + protected function setUpProfile() { + // This step is skipped, because there is a distribution profile. + } + + /** + * Confirms that the installation succeeded. + */ + public function testInstalled() { + $this->assertUrl('user/1'); + $this->assertResponse(200); + + // Confirm that we are logged-in after installation. + $this->assertText($this->rootUser->getDisplayName()); + + // Verify German was configured but not English. + $this->drupalGet('admin/config/regional/language'); + $this->assertText('German'); + $this->assertNoText('English'); + } + + /** + * Returns the string for the test .po file. + * + * @param string $langcode + * The language code. + * @return string + * Contents for the test .po file. + */ + protected function getPo($langcode) { + return <<drupalGet($GLOBALS['base_url'] . '/core/install.php'); + $this->visitInstaller(); $this->assertRaw('Drupal already installed'); // Delete settings.php and attempt to reinstall again. unlink($this->siteDirectory . '/settings.php'); - $this->drupalGet($GLOBALS['base_url'] . '/core/install.php'); + $this->visitInstaller(); $this->setUpLanguage(); $this->setUpProfile(); $this->setUpSettings(); diff --git a/core/modules/system/src/Tests/Installer/InstallerLanguagePageTest.php b/core/modules/system/src/Tests/Installer/InstallerLanguagePageTest.php index d469532..4d9383e 100644 --- a/core/modules/system/src/Tests/Installer/InstallerLanguagePageTest.php +++ b/core/modules/system/src/Tests/Installer/InstallerLanguagePageTest.php @@ -26,7 +26,7 @@ protected function setUpLanguage() { touch(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.xoxo.po'); // Check that all predefined languages show up with their native names. - $this->drupalGet($GLOBALS['base_url'] . '/core/install.php'); + $this->visitInstaller(); foreach (LanguageManager::getStandardLanguageList() as $langcode => $names) { $this->assertOption('edit-langcode', $langcode); $this->assertRaw('>' . $names[1] . '<'); diff --git a/core/modules/system/src/Tests/Installer/InstallerTest.php b/core/modules/system/src/Tests/Installer/InstallerTest.php index debb891..a2dd968 100644 --- a/core/modules/system/src/Tests/Installer/InstallerTest.php +++ b/core/modules/system/src/Tests/Installer/InstallerTest.php @@ -42,7 +42,7 @@ protected function setUpLanguage() { $this->assertRaw(''); // Assert that the expected title is present. - $this->assertEqual('Choose language', $this->cssSelect('main h1')[0]); + $this->assertEqual('Choose language', $this->cssSelect('main h2')[0]); parent::setUpLanguage(); } @@ -52,7 +52,9 @@ protected function setUpLanguage() { */ protected function setUpProfile() { // Assert that the expected title is present. - $this->assertEqual('Select an installation profile', $this->cssSelect('main h1')[0]); + $this->assertEqual('Select an installation profile', $this->cssSelect('main h2')[0]); + $result = $this->xpath('//span[contains(@class, :class) and contains(text(), :text)]', array(':class' => 'visually-hidden', ':text' => 'Select an installation profile')); + $this->assertEqual(count($result), 1, "Title/Label not displayed when '#title_display' => 'invisible' attribute is set"); parent::setUpProfile(); } @@ -62,7 +64,7 @@ protected function setUpProfile() { */ protected function setUpSettings() { // Assert that the expected title is present. - $this->assertEqual('Database configuration', $this->cssSelect('main h1')[0]); + $this->assertEqual('Database configuration', $this->cssSelect('main h2')[0]); parent::setUpSettings(); } @@ -72,7 +74,7 @@ protected function setUpSettings() { */ protected function setUpSite() { // Assert that the expected title is present. - $this->assertEqual('Configure site', $this->cssSelect('main h1')[0]); + $this->assertEqual('Configure site', $this->cssSelect('main h2')[0]); parent::setUpSite(); } diff --git a/core/modules/system/src/Tests/Installer/InstallerTranslationQueryTest.php b/core/modules/system/src/Tests/Installer/InstallerTranslationQueryTest.php new file mode 100644 index 0000000..dfa3d89 --- /dev/null +++ b/core/modules/system/src/Tests/Installer/InstallerTranslationQueryTest.php @@ -0,0 +1,90 @@ +siteDirectory . '/files/translations', 0777, TRUE); + file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', $this->getPo('de')); + + // The unrouted URL assembler does not exist at this point, so we build the + // URL ourselves. + $this->drupalGet($GLOBALS['base_url'] . '/core/install.php' . '?langcode=' . $this->langcode); + + // The language should have been automatically detected, all following + // screens should be translated already. + $elements = $this->xpath('//input[@type="submit"]/@value'); + $this->assertEqual((string) current($elements), 'Save and continue de'); + $this->translations['Save and continue'] = 'Save and continue de'; + + // Check the language direction. + $direction = (string) current($this->xpath('/html/@dir')); + $this->assertEqual($direction, 'ltr'); + } + + /** + * {@inheritdoc} + */ + protected function setUpLanguage() { + // The language was was preset by passing a query parameter in the URL, so + // no explicit language selection is necessary. + } + + /** + * Verifies the expected behaviors of the installation result. + */ + public function testInstaller() { + $this->assertUrl('user/1'); + $this->assertResponse(200); + + // Verify German was configured but not English. + $this->drupalGet('admin/config/regional/language'); + $this->assertText('German'); + $this->assertNoText('English'); + } + + /** + * Returns the string for the test .po file. + * + * @param string $langcode + * The language code. + * @return string + * Contents for the test .po file. + */ + protected function getPo($langcode) { + return <<installSchema('system', array('key_value_expire', 'flood', 'queue')); + $this->installSchema('system', array('key_value_expire')); } /** diff --git a/core/modules/system/src/Tests/KeyValueStore/KeyValueContentEntityStorageTest.php b/core/modules/system/src/Tests/KeyValueStore/KeyValueContentEntityStorageTest.php index 66fd3c2..cd09af9 100644 --- a/core/modules/system/src/Tests/KeyValueStore/KeyValueContentEntityStorageTest.php +++ b/core/modules/system/src/Tests/KeyValueStore/KeyValueContentEntityStorageTest.php @@ -10,6 +10,7 @@ use Drupal\Core\Entity\EntityMalformedException; use Drupal\Core\Entity\EntityStorageException; use Drupal\simpletest\KernelTestBase; +use Drupal\entity_test\Entity\EntityTestLabel; /** * Tests KeyValueEntityStorage for content entities. @@ -39,7 +40,7 @@ protected function setUp() { function testCRUD() { $default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId(); // Verify default properties on a newly created empty entity. - $empty = entity_create('entity_test_label'); + $empty = EntityTestLabel::create(); $this->assertIdentical($empty->id->value, NULL); $this->assertIdentical($empty->name->value, NULL); $this->assertTrue($empty->uuid->value); @@ -73,7 +74,7 @@ function testCRUD() { } // Verify that an entity with an empty ID string is considered empty, too. - $empty_id = entity_create('entity_test_label', array( + $empty_id = EntityTestLabel::create(array( 'id' => '', )); $this->assertIdentical($empty_id->isNew(), TRUE); @@ -86,7 +87,7 @@ function testCRUD() { } // Verify properties on a newly created entity. - $entity_test = entity_create('entity_test_label', $expected = array( + $entity_test = EntityTestLabel::create($expected = array( 'id' => $this->randomMachineName(), 'name' => $this->randomString(), )); @@ -129,7 +130,7 @@ function testCRUD() { // Ensure that creating an entity with the same id as an existing one is not // possible. - $same_id = entity_create('entity_test_label', array( + $same_id = EntityTestLabel::create(array( 'id' => $entity_test->id(), )); $this->assertIdentical($same_id->isNew(), TRUE); diff --git a/core/modules/system/src/Tests/Lock/LockUnitTest.php b/core/modules/system/src/Tests/Lock/LockUnitTest.php index ec249c5..b726add 100644 --- a/core/modules/system/src/Tests/Lock/LockUnitTest.php +++ b/core/modules/system/src/Tests/Lock/LockUnitTest.php @@ -24,17 +24,9 @@ class LockUnitTest extends KernelTestBase { */ protected $lock; - /** - * Modules to enable. - * - * @var array - */ - public static $modules = array('system'); - protected function setUp() { parent::setUp(); $this->lock = new DatabaseLockBackend($this->container->get('database')); - $this->installSchema('system', 'semaphore'); } /** diff --git a/core/modules/system/src/Tests/Menu/MenuLinkDefaultIntegrationTest.php b/core/modules/system/src/Tests/Menu/MenuLinkDefaultIntegrationTest.php index 6078875..d96b29c 100644 --- a/core/modules/system/src/Tests/Menu/MenuLinkDefaultIntegrationTest.php +++ b/core/modules/system/src/Tests/Menu/MenuLinkDefaultIntegrationTest.php @@ -28,14 +28,6 @@ class MenuLinkDefaultIntegrationTest extends KernelTestBase { ); /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - $this->installSchema('system', array('router')); - } - - /** * Tests moving a static menu link without a specified menu to the root. */ public function testMoveToRoot() { diff --git a/core/modules/system/src/Tests/Menu/MenuLinkTreeTest.php b/core/modules/system/src/Tests/Menu/MenuLinkTreeTest.php index 011b58f..04e366a 100644 --- a/core/modules/system/src/Tests/Menu/MenuLinkTreeTest.php +++ b/core/modules/system/src/Tests/Menu/MenuLinkTreeTest.php @@ -53,7 +53,7 @@ class MenuLinkTreeTest extends KernelTestBase { */ protected function setUp() { parent::setUp(); - $this->installSchema('system', array('router')); + \Drupal::service('router.builder')->rebuild(); $this->installEntitySchema('menu_link_content'); $this->linkTree = $this->container->get('menu.link_tree'); diff --git a/core/modules/system/src/Tests/Migrate/MigrateMenuTest.php b/core/modules/system/src/Tests/Migrate/MigrateMenuTest.php deleted file mode 100644 index 9dc42d4..0000000 --- a/core/modules/system/src/Tests/Migrate/MigrateMenuTest.php +++ /dev/null @@ -1,59 +0,0 @@ -executeMigration('menu'); - } - - /** - * Tests the Drupal 6 menu to Drupal 8 migration. - */ - public function testMenu() { - $navigation_menu = Menu::load('navigation'); - $this->assertIdentical('navigation', $navigation_menu->id()); - $this->assertIdentical('Navigation', $navigation_menu->label()); - $expected = <<assertIdentical($expected, $navigation_menu->getDescription()); - - // Test that we can re-import using the ConfigEntityBase destination. - Database::getConnection('default', 'migrate') - ->update('menu_custom') - ->fields(array('title' => 'Home Navigation')) - ->condition('menu_name', 'navigation') - ->execute(); - - $migration = Migration::load('menu'); - \Drupal::database() - ->truncate($migration->getIdMap()->mapTableName()) - ->execute(); - $this->executeMigration($migration); - - $navigation_menu = Menu::load('navigation'); - $this->assertIdentical('Home Navigation', $navigation_menu->label()); - } - -} diff --git a/core/modules/system/src/Tests/Migrate/d6/MigrateDateFormatTest.php b/core/modules/system/src/Tests/Migrate/d6/MigrateDateFormatTest.php deleted file mode 100644 index 28f90d7..0000000 --- a/core/modules/system/src/Tests/Migrate/d6/MigrateDateFormatTest.php +++ /dev/null @@ -1,64 +0,0 @@ -executeMigration('d6_date_formats'); - } - - /** - * Tests the Drupal 6 date formats to Drupal 8 migration. - */ - public function testDateFormats() { - $short_date_format = DateFormat::load('short'); - $this->assertIdentical('\S\H\O\R\T m/d/Y - H:i', $short_date_format->getPattern()); - - $medium_date_format = DateFormat::load('medium'); - $this->assertIdentical('\M\E\D\I\U\M D, m/d/Y - H:i', $medium_date_format->getPattern()); - - $long_date_format = DateFormat::load('long'); - $this->assertIdentical('\L\O\N\G l, F j, Y - H:i', $long_date_format->getPattern()); - - // Test that we can re-import using the EntityDateFormat destination. - Database::getConnection('default', 'migrate') - ->update('variable') - ->fields(array('value' => serialize('\S\H\O\R\T d/m/Y - H:i'))) - ->condition('name', 'date_format_short') - ->execute(); - - \Drupal::database() - ->truncate(Migration::load('d6_date_formats')->getIdMap()->mapTableName()) - ->execute(); - - $migration = \Drupal::entityManager() - ->getStorage('migration') - ->loadUnchanged('d6_date_formats'); - $this->executeMigration($migration); - - $short_date_format = DateFormat::load('short'); - $this->assertIdentical('\S\H\O\R\T d/m/Y - H:i', $short_date_format->getPattern()); - - } - -} diff --git a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemCronTest.php b/core/modules/system/src/Tests/Migrate/d6/MigrateSystemCronTest.php deleted file mode 100644 index 97c1170..0000000 --- a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemCronTest.php +++ /dev/null @@ -1,36 +0,0 @@ -executeMigration('d6_system_cron'); - } - - /** - * Tests migration of system (cron) variables to system.cron.yml. - */ - public function testSystemCron() { - $config = $this->config('system.cron'); - $this->assertIdentical(172800, $config->get('threshold.requirements_warning')); - $this->assertIdentical(1209600, $config->get('threshold.requirements_error')); - } - -} diff --git a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemDateTest.php b/core/modules/system/src/Tests/Migrate/d6/MigrateSystemDateTest.php deleted file mode 100644 index 28b6bcb..0000000 --- a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemDateTest.php +++ /dev/null @@ -1,37 +0,0 @@ -executeMigration('d6_system_date'); - } - - /** - * Tests migration of user variables to system_date.yml. - */ - public function testSystemDate() { - $config = $this->config('system.date'); - $this->assertIdentical(4, $config->get('first_day')); - $this->assertIdentical(FALSE, $config->get('timezone.user.configurable')); - $this->assertIdentical("Europe/Paris", $config->get('timezone.default')); - } - -} diff --git a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemFileTest.php b/core/modules/system/src/Tests/Migrate/d6/MigrateSystemFileTest.php deleted file mode 100644 index e0ffc34..0000000 --- a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemFileTest.php +++ /dev/null @@ -1,36 +0,0 @@ -executeMigration('d6_system_file'); - } - - /** - * Tests migration of system (file) variables to system.file.yml. - */ - public function testSystemFile() { - $config = \Drupal::configFactory()->getEditable('system.file'); - $this->assertIdentical('files/temp', $config->get('path.temporary')); - $this->assertIdentical(TRUE, $config->get('allow_insecure_uploads')); - } - -} diff --git a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemImageGdTest.php b/core/modules/system/src/Tests/Migrate/d6/MigrateSystemImageGdTest.php deleted file mode 100644 index bc41d52..0000000 --- a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemImageGdTest.php +++ /dev/null @@ -1,35 +0,0 @@ -executeMigration('d6_system_image_gd'); - } - - /** - * Tests migration of system (image GD) variables to system.image.gd.yml. - */ - public function testSystemImageGd() { - $config = $this->config('system.image.gd'); - $this->assertIdentical(75, $config->get('jpeg_quality')); - } - -} diff --git a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemImageTest.php b/core/modules/system/src/Tests/Migrate/d6/MigrateSystemImageTest.php deleted file mode 100644 index c297ad1..0000000 --- a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemImageTest.php +++ /dev/null @@ -1,35 +0,0 @@ -executeMigration('d6_system_image'); - } - - /** - * Tests migration of system (image) variables to system.image.yml. - */ - public function testSystemImage() { - $config = $this->config('system.image'); - $this->assertIdentical('gd', $config->get('toolkit')); - } - -} diff --git a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemLoggingTest.php b/core/modules/system/src/Tests/Migrate/d6/MigrateSystemLoggingTest.php deleted file mode 100644 index eba3d51..0000000 --- a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemLoggingTest.php +++ /dev/null @@ -1,39 +0,0 @@ -executeMigration('d6_system_logging'); - } - - /** - * Tests migration of system error_level variables to system.logging.yml. - */ - public function testSystemLogging() { - $config = $this->config('system.logging'); - $this->assertIdentical('some', $config->get('error_level')); - $this->assertConfigSchema(\Drupal::service('config.typed'), 'system.logging', $config->get()); - } - -} diff --git a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemMaintenanceTest.php b/core/modules/system/src/Tests/Migrate/d6/MigrateSystemMaintenanceTest.php deleted file mode 100644 index dd3372f..0000000 --- a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemMaintenanceTest.php +++ /dev/null @@ -1,35 +0,0 @@ -executeMigration('d6_system_maintenance'); - } - - /** - * Tests migration of system (maintenance) variables to system.maintenance.yml. - */ - public function testSystemMaintenance() { - $config = $this->config('system.maintenance'); - $this->assertIdentical('Drupal is currently under maintenance. We should be back shortly. Thank you for your patience.', $config->get('message')); - } - -} diff --git a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemPerformanceTest.php b/core/modules/system/src/Tests/Migrate/d6/MigrateSystemPerformanceTest.php deleted file mode 100644 index 6b2ebc2..0000000 --- a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemPerformanceTest.php +++ /dev/null @@ -1,37 +0,0 @@ -executeMigration('d6_system_performance'); - } - - /** - * Tests migration of system (Performance) variables to system.performance.yml. - */ - public function testSystemPerformance() { - $config = $this->config('system.performance'); - $this->assertIdentical(FALSE, $config->get('css.preprocess')); - $this->assertIdentical(FALSE, $config->get('js.preprocess')); - $this->assertIdentical(0, $config->get('cache.page.max_age')); - } - -} diff --git a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemRssTest.php b/core/modules/system/src/Tests/Migrate/d6/MigrateSystemRssTest.php deleted file mode 100644 index 3e59289..0000000 --- a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemRssTest.php +++ /dev/null @@ -1,36 +0,0 @@ -executeMigration('d6_system_rss'); - } - - /** - * Tests migration of system (rss) variables to system.rss.yml. - */ - public function testSystemRss() { - $config = $this->config('system.rss'); - $this->assertIdentical(10, $config->get('items.limit')); - $this->assertIdentical('title', $config->get('items.view_mode')); - } - -} diff --git a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemSiteTest.php b/core/modules/system/src/Tests/Migrate/d6/MigrateSystemSiteTest.php deleted file mode 100644 index 46596c9..0000000 --- a/core/modules/system/src/Tests/Migrate/d6/MigrateSystemSiteTest.php +++ /dev/null @@ -1,41 +0,0 @@ -executeMigration('d6_system_site'); - } - - /** - * Tests migration of system (site) variables to system.site.yml. - */ - public function testSystemSite() { - $config = $this->config('system.site'); - $this->assertIdentical('site_name', $config->get('name')); - $this->assertIdentical('site_mail@example.com', $config->get('mail')); - $this->assertIdentical('Migrate rocks', $config->get('slogan')); - $this->assertIdentical('/user', $config->get('page.403')); - $this->assertIdentical('/page-not-found', $config->get('page.404')); - $this->assertIdentical('/node', $config->get('page.front')); - $this->assertIdentical(FALSE, $config->get('admin_compact_mode')); - } - -} diff --git a/core/modules/system/src/Tests/Module/ExperimentalModuleTest.php b/core/modules/system/src/Tests/Module/ExperimentalModuleTest.php new file mode 100644 index 0000000..846e54b --- /dev/null +++ b/core/modules/system/src/Tests/Module/ExperimentalModuleTest.php @@ -0,0 +1,130 @@ +adminUser = $this->drupalCreateUser(['access administration pages', 'administer modules']); + $this->drupalLogin($this->adminUser); + } + + /** + * Tests installing experimental modules and dependencies in the UI. + */ + public function testExperimentalConfirmForm() { + + // First, test installing a non-experimental module with no dependencies. + // There should be no confirmation form and no experimental module warning. + $edit = []; + $edit["modules[Testing][test_page_test][enable]"] = TRUE; + $this->drupalPostForm('admin/modules', $edit, t('Install')); + $this->assertText('Module Test page has been enabled.'); + $this->assertNoText('Experimental modules are provided for testing purposes only.'); + + // Uninstall the module. + \Drupal::service('module_installer')->uninstall(['test_page_test']); + + // Next, test installing an experimental module with no dependencies. + // There should be a confirmation form with an experimental warning, but no + // list of dependencies. + $edit = []; + $edit["modules[Core (Experimental)][experimental_module_test][enable]"] = TRUE; + $this->drupalPostForm('admin/modules', $edit, 'Install'); + + // The module should not be enabled and there should be a warning and a + // list of the experimental modules with only this one. + $this->assertNoText('Experimental Test has been enabled.'); + $this->assertText('Experimental modules are provided for testing purposes only.'); + $this->assertText('The following modules are experimental: Experimental Test'); + + // There should be no message about enabling dependencies. + $this->assertNoText('You must enable'); + + // Enable the module and confirm that it worked. + $this->drupalPostForm(NULL, [], 'Continue'); + $this->assertText('Experimental Test has been enabled.'); + + // Uninstall the module. + \Drupal::service('module_installer')->uninstall(['experimental_module_test']); + + // Test enabling a module that is not itself experimental, but that depends + // on an experimental module. + $edit = []; + $edit["modules[Testing][experimental_module_dependency_test][enable]"] = TRUE; + $this->drupalPostForm('admin/modules', $edit, 'Install'); + + // The module should not be enabled and there should be a warning and a + // list of the experimental modules with only this one. + $this->assertNoText('2 modules have been enabled: Experimental Dependency Test, Experimental Test'); + $this->assertText('Experimental modules are provided for testing purposes only.'); + + $this->assertText('The following modules are experimental: Experimental Test'); + + // Ensure the non-experimental module is not listed as experimental. + $this->assertNoText('The following modules are experimental: Experimental Test, Experimental Dependency Test'); + $this->assertNoText('The following modules are experimental: Experimental Dependency Test'); + + // There should be a message about enabling dependencies. + $this->assertText('You must enable the Experimental Test module to install Experimental Dependency Test'); + + // Enable the module and confirm that it worked. + $this->drupalPostForm(NULL, [], 'Continue'); + $this->assertText('2 modules have been enabled: Experimental Dependency Test, Experimental Test'); + + // Uninstall the modules. + \Drupal::service('module_installer')->uninstall(['experimental_module_test', 'experimental_module_dependency_test']); + + // Finally, check both the module and its experimental dependency. There is + // still a warning about experimental modules, but no message about + // dependencies, since the user specifically enabled the dependency. + $edit = []; + $edit["modules[Core (Experimental)][experimental_module_test][enable]"] = TRUE; + $edit["modules[Testing][experimental_module_dependency_test][enable]"] = TRUE; + $this->drupalPostForm('admin/modules', $edit, 'Install'); + + // The module should not be enabled and there should be a warning and a + // list of the experimental modules with only this one. + $this->assertNoText('2 modules have been enabled: Experimental Dependency Test, Experimental Test'); + $this->assertText('Experimental modules are provided for testing purposes only.'); + + $this->assertText('The following modules are experimental: Experimental Test'); + + // Ensure the non-experimental module is not listed as experimental. + $this->assertNoText('The following modules are experimental: Experimental Test, Experimental Dependency Test'); + $this->assertNoText('The following modules are experimental: Experimental Dependency Test'); + + // There should be no message about enabling dependencies. + $this->assertNoText('You must enable'); + + // Enable the module and confirm that it worked. + $this->drupalPostForm(NULL, [], 'Continue'); + $this->assertText('2 modules have been enabled: Experimental Test, Experimental Dependency Test'); + + } + +} diff --git a/core/modules/system/src/Tests/Module/InstallUninstallTest.php b/core/modules/system/src/Tests/Module/InstallUninstallTest.php index f50c68a..cfaa7e4 100644 --- a/core/modules/system/src/Tests/Module/InstallUninstallTest.php +++ b/core/modules/system/src/Tests/Module/InstallUninstallTest.php @@ -100,9 +100,24 @@ public function testInstallUninstall() { $edit["modules[$package][$name][enable]"] = TRUE; $this->drupalPostForm('admin/modules', $edit, t('Install')); + // Handle experimental modules, which require a confirmation screen. + if ($package == 'Core (Experimental)') { + $this->assertText('Are you sure you wish to enable experimental modules?'); + if (count($modules_to_install) > 1) { + // When there are experimental modules, needed dependencies do not + // result in the same page title, but there will be expected text + // indicating they need to be enabled. + $this->assertText('You must enable'); + } + $this->drupalPostForm(NULL, array(), t('Continue')); + } // Handle the case where modules were installed along with this one and // where we therefore hit a confirmation screen. - if (count($modules_to_install) > 1) { + elseif (count($modules_to_install) > 1) { + // Verify that we are on the correct form and that the expected text + // about enabling dependencies appears. + $this->assertText('Some required modules must be enabled'); + $this->assertText('You must enable'); $this->drupalPostForm(NULL, array(), t('Continue')); } @@ -178,10 +193,21 @@ public function testInstallUninstall() { // - That enabling more than one module at the same time does not lead to // any errors. $edit = array(); + $experimental = FALSE; foreach ($all_modules as $name => $module) { $edit['modules[' . $module->info['package'] . '][' . $name . '][enable]'] = TRUE; + // Track whether there is at least one experimental module. + if ($module->info['package'] == 'Core (Experimental)') { + $experimental = TRUE; + } } $this->drupalPostForm('admin/modules', $edit, t('Install')); + + // If there are experimental modules, click the confirm form. + if ($experimental) { + $this->assertText('Are you sure you wish to enable experimental modules?'); + $this->drupalPostForm(NULL, array(), t('Continue')); + } $this->assertText(t('@count modules have been enabled: ', array('@count' => count($all_modules))), 'Modules status has been updated.'); } diff --git a/core/modules/system/src/Tests/Module/UninstallTest.php b/core/modules/system/src/Tests/Module/UninstallTest.php index 253ea18..8c91d8b 100644 --- a/core/modules/system/src/Tests/Module/UninstallTest.php +++ b/core/modules/system/src/Tests/Module/UninstallTest.php @@ -10,6 +10,8 @@ use Drupal\Core\Cache\Cache; use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Entity\EntityMalformedException; +use Drupal\node\Entity\Node; +use Drupal\node\Entity\NodeType; use Drupal\simpletest\WebTestBase; /** @@ -46,12 +48,15 @@ function testUninstallPage() { $this->drupalLogin($account); // Create a node type. - $node_type = entity_create('node_type', array('type' => 'uninstall_blocker', 'name' => 'Uninstall blocker')); + $node_type = NodeType::create(['type' => 'uninstall_blocker', 'name' => 'Uninstall blocker']); // Create a dependency that can be fixed. $node_type->setThirdPartySetting('module_test', 'key', 'value'); $node_type->save(); // Add a node to prevent node from being uninstalled. - $node = entity_create('node', array('type' => 'uninstall_blocker', 'title' => $this->randomString())); + $node = Node::create([ + 'type' => 'uninstall_blocker', + 'title' => $this->randomString(), + ]); $node->save(); $this->drupalGet('admin/modules/uninstall'); diff --git a/core/modules/system/src/Tests/Path/UrlAliasFixtures.php b/core/modules/system/src/Tests/Path/UrlAliasFixtures.php index 92438aa..e0545e6 100644 --- a/core/modules/system/src/Tests/Path/UrlAliasFixtures.php +++ b/core/modules/system/src/Tests/Path/UrlAliasFixtures.php @@ -8,6 +8,7 @@ namespace Drupal\system\Tests\Path; use Drupal\Core\Database\Connection; +use Drupal\Core\Path\AliasStorage; /** * Utility methods to generate sample data, database configuration, etc. @@ -92,7 +93,7 @@ public function tableDefinition() { module_load_install('system'); $schema = system_schema(); - $tables['url_alias'] = $schema['url_alias']; + $tables['url_alias'] = AliasStorage::schemaDefinition(); $tables['key_value'] = $schema['key_value']; return $tables; diff --git a/core/modules/system/src/Tests/Path/UrlAlterFunctionalTest.php b/core/modules/system/src/Tests/Path/UrlAlterFunctionalTest.php index 71915ac..7d576bc 100644 --- a/core/modules/system/src/Tests/Path/UrlAlterFunctionalTest.php +++ b/core/modules/system/src/Tests/Path/UrlAlterFunctionalTest.php @@ -8,6 +8,7 @@ namespace Drupal\system\Tests\Path; use Drupal\simpletest\WebTestBase; +use Drupal\taxonomy\Entity\Term; /** * Tests altering the inbound path and the outbound path. @@ -64,10 +65,10 @@ function testUrlAlter() { $this->assertUrlOutboundAlter('/forum', '/community'); $forum_vid = $this->config('forum.settings')->get('vocabulary'); $term_name = $this->randomMachineName(); - $term = entity_create('taxonomy_term', array( + $term = Term::create([ 'name' => $term_name, 'vid' => $forum_vid, - )); + ]); $term->save(); $this->drupalGet("community/" . $term->id()); $this->assertText($term_name, 'The community/{tid} path gets resolved correctly'); diff --git a/core/modules/system/src/Tests/Plugin/Condition/RequestPathTest.php b/core/modules/system/src/Tests/Plugin/Condition/RequestPathTest.php index 5d80493..7a53844 100644 --- a/core/modules/system/src/Tests/Plugin/Condition/RequestPathTest.php +++ b/core/modules/system/src/Tests/Plugin/Condition/RequestPathTest.php @@ -62,7 +62,7 @@ class RequestPathTest extends KernelTestBase { protected function setUp() { parent::setUp(); - $this->installSchema('system', array('sequences', 'url_alias')); + $this->installSchema('system', array('sequences')); $this->pluginManager = $this->container->get('plugin.manager.condition'); diff --git a/core/modules/system/src/Tests/Plugin/ContextPluginTest.php b/core/modules/system/src/Tests/Plugin/ContextPluginTest.php index 05c4c7e..4a9b73a 100644 --- a/core/modules/system/src/Tests/Plugin/ContextPluginTest.php +++ b/core/modules/system/src/Tests/Plugin/ContextPluginTest.php @@ -9,9 +9,11 @@ use Drupal\Component\Plugin\Exception\ContextException; use Drupal\Core\Plugin\Context\ContextDefinition; +use Drupal\node\Entity\Node; use Drupal\node\Entity\NodeType; use Drupal\plugin_test\Plugin\MockBlockManager; use Drupal\simpletest\KernelTestBase; +use Drupal\user\Entity\User; /** * Tests that contexts are properly set and working within plugins. @@ -36,7 +38,7 @@ function testContext() { $manager = new MockBlockManager(); $plugin = $manager->createInstance('user_name'); // Create a node, add it as context, catch the exception. - $node = entity_create('node', array('title' => $name, 'type' => 'page')); + $node = Node::create(['type' => 'page', 'title' => $name]); // Try to get context that is missing its definition. try { @@ -72,7 +74,7 @@ function testContext() { // Set an appropriate context value and check to make sure its methods work // as expected. - $user = entity_create('user', array('name' => $name)); + $user = User::create(['name' => $name]); $plugin->setContextValue('user', $user); $this->assertEqual($plugin->getContextValue('user')->getUsername(), $user->getUsername()); diff --git a/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php b/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php index 6cca8f5..d038dbf 100644 --- a/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php +++ b/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php @@ -57,12 +57,21 @@ protected function setUp() { 'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Orange', 'provider' => 'plugin_test', ), + 'big_apple' => array( + 'id' => 'big_apple', + 'label' => 'Big Apple', + 'color' => 'green', + 'class' => 'Drupal\plugin_test_extended\Plugin\plugin_test\fruit\BigApple', + 'provider' => 'plugin_test_extended', + ), ); $base_directory = \Drupal::root() . '/core/modules/system/tests/modules/plugin_test/src'; - $namespaces = new \ArrayObject(array('Drupal\plugin_test' => $base_directory)); + $base_directory2 = \Drupal::root() . '/core/modules/system/tests/modules/plugin_test_extended/src'; + $namespaces = new \ArrayObject(array('Drupal\plugin_test' => $base_directory, 'Drupal\plugin_test_extended' => $base_directory2)); - $this->discovery = new AnnotatedClassDiscovery('Plugin/plugin_test/fruit', $namespaces); + $annotation_namespaces = ['Drupal\plugin_test\Plugin\Annotation', 'Drupal\plugin_test_extended\Plugin\Annotation']; + $this->discovery = new AnnotatedClassDiscovery('Plugin/plugin_test/fruit', $namespaces, 'Drupal\Component\Annotation\Plugin', $annotation_namespaces); $this->emptyDiscovery = new AnnotatedClassDiscovery('Plugin/non_existing_module/non_existing_plugin_type', $namespaces); } diff --git a/core/modules/system/src/Tests/Queue/QueueSerializationTest.php b/core/modules/system/src/Tests/Queue/QueueSerializationTest.php index 638b3f5..4a35cce 100644 --- a/core/modules/system/src/Tests/Queue/QueueSerializationTest.php +++ b/core/modules/system/src/Tests/Queue/QueueSerializationTest.php @@ -81,7 +81,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { */ protected function setUp() { parent::setUp(); - $this->installSchema('system', ['key_value_expire', 'sequences', 'queue']); + $this->installSchema('system', ['key_value_expire', 'sequences']); $this->installEntitySchema('user'); $this->queue = \Drupal::service('queue.database')->get('aggregator_refresh'); $test_user = User::create(array( diff --git a/core/modules/system/src/Tests/Queue/QueueTest.php b/core/modules/system/src/Tests/Queue/QueueTest.php index ab1b083..a1ab45e 100644 --- a/core/modules/system/src/Tests/Queue/QueueTest.php +++ b/core/modules/system/src/Tests/Queue/QueueTest.php @@ -20,17 +20,9 @@ class QueueTest extends KernelTestBase { /** - * The modules to enable. - * - * @var array - */ - public static $modules = array('system'); - - /** * Tests the System queue. */ public function testSystemQueue() { - $this->installSchema('system', 'queue'); // Create two queues. $queue1 = new DatabaseQueue($this->randomMachineName(), Database::getConnection()); $queue1->createQueue(); diff --git a/core/modules/system/src/Tests/Render/Element/TableTest.php b/core/modules/system/src/Tests/Render/Element/TableTest.php index 7d94cd9..827b55a 100644 --- a/core/modules/system/src/Tests/Render/Element/TableTest.php +++ b/core/modules/system/src/Tests/Render/Element/TableTest.php @@ -29,7 +29,6 @@ class TableTest extends KernelTestBase { protected function setUp() { parent::setUp(); - $this->installSchema('system', 'router'); \Drupal::service('router.builder')->rebuild(); } @@ -98,7 +97,7 @@ function testThemeTableWithEmptyMessage() { // Enable the Classy theme. \Drupal::service('theme_handler')->install(['classy']); - $this->config('system.theme')->set('default', 'classy')->save(); + \Drupal::service('theme_handler')->setDefault('classy'); $this->render($table); $this->removeWhiteSpace(); diff --git a/core/modules/system/src/Tests/RouteProcessor/RouteNoneTest.php b/core/modules/system/src/Tests/RouteProcessor/RouteNoneTest.php index 551e4d8..f029156 100644 --- a/core/modules/system/src/Tests/RouteProcessor/RouteNoneTest.php +++ b/core/modules/system/src/Tests/RouteProcessor/RouteNoneTest.php @@ -42,7 +42,6 @@ class RouteNoneTest extends KernelTestBase { protected function setUp() { parent::setUp(); - $this->installSchema('system', ['router']); \Drupal::service('router.builder')->rebuild(); $this->urlGenerator = \Drupal::urlGenerator(); diff --git a/core/modules/system/src/Tests/RouteProcessor/RouteProcessorCurrentIntegrationTest.php b/core/modules/system/src/Tests/RouteProcessor/RouteProcessorCurrentIntegrationTest.php index bc341a6..8cdb9c7 100644 --- a/core/modules/system/src/Tests/RouteProcessor/RouteProcessorCurrentIntegrationTest.php +++ b/core/modules/system/src/Tests/RouteProcessor/RouteProcessorCurrentIntegrationTest.php @@ -41,7 +41,6 @@ class RouteProcessorCurrentIntegrationTest extends KernelTestBase { protected function setUp() { parent::setUp(); - $this->installSchema('system', ['router']); \Drupal::service('router.builder')->rebuild(); $this->urlGenerator = \Drupal::urlGenerator(); diff --git a/core/modules/system/src/Tests/Routing/ContentNegotiationRoutingTest.php b/core/modules/system/src/Tests/Routing/ContentNegotiationRoutingTest.php index 98515b8..71d3fe2 100644 --- a/core/modules/system/src/Tests/Routing/ContentNegotiationRoutingTest.php +++ b/core/modules/system/src/Tests/Routing/ContentNegotiationRoutingTest.php @@ -31,7 +31,6 @@ protected function setUp() { \Drupal::unsetContainer(); parent::setUp(); - $this->installSchema('system', ['router', 'url_alias']); \Drupal::service('router.builder')->rebuild(); } diff --git a/core/modules/system/src/Tests/Routing/DestinationTest.php b/core/modules/system/src/Tests/Routing/DestinationTest.php index a590d62..ce72f7b 100644 --- a/core/modules/system/src/Tests/Routing/DestinationTest.php +++ b/core/modules/system/src/Tests/Routing/DestinationTest.php @@ -74,7 +74,7 @@ public function testDestination() { // Make sure that 404 pages do not populate $_GET['destination'] with // external URLs. - \Drupal::configFactory()->getEditable('system.site')->set('page.404', 'system-test/get-destination')->save(); + \Drupal::configFactory()->getEditable('system.site')->set('page.404', '/system-test/get-destination')->save(); $this->drupalGet('http://example.com', ['external' => FALSE]); $this->assertResponse(404); $this->assertIdentical(Url::fromRoute('')->toString(), $this->getRawContent(), 'External URL is not allowed on 404 pages.'); diff --git a/core/modules/system/src/Tests/Routing/ExceptionHandlingTest.php b/core/modules/system/src/Tests/Routing/ExceptionHandlingTest.php index ee3b6eb..a404916 100644 --- a/core/modules/system/src/Tests/Routing/ExceptionHandlingTest.php +++ b/core/modules/system/src/Tests/Routing/ExceptionHandlingTest.php @@ -30,7 +30,7 @@ class ExceptionHandlingTest extends KernelTestBase { protected function setUp() { parent::setUp(); - $this->installSchema('system', ['router']); + $this->installEntitySchema('date_format'); \Drupal::service('router.builder')->rebuild(); } @@ -99,6 +99,42 @@ public function testHtml404() { } /** + * Tests that the exception response is executed in the original context. + */ + public function testExceptionResponseGeneratedForOriginalRequest() { + // Test with 404 path pointing to a route that uses '_controller'. + $response = $this->doTest404Route('/router_test/test25'); + $this->assertTrue(strpos($response->getContent(), '/not-found') !== FALSE); + + // Test with 404 path pointing to a route that uses '_form'. + $response = $this->doTest404Route('/router_test/test26'); + $this->assertTrue(strpos($response->getContent(), '
                          doTest404Route('/router_test/test27'); + $this->assertTrue(strpos($response->getContent(), 'config('system.site')->set('page.404', $path)->save(); + + $request = Request::create('/not-found'); + $request->setFormat('html', ['text/html']); + + /** @var \Symfony\Component\HttpKernel\HttpKernelInterface $kernel */ + $kernel = \Drupal::getContainer()->get('http_kernel'); + return $kernel->handle($request)->prepare($request); + } + + /** * Tests if exception backtraces are properly escaped when output to HTML. */ public function testBacktraceEscaping() { diff --git a/core/modules/system/src/Tests/Routing/RouteProviderTest.php b/core/modules/system/src/Tests/Routing/RouteProviderTest.php index 81c63b1..056a37a 100644 --- a/core/modules/system/src/Tests/Routing/RouteProviderTest.php +++ b/core/modules/system/src/Tests/Routing/RouteProviderTest.php @@ -86,8 +86,6 @@ protected function setUp() { $this->cache = new MemoryBackend('data'); $this->pathProcessor = \Drupal::service('path_processor_manager'); $this->cacheTagsInvalidator = \Drupal::service('cache_tags.invalidator'); - - $this->installSchema('system', 'url_alias'); } /** diff --git a/core/modules/system/src/Tests/Routing/UrlIntegrationTest.php b/core/modules/system/src/Tests/Routing/UrlIntegrationTest.php index 66239c9..1d3f934 100644 --- a/core/modules/system/src/Tests/Routing/UrlIntegrationTest.php +++ b/core/modules/system/src/Tests/Routing/UrlIntegrationTest.php @@ -27,15 +27,6 @@ class UrlIntegrationTest extends KernelTestBase { public static $modules = array('user', 'router_test', 'system'); /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - - $this->installSchema('system', ['router']); - } - - /** * Ensures that the access() method on \Drupal\Core\Url objects works. */ public function testAccess() { diff --git a/core/modules/system/src/Tests/System/AccessDeniedTest.php b/core/modules/system/src/Tests/System/AccessDeniedTest.php index b068802..db231c3 100644 --- a/core/modules/system/src/Tests/System/AccessDeniedTest.php +++ b/core/modules/system/src/Tests/System/AccessDeniedTest.php @@ -23,7 +23,7 @@ class AccessDeniedTest extends WebTestBase { * * @var array */ - public static $modules = ['block']; + public static $modules = ['block', 'node', 'system_test']; protected $adminUser; @@ -34,6 +34,8 @@ protected function setUp() { // Create an administrative user. $this->adminUser = $this->drupalCreateUser(['access administration pages', 'administer site configuration', 'link to any page', 'administer blocks']); + $this->adminUser->roles[] = 'administrator'; + $this->adminUser->save(); user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, array('access user profiles')); user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, array('access user profiles')); @@ -44,6 +46,14 @@ function testAccessDenied() { $this->assertText(t('Access denied'), 'Found the default 403 page'); $this->assertResponse(403); + // Ensure that users without permission are denied access and have the + // correct path information in drupalSettings. + $this->drupalLogin($this->createUser([])); + $this->drupalGet('admin', ['query' => ['foo' => 'bar']]); + $this->assertEqual($this->drupalSettings['path']['currentPath'], 'admin'); + $this->assertEqual($this->drupalSettings['path']['currentPathIsAdmin'], TRUE); + $this->assertEqual($this->drupalSettings['path']['currentQuery'], ['foo' => 'bar']); + $this->drupalLogin($this->adminUser); // Set a custom 404 page without a starting slash. @@ -99,6 +109,29 @@ function testAccessDenied() { $this->drupalPostForm('admin/config/system/site-information', $edit, t('Log in')); // Check that we're still on the same page. - $this->assertText(t('Site information')); + $this->assertText(t('Basic site settings')); } + + /** + * Tests that an inaccessible custom 403 page falls back to the default. + */ + public function testAccessDeniedCustomPageWithAccessDenied() { + // Sets up a 403 page not accessible by the anonymous user. + $this->config('system.site')->set('page.403', '/system-test/custom-4xx')->save(); + + $this->drupalGet('/system-test/always-denied'); + $this->assertNoText('Admin-only 4xx response'); + $this->assertText('You are not authorized to access this page.'); + $this->assertResponse(403); + // Verify the access cacheability metadata for custom 403 is bubbled. + $this->assertCacheContext('user.roles'); + + $this->drupalLogin($this->adminUser); + $this->drupalGet('/system-test/always-denied'); + $this->assertText('Admin-only 4xx response'); + $this->assertResponse(403); + // Verify the access cacheability metadata for custom 403 is bubbled. + $this->assertCacheContext('user.roles'); + } + } diff --git a/core/modules/system/src/Tests/System/CronQueueTest.php b/core/modules/system/src/Tests/System/CronQueueTest.php index b11e9c2..dac042b 100644 --- a/core/modules/system/src/Tests/System/CronQueueTest.php +++ b/core/modules/system/src/Tests/System/CronQueueTest.php @@ -61,5 +61,13 @@ public function testExceptions() { $this->assertEqual($item->data, 'crash', 'Failing item remains in the queue.'); $item = $queue->claimItem(); $this->assertEqual($item->data, 'ignored', 'Item beyond the failing item remains in the queue.'); + + // Test the requeueing functionality. + $queue = $this->container->get('queue')->get('cron_queue_test_requeue_exception'); + $queue->createItem([]); + $this->cronRun(); + $this->assertEqual(\Drupal::state()->get('cron_queue_test_requeue_exception'), 2); + $this->assertFalse($queue->numberOfItems()); } + } diff --git a/core/modules/system/src/Tests/System/DateTimeTest.php b/core/modules/system/src/Tests/System/DateTimeTest.php index b2a5d2f..bb69dcf 100644 --- a/core/modules/system/src/Tests/System/DateTimeTest.php +++ b/core/modules/system/src/Tests/System/DateTimeTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\System; +use Drupal\Core\Datetime\Entity\DateFormat; use Drupal\Core\Url; use Drupal\simpletest\WebTestBase; @@ -135,7 +136,7 @@ function testDateFormatConfiguration() { $this->assertText($name, 'Custom date format appears in the date format list.'); $this->assertText(t('Delete'), 'Delete link for custom date format appears.'); - $date_format = entity_create('date_format', array( + $date_format = DateFormat::create(array( 'id' => 'xss_short', 'label' => 'XSS format', 'pattern' => '\<\s\c\r\i\p\t\>\a\l\e\r\t\(\'\X\S\S\'\)\;\<\/\s\c\r\i\p\t\>', diff --git a/core/modules/system/src/Tests/System/PageNotFoundTest.php b/core/modules/system/src/Tests/System/PageNotFoundTest.php index b7c0609..ffe985b 100644 --- a/core/modules/system/src/Tests/System/PageNotFoundTest.php +++ b/core/modules/system/src/Tests/System/PageNotFoundTest.php @@ -17,6 +17,14 @@ * @group system */ class PageNotFoundTest extends WebTestBase { + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = ['system_test']; + protected $adminUser; protected function setUp() { @@ -24,6 +32,8 @@ protected function setUp() { // Create an administrative user. $this->adminUser = $this->drupalCreateUser(array('administer site configuration', 'link to any page')); + $this->adminUser->roles[] = 'administrator'; + $this->adminUser->save(); user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, array('access user profiles')); user_role_grant_permissions(RoleInterface::AUTHENTICATED_ID, array('access user profiles')); @@ -50,4 +60,28 @@ function testPageNotFound() { $this->drupalGet($this->randomMachineName(10)); $this->assertText($this->adminUser->getUsername(), 'Found the custom 404 page'); } + + /** + * Tests that an inaccessible custom 404 page falls back to the default. + */ + public function testPageNotFoundCustomPageWithAccessDenied() { + // Sets up a 404 page not accessible by the anonymous user. + $this->config('system.site')->set('page.404', '/system-test/custom-4xx')->save(); + + $this->drupalGet('/this-path-does-not-exist'); + $this->assertNoText('Admin-only 4xx response'); + $this->assertText('The requested page could not be found.'); + $this->assertResponse(404); + // Verify the access cacheability metadata for custom 404 is bubbled. + $this->assertCacheContext('user.roles'); + + $this->drupalLogin($this->adminUser); + $this->drupalGet('/this-path-does-not-exist'); + $this->assertText('Admin-only 4xx response'); + $this->assertNoText('The requested page could not be found.'); + $this->assertResponse(404); + // Verify the access cacheability metadata for custom 404 is bubbled. + $this->assertCacheContext('user.roles'); + } + } diff --git a/core/modules/system/src/Tests/System/SitesDirectoryHardeningTest.php b/core/modules/system/src/Tests/System/SitesDirectoryHardeningTest.php new file mode 100644 index 0000000..86ba0a2 --- /dev/null +++ b/core/modules/system/src/Tests/System/SitesDirectoryHardeningTest.php @@ -0,0 +1,115 @@ +kernel->getSitePath(); + $settings_file = $this->settingsFile($site_path); + + // First, we check based on what the initial install has set. + $this->assertTrue(drupal_verify_install_file($site_path, FILE_NOT_WRITABLE, 'dir'), new FormattableMarkup('Verified permissions for @file.', array('@file' => $site_path))); + + // We intentionally don't check for settings.local.php as that file is + // not created by Drupal. + $this->assertTrue(drupal_verify_install_file($settings_file, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE), new FormattableMarkup('Verified permissions for @file.', array('@file' => $settings_file))); + + $this->makeWritable($site_path); + $this->checkSystemRequirements(); + + $this->assertTrue(drupal_verify_install_file($site_path, FILE_NOT_WRITABLE, 'dir'), new FormattableMarkup('Verified permissions for @file after manual permissions change.', array('@file' => $site_path))); + $this->assertTrue(drupal_verify_install_file($settings_file, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE), new FormattableMarkup('Verified permissions for @file after manual permissions change.', array('@file' => $settings_file))); + } + + /** + * Tests writable files remain writable when directory hardening is disabled. + */ + public function testSitesDirectoryHardeningConfig() { + $site_path = $this->kernel->getSitePath(); + $settings_file = $this->settingsFile($site_path); + + // Disable permissions enforcement. + $settings = Settings::getAll(); + $settings['skip_permissions_hardening'] = TRUE; + new Settings($settings); + $this->assertTrue(Settings::get('skip_permissions_hardening'), 'Able to set hardening to true'); + $this->makeWritable($site_path); + + // Manually trigger the requirements check. + $requirements = $this->checkSystemRequirements(); + $this->assertEqual(REQUIREMENT_WARNING, $requirements['configuration_files']['severity'], 'Warning severity is properly set.'); + $this->assertEqual($this->t('Protection disabled'), (string) $requirements['configuration_files']['description']['#context']['configuration_error_list']['#items'][0], 'Description is properly set.'); + + $this->assertTrue(is_writable($site_path), 'Site directory remains writable when automatically fixing permissions is disabled.'); + $this->assertTrue(is_writable($settings_file), 'settings.php remains writable when automatically fixing permissions is disabled.'); + + // Re-enable permissions enforcement. + $settings = Settings::getAll(); + $settings['skip_permissions_hardening'] = FALSE; + new Settings($settings); + + // Manually trigger the requirements check. + $this->checkSystemRequirements(); + + $this->assertFalse(is_writable($site_path), 'Site directory is protected when automatically fixing permissions is enabled.'); + $this->assertFalse(is_writable($settings_file), 'settings.php is protected when automatically fixing permissions is enabled.'); + } + + /** + * Checks system runtime requirements. + * + * @return array + * An array of system requirements. + */ + protected function checkSystemRequirements() { + module_load_install('system'); + return system_requirements('runtime'); + } + + /** + * Makes the given path and settings file writable. + * + * @param string $site_path + * The sites directory path, such as 'sites/default'. + */ + protected function makeWritable($site_path) { + chmod($site_path, 0755); + chmod($this->settingsFile($site_path), 0644); + } + + /** + * Returns the path to settings.php + * + * @param string $site_path + * The sites subdirectory path. + * + * @return string + * The path to settings.php. + */ + protected function settingsFile($site_path) { + $settings_file = $site_path . '/settings.php'; + return $settings_file; + } +} diff --git a/core/modules/system/src/Tests/System/TokenReplaceUnitTest.php b/core/modules/system/src/Tests/System/TokenReplaceUnitTest.php index b8ac4f3..64517b7 100644 --- a/core/modules/system/src/Tests/System/TokenReplaceUnitTest.php +++ b/core/modules/system/src/Tests/System/TokenReplaceUnitTest.php @@ -85,8 +85,6 @@ public function testClear() { * Tests the generation of all system site information tokens. */ public function testSystemSiteTokenReplacement() { - // The use of the \Drupal::url() method requires the url_alias table to exist. - $this->installSchema('system', 'url_alias'); $url_options = array( 'absolute' => TRUE, 'language' => $this->interfaceLanguage, diff --git a/core/modules/system/src/Tests/System/TokenReplaceUnitTestBase.php b/core/modules/system/src/Tests/System/TokenReplaceUnitTestBase.php index 95ed3f5..8a22de7 100644 --- a/core/modules/system/src/Tests/System/TokenReplaceUnitTestBase.php +++ b/core/modules/system/src/Tests/System/TokenReplaceUnitTestBase.php @@ -39,7 +39,6 @@ protected function setUp() { parent::setUp(); // Install default system configuration. $this->installConfig(array('system')); - $this->installSchema('system', array('router')); \Drupal::service('router.builder')->rebuild(); $this->interfaceLanguage = \Drupal::languageManager()->getCurrentLanguage(); diff --git a/core/modules/system/src/Tests/Theme/EntityFilteringThemeTest.php b/core/modules/system/src/Tests/Theme/EntityFilteringThemeTest.php index ba366a3..e1ae989 100644 --- a/core/modules/system/src/Tests/Theme/EntityFilteringThemeTest.php +++ b/core/modules/system/src/Tests/Theme/EntityFilteringThemeTest.php @@ -12,6 +12,8 @@ use Drupal\comment\CommentInterface; use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface; use Drupal\simpletest\WebTestBase; +use Drupal\comment\Entity\Comment; +use Drupal\taxonomy\Entity\Term; /** * Tests themed output for each entity type in all available themes to ensure @@ -93,10 +95,10 @@ protected function setUp() { $this->drupalLogin($this->user); // Create a test term. - $this->term = entity_create('taxonomy_term', array( + $this->term = Term::create([ 'name' => $this->xssLabel, 'vid' => 1, - )); + ]); $this->term->save(); // Add a comment field. @@ -110,7 +112,7 @@ protected function setUp() { )); // Create a test comment on the test node. - $this->comment = entity_create('comment', array( + $this->comment = Comment::create(array( 'entity_id' => $this->node->id(), 'entity_type' => 'node', 'field_name' => 'comment', diff --git a/core/modules/system/src/Tests/Theme/FunctionsTest.php b/core/modules/system/src/Tests/Theme/FunctionsTest.php index 5e31422..028eb5a 100644 --- a/core/modules/system/src/Tests/Theme/FunctionsTest.php +++ b/core/modules/system/src/Tests/Theme/FunctionsTest.php @@ -290,6 +290,127 @@ function testLinks() { } /** + * Tests links.html.twig using links with indexed keys. + */ + function testIndexedKeyedLinks() { + // Turn off the query for the + // \Drupal\Core\Utility\LinkGeneratorInterface::generate() method to compare + // the active link correctly. + $original_query = \Drupal::request()->query->all(); + \Drupal::request()->query->replace([]); + // Verify that empty variables produce no output. + $variables = []; + $expected = ''; + $this->assertThemeOutput('links', $variables, $expected, 'Empty %callback generates no output.'); + + $variables = []; + $variables['heading'] = 'Some title'; + $expected = ''; + $this->assertThemeOutput('links', $variables, $expected, 'Empty %callback with heading generates no output.'); + + // Verify that a list of links is properly rendered. + $variables = []; + $variables['attributes'] = ['id' => 'somelinks']; + $variables['links'] = array( + array( + 'title' => 'A ', + 'url' => Url::fromUri('base:a/link'), + ), + array( + 'title' => 'Plain "text"', + ), + array( + 'title' => SafeMarkup::format('@text', array('@text' => 'potentially unsafe text that be escaped')), + ), + array( + 'title' => 'Front page', + 'url' => Url::fromRoute(''), + ), + array( + 'title' => 'Test route', + 'url' => Url::fromRoute('router_test.1'), + ), + array( + 'title' => 'Query test route', + 'url' => Url::fromRoute('router_test.1'), + 'query' => array( + 'key' => 'value', + ) + ), + ); + + $expected_links = ''; + $expected_links .= ''; + + // Verify that passing a string as heading works. + $variables['heading'] = 'Links heading'; + $expected_heading = '

                          Links heading

                          '; + $expected = $expected_heading . $expected_links; + $this->assertThemeOutput('links', $variables, $expected); + + // Restore the original request's query. + \Drupal::request()->query->replace($original_query); + + // Verify that passing an array as heading works (core support). + $variables['heading'] = [ + 'text' => 'Links heading', + 'level' => 'h3', + 'attributes' => ['class' => ['heading']], + ]; + $expected_heading = '

                          Links heading

                          '; + $expected = $expected_heading . $expected_links; + $this->assertThemeOutput('links', $variables, $expected); + + // Verify that passing attributes for the heading works. + $variables['heading'] = ['text' => 'Links heading', 'level' => 'h3', 'attributes' => ['id' => 'heading']]; + $expected_heading = '

                          Links heading

                          '; + $expected = $expected_heading . $expected_links; + $this->assertThemeOutput('links', $variables, $expected); + + // Verify that passing attributes for the links work. + $variables['links'][1]['attributes'] = [ + 'class' => ['a/class'], + ]; + $expected_links = ''; + $expected_links .= ''; + $expected = $expected_heading . $expected_links; + $this->assertThemeOutput('links', $variables, $expected); + + // Verify the data- attributes for setting the "active" class on links. + \Drupal::currentUser()->setAccount(new UserSession(array('uid' => 1))); + $variables['set_active_class'] = TRUE; + $expected_links = ''; + $expected_links .= ''; + $expected = $expected_heading . $expected_links; + $this->assertThemeOutput('links', $variables, $expected); + } + + /** * Test the use of drupal_pre_render_links() on a nested array of links. */ function testDrupalPreRenderLinks() { diff --git a/core/modules/system/src/Tests/Theme/MessageTest.php b/core/modules/system/src/Tests/Theme/MessageTest.php index db27d2a..878d5bc 100644 --- a/core/modules/system/src/Tests/Theme/MessageTest.php +++ b/core/modules/system/src/Tests/Theme/MessageTest.php @@ -27,7 +27,7 @@ class MessageTest extends KernelTestBase { function testMessages() { // Enable the Classy theme. \Drupal::service('theme_handler')->install(['classy']); - $this->config('system.theme')->set('default', 'classy')->save(); + \Drupal::service('theme_handler')->setDefault('classy'); drupal_set_message('An error occurred', 'error'); drupal_set_message('But then something nice happened'); diff --git a/core/modules/system/src/Tests/Theme/StableThemeTest.php b/core/modules/system/src/Tests/Theme/StableThemeTest.php index 1f898ba..0df5c71 100644 --- a/core/modules/system/src/Tests/Theme/StableThemeTest.php +++ b/core/modules/system/src/Tests/Theme/StableThemeTest.php @@ -52,7 +52,7 @@ protected function setUp() { */ public function testStableIsDefault() { $this->themeHandler->install(['test_stable']); - $this->config('system.theme')->set('default', 'test_stable')->save(); + $this->themeHandler->setDefault('test_stable'); $theme = $this->themeManager->getActiveTheme(); /** @var \Drupal\Core\Theme\ActiveTheme $base_theme */ $base_themes = $theme->getBaseThemes(); @@ -65,7 +65,7 @@ public function testStableIsDefault() { */ public function testWildWest() { $this->themeHandler->install(['test_wild_west']); - $this->config('system.theme')->set('default', 'test_wild_west')->save(); + $this->themeHandler->setDefault('test_wild_west'); $theme = $this->themeManager->getActiveTheme(); /** @var \Drupal\Core\Theme\ActiveTheme $base_theme */ $base_themes = $theme->getBaseThemes(); diff --git a/core/modules/system/src/Tests/Theme/ThemeTest.php b/core/modules/system/src/Tests/Theme/ThemeTest.php index a84ef8f..3d8b784 100644 --- a/core/modules/system/src/Tests/Theme/ThemeTest.php +++ b/core/modules/system/src/Tests/Theme/ThemeTest.php @@ -175,7 +175,7 @@ function testCSSOverride() { $config->set('css.preprocess', 0); $config->save(); $this->drupalGet('theme-test/suggestion'); - $this->assertNoText('system.module.css', "The theme's .info.yml file is able to remove a module CSS file from being added to the page."); + $this->assertNoText('js.module.css', 'The theme\'s .info.yml file is able to override a module CSS file from being added to the page.'); // Also test with aggregation enabled, simply ensuring no PHP errors are // triggered during drupal_build_css_cache() when a source file doesn't diff --git a/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php b/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php index 1b330d0..c220dde 100644 --- a/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php +++ b/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php @@ -31,7 +31,7 @@ function testTwigDebugMarkup() { $renderer = $this->container->get('renderer'); $extension = twig_extension(); \Drupal::service('theme_handler')->install(array('test_theme')); - $this->config('system.theme')->set('default', 'test_theme')->save(); + \Drupal::service('theme_handler')->setDefault('test_theme'); $this->drupalCreateContentType(array('type' => 'page')); // Enable debug, rebuild the service container, and clear all caches. $parameters = $this->container->getParameter('twig.config'); diff --git a/core/modules/system/src/Tests/Theme/TwigTransTest.php b/core/modules/system/src/Tests/Theme/TwigTransTest.php index 867c4d5..9eec247 100644 --- a/core/modules/system/src/Tests/Theme/TwigTransTest.php +++ b/core/modules/system/src/Tests/Theme/TwigTransTest.php @@ -54,7 +54,7 @@ protected function setUp() { // Setup test_theme. \Drupal::service('theme_handler')->install(array('test_theme')); - $this->config('system.theme')->set('default', 'test_theme')->save(); + \Drupal::service('theme_handler')->setDefault('test_theme'); // Create and log in as admin. $this->adminUser = $this->drupalCreateUser(array( diff --git a/core/modules/system/src/Tests/Theme/TwigWhiteListTest.php b/core/modules/system/src/Tests/Theme/TwigWhiteListTest.php index 99612c2..edc0c91 100644 --- a/core/modules/system/src/Tests/Theme/TwigWhiteListTest.php +++ b/core/modules/system/src/Tests/Theme/TwigWhiteListTest.php @@ -46,7 +46,7 @@ class TwigWhiteListTest extends KernelTestBase { */ protected function setUp() { parent::setUp(); - $this->installSchema('system', array('router', 'sequences')); + $this->installSchema('system', array('sequences')); $this->installEntitySchema('node'); $this->installEntitySchema('user'); $this->installEntitySchema('taxonomy_term'); diff --git a/core/modules/system/src/Tests/TypedData/TypedDataTest.php b/core/modules/system/src/Tests/TypedData/TypedDataTest.php index c6a7f1c..8c24946 100644 --- a/core/modules/system/src/Tests/TypedData/TypedDataTest.php +++ b/core/modules/system/src/Tests/TypedData/TypedDataTest.php @@ -12,6 +12,7 @@ use Drupal\Core\TypedData\ListDataDefinition; use Drupal\Core\TypedData\MapDataDefinition; use Drupal\Core\TypedData\TypedDataInterface; +use Drupal\file\Entity\File; use Drupal\simpletest\KernelTestBase; use Drupal\Core\Datetime\DrupalDateTime; @@ -247,7 +248,7 @@ public function testGetAndSet() { for ($i = 0; $i < 3; $i++){ $path = "public://example_$i.png"; file_unmanaged_copy(\Drupal::root() . '/core/misc/druplicon.png', $path); - $image = entity_create('file', array('uri' => $path)); + $image = File::create(['uri' => $path]); $image->save(); $files[] = $image; } diff --git a/core/modules/system/src/Tests/Update/DbDumpTest.php b/core/modules/system/src/Tests/Update/DbDumpTest.php index 7cd099b..230743d 100644 --- a/core/modules/system/src/Tests/Update/DbDumpTest.php +++ b/core/modules/system/src/Tests/Update/DbDumpTest.php @@ -90,9 +90,7 @@ protected function setUp() { // Create some schemas so our export contains tables. $this->installSchema('system', [ 'key_value_expire', - 'semaphore', 'sessions', - 'url_alias', ]); $this->installSchema('dblog', ['watchdog']); $this->installEntitySchema('block_content'); @@ -113,6 +111,9 @@ protected function setUp() { $account = User::create(['mail' => 'q\'uote$dollar@example.com', 'name' => '$dollar']); $account->save(); + // Create url_alias (this will create 'url_alias'). + $this->container->get('path.alias_storage')->save('/user/' . $account->id(), '/user/example'); + // Create a cache table (this will create 'cache_discovery'). \Drupal::cache('discovery')->set('test', $this->data); @@ -125,13 +126,14 @@ protected function setUp() { 'cachetags', 'config', 'cache_bootstrap', + 'cache_data', + 'cache_default', 'cache_discovery', 'cache_entity', 'file_managed', 'key_value_expire', 'menu_link_content', 'menu_link_content_data', - 'semaphore', 'sequences', 'sessions', 'url_alias', diff --git a/core/modules/system/src/Tests/Update/InvalidUpdateHookTest.php b/core/modules/system/src/Tests/Update/InvalidUpdateHookTest.php index 84c75d8..becf517 100644 --- a/core/modules/system/src/Tests/Update/InvalidUpdateHookTest.php +++ b/core/modules/system/src/Tests/Update/InvalidUpdateHookTest.php @@ -8,7 +8,6 @@ namespace Drupal\system\Tests\Update; use Drupal\simpletest\WebTestBase; -use Drupal\Core\Extension\ExtensionSchemaVersionException; /** * Tests that a module implementing hook_update_8000() causes an error to be diff --git a/core/modules/system/src/Tests/Utility/LinkGenerationTest.php b/core/modules/system/src/Tests/Utility/LinkGenerationTest.php index 62c4d23..d2d6e9a 100644 --- a/core/modules/system/src/Tests/Utility/LinkGenerationTest.php +++ b/core/modules/system/src/Tests/Utility/LinkGenerationTest.php @@ -7,7 +7,7 @@ namespace Drupal\system\Tests\Utility; -use Drupal\Component\Utility\SafeMarkup; +use Drupal\Component\Render\MarkupInterface; use Drupal\Core\Render\RenderContext; use Drupal\Core\Url; use Drupal\simpletest\KernelTestBase; @@ -32,7 +32,7 @@ function testHookLinkAlter() { return \Drupal::l(['#markup' => 'link with markup'], $url); }); $this->setRawContent($link); - $this->assertTrue(SafeMarkup::isSafe($link), 'The output of link generation is marked safe as it is a link.'); + $this->assertTrue($link instanceof MarkupInterface, 'The output of link generation is marked safe as it is a link.'); // Ensure the content of the link is not escaped. $this->assertRaw('link with markup'); @@ -42,7 +42,7 @@ function testHookLinkAlter() { return \Drupal::l(['#markup' => 'link with markup'], $url); }); $this->setRawContent($link); - $this->assertTrue(SafeMarkup::isSafe($link), 'The output of link generation is marked safe as it is a link.'); + $this->assertTrue($link instanceof MarkupInterface, 'The output of link generation is marked safe as it is a link.'); // Ensure the content of the link is escaped. $this->assertEscaped('link with markup Test!'); @@ -52,7 +52,7 @@ function testHookLinkAlter() { return \Drupal::l(['#markup' => 'link with markup'], $url); }); $this->setRawContent($link); - $this->assertTrue(SafeMarkup::isSafe($link), 'The output of link generation is marked safe as it is a link.'); + $this->assertTrue($link instanceof MarkupInterface, 'The output of link generation is marked safe as it is a link.'); // Ensure the content of the link is escaped. $this->assertRaw('link with markup Test!'); @@ -61,7 +61,7 @@ function testHookLinkAlter() { return \Drupal::l('link with markup', $url); }); $this->setRawContent($link); - $this->assertTrue(SafeMarkup::isSafe($link), 'The output of link generation is marked safe as it is a link.'); + $this->assertTrue($link instanceof MarkupInterface, 'The output of link generation is marked safe as it is a link.'); // Ensure the content of the link is escaped. $this->assertEscaped('link with markup'); $this->assertRaw('Test!'); diff --git a/core/modules/system/system.install b/core/modules/system/system.install index afa5892..44212b1 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -61,7 +61,7 @@ function system_requirements($phase) { if (!empty($experimental)) { $requirements['experimental'] = array( 'title' => t('Experimental modules enabled'), - 'value' => t('Experimental modules found: %module_list. Experimental modules are provided for testing purposes only. Use at your own risk.', array('%module_list' => implode(', ', $experimental))), + 'value' => t('Experimental modules found: %module_list. Experimental modules are provided for testing purposes only. Use at your own risk.', array('%module_list' => implode(', ', $experimental), ':url' => 'https://www.drupal.org/core/experimental')), 'severity' => REQUIREMENT_WARNING, ); } @@ -246,7 +246,7 @@ function system_requirements($phase) { $requirements['php_opcache'] = array( 'value' => t('Not enabled'), 'severity' => REQUIREMENT_WARNING, - 'description' => t('PHP OPcode caching can improve your site\'s performance considerably. It is highly recommended to have OPcache installed on your server.', array(':opcache_link' => 'http://php.net/manual/en/opcache.installation.php')), + 'description' => t('PHP OPcode caching can improve your site\'s performance considerably. It is highly recommended to have OPcache installed on your server.'), ); } else { @@ -365,12 +365,25 @@ function system_requirements($phase) { else { $site_path = DrupalKernel::findSitePath(Request::createFromGlobals()); } - if (!drupal_verify_install_file($site_path, FILE_NOT_WRITABLE, 'dir')) { - $conf_errors[] = t("The directory %file is not protected from modifications and poses a security risk. You must change the directory's permissions to be non-writable.", array('%file' => $site_path)); + // Allow system administrators to disable permissions hardening for the site + // directory. This allows additional files in the site directory to be + // updated when they are managed in a version control system. + if (Settings::get('skip_permissions_hardening')) { + $conf_errors[] = t('Protection disabled'); + // If permissions hardening is disabled, then only show a warning for a + // writable file, as a reminder, rather than an error. + $file_protection_severity = REQUIREMENT_WARNING; + } + else { + // In normal operation, writable files or directories are an error. + $file_protection_severity = REQUIREMENT_ERROR; + if (!drupal_verify_install_file($site_path, FILE_NOT_WRITABLE, 'dir')) { + $conf_errors[] = t("The directory %file is not protected from modifications and poses a security risk. You must change the directory's permissions to be non-writable.", array('%file' => $site_path)); + } } foreach (array('settings.php', 'settings.local.php', 'services.yml') as $conf_file) { $full_path = $site_path . '/' . $conf_file; - if (file_exists($full_path) && !drupal_verify_install_file($full_path, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE)) { + if (file_exists($full_path) && (Settings::get('skip_permissions_hardening') || !drupal_verify_install_file($full_path, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE))) { $conf_errors[] = t("The file %file is not protected from modifications and poses a security risk. You must change the file's permissions to be non-writable.", array('%file' => $full_path)); } } @@ -393,7 +406,7 @@ function system_requirements($phase) { } $requirements['configuration_files'] = array( 'value' => t('Not protected'), - 'severity' => REQUIREMENT_ERROR, + 'severity' => $file_protection_severity, 'description' => $description, ); } @@ -841,83 +854,6 @@ function system_install() { * Implements hook_schema(). */ function system_schema() { - $schema['batch'] = array( - 'description' => 'Stores details about batches (processes that run in multiple HTTP requests).', - 'fields' => array( - 'bid' => array( - 'description' => 'Primary Key: Unique batch ID.', - // This is not a serial column, to allow both progressive and - // non-progressive batches. See batch_process(). - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'token' => array( - 'description' => "A string token generated against the current user's session id and the batch id, used to ensure that only the user who submitted the batch can effectively access it.", - 'type' => 'varchar_ascii', - 'length' => 64, - 'not null' => TRUE, - ), - 'timestamp' => array( - 'description' => 'A Unix timestamp indicating when this batch was submitted for processing. Stale batches are purged at cron time.', - 'type' => 'int', - 'not null' => TRUE, - ), - 'batch' => array( - 'description' => 'A serialized array containing the processing data for the batch.', - 'type' => 'blob', - 'not null' => FALSE, - 'size' => 'big', - ), - ), - 'primary key' => array('bid'), - 'indexes' => array( - 'token' => array('token'), - ), - ); - - $schema['flood'] = array( - 'description' => 'Flood controls the threshold of events, such as the number of contact attempts.', - 'fields' => array( - 'fid' => array( - 'description' => 'Unique flood event ID.', - 'type' => 'serial', - 'not null' => TRUE, - ), - 'event' => array( - 'description' => 'Name of event (e.g. contact).', - 'type' => 'varchar_ascii', - 'length' => 64, - 'not null' => TRUE, - 'default' => '', - ), - 'identifier' => array( - 'description' => 'Identifier of the visitor, such as an IP address or hostname.', - 'type' => 'varchar_ascii', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - ), - 'timestamp' => array( - 'description' => 'Timestamp of the event.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'expiration' => array( - 'description' => 'Expiration timestamp. Expired events are purged on cron run.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('fid'), - 'indexes' => array( - 'allow' => array('event', 'identifier', 'timestamp'), - 'purge' => array('expiration'), - ), - ); - $schema['key_value'] = array( 'description' => 'Generic key-value storage table. See the state system for an example.', 'fields' => array( @@ -983,129 +919,6 @@ function system_schema() { ), ); - $schema['queue'] = array( - 'description' => 'Stores items in queues.', - 'fields' => array( - 'item_id' => array( - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'description' => 'Primary Key: Unique item ID.', - ), - 'name' => array( - 'type' => 'varchar_ascii', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The queue name.', - ), - 'data' => array( - 'type' => 'blob', - 'not null' => FALSE, - 'size' => 'big', - 'serialize' => TRUE, - 'description' => 'The arbitrary data for the item.', - ), - 'expire' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Timestamp when the claim lease expires on the item.', - ), - 'created' => array( - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Timestamp when the item was created.', - ), - ), - 'primary key' => array('item_id'), - 'indexes' => array( - 'name_created' => array('name', 'created'), - 'expire' => array('expire'), - ), - ); - - $schema['router'] = array( - 'description' => 'Maps paths to various callbacks (access, page and title)', - 'fields' => array( - 'name' => array( - 'description' => 'Primary Key: Machine name of this route', - 'type' => 'varchar_ascii', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'path' => array( - 'description' => 'The path for this URI', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'pattern_outline' => array( - 'description' => 'The pattern', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'fit' => array( - 'description' => 'A numeric representation of how specific the path is.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'route' => array( - 'description' => 'A serialized Route object', - 'type' => 'blob', - 'size' => 'big', - ), - 'number_parts' => array( - 'description' => 'Number of parts in this router path.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - 'size' => 'small', - ), - ), - 'indexes' => array( - 'pattern_outline_parts' => array('pattern_outline', 'number_parts'), - ), - 'primary key' => array('name'), - ); - - $schema['semaphore'] = array( - 'description' => 'Table for holding semaphores, locks, flags, etc. that cannot be stored as state since they must not be cached.', - 'fields' => array( - 'name' => array( - 'description' => 'Primary Key: Unique name.', - 'type' => 'varchar_ascii', - 'length' => 255, - 'not null' => TRUE, - 'default' => '' - ), - 'value' => array( - 'description' => 'A value for the semaphore.', - 'type' => 'varchar_ascii', - 'length' => 255, - 'not null' => TRUE, - 'default' => '' - ), - 'expire' => array( - 'description' => 'A Unix timestamp with microseconds indicating when the semaphore should expire.', - 'type' => 'float', - 'size' => 'big', - 'not null' => TRUE - ), - ), - 'indexes' => array( - 'value' => array('value'), - 'expire' => array('expire'), - ), - 'primary key' => array('name'), - ); - $schema['sequences'] = array( 'description' => 'Stores IDs.', 'fields' => array( @@ -1169,44 +982,6 @@ function system_schema() { ), ); - $schema['url_alias'] = array( - 'description' => 'A list of URL aliases for Drupal paths; a user may visit either the source or destination path.', - 'fields' => array( - 'pid' => array( - 'description' => 'A unique path alias identifier.', - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ), - 'source' => array( - 'description' => 'The Drupal path this alias is for; e.g. node/12.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'alias' => array( - 'description' => 'The alias for this path; e.g. title-of-the-story.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'langcode' => array( - 'description' => "The language code this alias is for; if 'und', the alias will be used for unknown languages. Each Drupal path can have an alias for each supported language.", - 'type' => 'varchar_ascii', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - ), - 'primary key' => array('pid'), - 'indexes' => array( - 'alias_langcode_pid' => array('alias', 'langcode', 'pid'), - 'source_langcode_pid' => array('source', 'langcode', 'pid'), - ), - ); - return $schema; } diff --git a/core/modules/system/system.links.menu.yml b/core/modules/system/system.links.menu.yml index ba21cee..b5f9d87 100644 --- a/core/modules/system/system.links.menu.yml +++ b/core/modules/system/system.links.menu.yml @@ -18,7 +18,7 @@ system.admin_structure: system.themes_page: route_name: system.themes_page title: Appearance - description: 'Select and configure your themes.' + description: 'Select and configure themes.' parent: system.admin weight: -6 system.modules_list: @@ -40,7 +40,7 @@ system.admin_config_media: weight: -10 system.file_system_settings: title: 'File system' - description: 'Tell Drupal where to store uploaded files and how they are accessed.' + description: 'Configure the location of uploaded files and how they are accessed.' parent: system.admin_config_media route_name: system.file_system_settings system.image_toolkit_settings: @@ -56,64 +56,64 @@ system.admin_config_services: system.rss_feeds_settings: title: 'RSS publishing' parent: system.admin_config_services - description: 'Configure the site description, the number of items per feed and whether feeds should be titles/teasers/full-text.' + description: 'Configure the site description, the number of items per feed, and whether feeds should be titles/teasers/full-text.' route_name: system.rss_feeds_settings system.admin_config_development: route_name: system.admin_config_development parent: system.admin_config title: Development - description: 'Development tools.' + description: 'Configure and use development tools.' weight: -10 system.site_maintenance_mode: title: 'Maintenance mode' parent: system.admin_config_development - description: 'Take the site offline for maintenance or bring it back online.' + description: 'Take the site offline for updates and other maintenance tasks.' route_name: system.site_maintenance_mode weight: -10 system.performance_settings: title: Performance parent: system.admin_config_development - description: 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.' + description: 'Configure caching and bandwidth optimization.' route_name: system.performance_settings weight: -20 system.logging_settings: title: 'Logging and errors' parent: system.admin_config_development - description: 'Settings for logging and alerts modules. Various modules can route Drupal''s system events to different destinations, such as syslog, database, email, etc.' + description: 'Configure the display of error messages and database logging.' route_name: system.logging_settings weight: -15 system.admin_config_regional: route_name: system.admin_config_regional title: 'Regional and language' parent: system.admin_config - description: 'Regional settings, localization and translation.' + description: 'Configure regional settings, localization, and translation.' weight: -5 system.regional_settings: title: 'Regional settings' parent: system.admin_config_regional - description: 'Settings for the site''s default time zone and country.' + description: 'Configure the locale and timezone settings.' route_name: system.regional_settings weight: -20 entity.date_format.collection: title: 'Date and time formats' parent: system.admin_config_regional - description: 'Configure display format strings for date and time.' + description: 'Configure how dates and times are displayed.' route_name: entity.date_format.collection weight: -9 system.admin_config_search: title: 'Search and metadata' route_name: system.admin_config_search parent: system.admin_config - description: 'Local site search, metadata and SEO.' + description: 'Configure site search, metadata, and search engine optimization.' weight: -10 system.admin_config_system: title: System route_name: system.admin_config_system parent: system.admin_config - description: 'General system related configuration.' + description: 'Configure basic site settings, actions, and cron.' weight: -20 system.site_information_settings: - title: 'Site information' + title: 'Basic site settings' parent: system.admin_config_system description: 'Change site name, email address, slogan, default front page, and error pages.' route_name: system.site_information_settings @@ -128,19 +128,19 @@ system.admin_config_ui: title: 'User interface' route_name: system.admin_config_ui parent: system.admin_config - description: 'Tools that enhance the user interface.' + description: 'Configure the administrative user interface.' weight: -15 system.admin_config_workflow: title: Workflow route_name: system.admin_config_workflow parent: system.admin_config - description: 'Content workflow, editorial workflow tools.' + description: 'Manage the content workflow.' weight: 5 system.admin_config_content: title: 'Content authoring' route_name: system.admin_config_content parent: system.admin_config - description: 'Settings related to formatting and authoring content.' + description: 'Configure content formatting and authoring.' weight: -15 system.admin_reports: title: Reports @@ -151,5 +151,5 @@ system.admin_reports: system.status: title: 'Status report' parent: system.admin_reports - description: 'Get a status report about your site''s operation and any detected problems.' + description: 'Get a status report about your site''s operation.' route_name: system.status diff --git a/core/modules/system/system.module b/core/modules/system/system.module index f280866..85f55ed 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -9,6 +9,7 @@ use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Asset\AttachedAssetsInterface; use Drupal\Core\Cache\Cache; +use Drupal\Core\Queue\QueueGarbageCollectionInterface; use Drupal\Core\Database\Query\AlterableInterface; use Drupal\Core\Extension\Extension; use Drupal\Core\Extension\ExtensionDiscovery; @@ -17,6 +18,7 @@ use Drupal\Core\PageCache\RequestPolicyInterface; use Drupal\Core\PhpStorage\PhpStorageFactory; use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Core\Routing\StackedRouteMatchInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Menu\MenuTreeParameters; use Drupal\Core\Extension\ModuleHandler; @@ -88,7 +90,7 @@ function system_help($route_name, RouteMatchInterface $route_match) { $output .= '
                          ' . t('Disabling drag-and-drop functionality') . '
                          '; $output .= '
                          ' . t('The default drag-and-drop user interface for ordering tables in the administrative interface presents a challenge for some users, including users of screen readers and other assistive technology. The drag-and-drop interface can be disabled in a table by clicking a link labeled "Show row weights" above the table. The replacement interface allows users to order the table by choosing numerical weights instead of dragging table rows.') . '
                          '; $output .= '
                          ' . t('Configuring basic site settings') . '
                          '; - $output .= '
                          ' . t('The System module provides pages for managing basic site configuration, including Date and time formats and basic Site information (site name, email address to send mail from, home page, and error pages). Additional configuration pages are listed on the main Configuration page.', array(':date-time-settings' => \Drupal::url('entity.date_format.collection'), ':site-info' => \Drupal::url('system.site_information_settings'), ':config' => \Drupal::url('system.admin_config'))) . '
                          '; + $output .= '
                          ' . t('The System module provides pages for managing basic site configuration, including Date and time formats and Basic site settings (site name, email address to send mail from, home page, and error pages). Additional configuration pages are listed on the main Configuration page.', array(':date-time-settings' => \Drupal::url('entity.date_format.collection'), ':site-info' => \Drupal::url('system.site_information_settings'), ':config' => \Drupal::url('system.admin_config'))) . '
                          '; $output .= '
                          ' . t('Checking site status') . '
                          '; $output .= '
                          ' . t('The Status report provides an overview of the configuration, status, and health of your site. Review this report to make sure there are not any problems to address, and to find information about the software your site and web server are using.', array(':status' => \Drupal::url('system.status'))) . '
                          '; $output .= '
                          ' . t('Using maintenance mode') . '
                          '; @@ -215,6 +217,13 @@ function system_theme() { 'variables' => array('menu_items' => NULL), 'file' => 'system.admin.inc', ), + 'entity_add_list' => array( + 'variables' => array( + 'bundles' => array(), + 'add_bundle_message' => NULL, + ), + 'template' => 'entity-add-list', + ), )); } @@ -315,6 +324,25 @@ function system_theme_suggestions_field(array $variables) { } /** + * Prepares variables for the list of available bundles. + * + * Default template: entity-add-list.html.twig. + * + * @param array $variables + * An associative array containing: + * - bundles: An array of bundles with the label, description, add_link keys. + * - add_bundle_message: The message shown when there are no bundles. Only ++ * available if the entity type uses bundle entities. + */ +function template_preprocess_entity_add_list(&$variables) { + foreach ($variables['bundles'] as $bundle_name => $bundle_info) { + $variables['bundles'][$bundle_name]['description'] = [ + '#markup' => $bundle_info['description'], + ]; + } +} + +/** * @defgroup authorize Authorized operations * @{ * Functions to run operations with elevated privileges via authorize.php. @@ -646,7 +674,9 @@ function system_js_settings_build(&$settings, AttachedAssetsInterface $assets) { * as well as theme_token ajax state. */ function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) { - $request = \Drupal::request(); + // As this is being output in the final response always use the master + // request. + $request = \Drupal::requestStack()->getMasterRequest(); $current_query = $request->query->all(); // Let output path processors set a prefix. @@ -656,8 +686,12 @@ function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) { $path_processor->processOutbound('/', $options); $pathPrefix = $options['prefix']; - $current_path = \Drupal::routeMatch()->getRouteName() ? Url::fromRouteMatch(\Drupal::routeMatch())->getInternalPath() : ''; - $current_path_is_admin = \Drupal::service('router.admin_context')->isAdminRoute(); + $route_match = \Drupal::routeMatch(); + if ($route_match instanceof StackedRouteMatchInterface) { + $route_match = $route_match->getMasterRouteMatch(); + } + $current_path = $route_match->getRouteName() ? Url::fromRouteMatch($route_match)->getInternalPath() : ''; + $current_path_is_admin = \Drupal::service('router.admin_context')->isAdminRoute($route_match->getRouteObject()); $path_settings = [ 'baseUrl' => $request->getBaseUrl() . '/', 'pathPrefix' => $pathPrefix, @@ -1061,21 +1095,6 @@ function system_rebuild_module_data() { } /** - * Returns an array of default theme features. - * - * @see \Drupal\Core\Extension\ThemeHandler::$defaultFeatures - */ -function _system_default_theme_features() { - return array( - 'favicon', - 'logo', - 'node_user_picture', - 'comment_user_picture', - 'comment_user_verification', - ); -} - -/** * Get a list of available regions from a specified theme. * * @param \Drupal\Core\Extension\Extension|string $theme @@ -1266,21 +1285,10 @@ function system_cron() { \Drupal::service('keyvalue.expirable.database')->garbageCollection(); } - // Clean up the queue for failed batches. - db_delete('queue') - ->condition('created', REQUEST_TIME - 864000, '<') - ->condition('name', 'drupal_batch:%', 'LIKE') - ->execute(); - - // Reset expired items in the default queue implementation table. If that's - // not used, this will simply be a no-op. - db_update('queue') - ->fields(array( - 'expire' => 0, - )) - ->condition('expire', 0, '<>') - ->condition('expire', REQUEST_TIME, '<') - ->execute(); + // Clean up any garbage in the queue service. + if (\Drupal::service('queue') instanceof QueueGarbageCollectionInterface) { + \Drupal::service('queue')->garbageCollection(); + } // Clean up PHP storage. PhpStorageFactory::get('container')->garbageCollection(); diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml index 14204c8..ed111f8 100644 --- a/core/modules/system/system.routing.yml +++ b/core/modules/system/system.routing.yml @@ -146,7 +146,7 @@ system.site_information_settings: path: '/admin/config/system/site-information' defaults: _form: 'Drupal\system\Form\SiteInformationForm' - _title: 'Site information' + _title: 'Basic site settings' requirements: _permission: 'administer site configuration' @@ -273,6 +273,14 @@ system.modules_list_confirm: requirements: _permission: 'administer modules' +system.modules_list_experimental_confirm: + path: '/admin/modules/list/confirm-experimental' + defaults: + _form: 'Drupal\system\Form\ModulesListExperimentalConfirmForm' + _title: 'Experimental modules' + requirements: + _permission: 'administer modules' + system.theme_uninstall: path: '/admin/appearance/uninstall' defaults: @@ -437,6 +445,8 @@ system.batch_page.html: _format: 'html' options: _admin_route: TRUE + # @todo Remove the '_no_big_pipe' route option when https://www.drupal.org/node/2589967 lands. + _no_big_pipe: TRUE system.batch_page.json: path: '/batch' diff --git a/core/modules/system/templates/entity-add-list.html.twig b/core/modules/system/templates/entity-add-list.html.twig new file mode 100644 index 0000000..8d73786 --- /dev/null +++ b/core/modules/system/templates/entity-add-list.html.twig @@ -0,0 +1,30 @@ +{# +/** + * @file + * Default theme implementation to present a list of available bundles. + * + * Available variables: + * - bundles: A list of bundles, each with the following properties: + * - label: Bundle label. + * - description: Bundle description. + * - add_link: Link to create an entity of this bundle. + * - add_bundle_message: The message shown when there are no bundles. Only + * available if the entity type uses bundle entities. + * + * @see template_preprocess_entity_add_list() + * + * @ingroup themeable + */ +#} +{% if bundles is not empty %} +
                          + {% for bundle in bundles %} +
                          {{ bundle.add_link }}
                          +
                          {{ bundle.description }}
                          + {% endfor %} +
                          +{% elseif add_bundle_message is not empty %} +

                          + {{ add_bundle_message }} +

                          +{% endif %} diff --git a/core/modules/system/templates/install-page.html.twig b/core/modules/system/templates/install-page.html.twig index 1d0e479..f6091fd 100644 --- a/core/modules/system/templates/install-page.html.twig +++ b/core/modules/system/templates/install-page.html.twig @@ -28,7 +28,7 @@
                          {% if title %} -

                          {{ title }}

                          +

                          {{ title }}

                          {% endif %} {{ page.highlighted }} {{ page.content }} diff --git a/core/modules/system/templates/links.html.twig b/core/modules/system/templates/links.html.twig index 86713b5..77e7260 100644 --- a/core/modules/system/templates/links.html.twig +++ b/core/modules/system/templates/links.html.twig @@ -13,7 +13,6 @@ * to l() as its $options parameter. * - attributes: (optional) HTML attributes for the anchor, or for the * tag if no 'href' is supplied. - * - link_key: The link CSS class. * - heading: (optional) A heading to precede the links. * - text: The heading text. * - level: The heading level (e.g. 'h2', 'h3'). @@ -43,8 +42,8 @@ {%- endif -%} {%- endif -%} - {%- for key, item in links -%} - + {%- for item in links -%} + {%- if item.link -%} {{ item.link }} {%- elseif item.text_attributes -%} diff --git a/core/modules/system/tests/fixtures/update/drupal-8.broken_routing.php b/core/modules/system/tests/fixtures/update/drupal-8.broken_routing.php index 3c5e9af..7a83e87 100644 --- a/core/modules/system/tests/fixtures/update/drupal-8.broken_routing.php +++ b/core/modules/system/tests/fixtures/update/drupal-8.broken_routing.php @@ -1,5 +1,10 @@ insert('config') + ->fields(['collection', 'name', 'data']) + ->values([ + 'collection' => '', + 'name' => 'views.view.' . $views_config['id'], + 'data' => serialize($views_config), + ])->execute(); diff --git a/core/modules/system/tests/fixtures/update/drupal8.views-image-style-dependency-2649914.yml b/core/modules/system/tests/fixtures/update/drupal8.views-image-style-dependency-2649914.yml new file mode 100644 index 0000000..585da34 --- /dev/null +++ b/core/modules/system/tests/fixtures/update/drupal8.views-image-style-dependency-2649914.yml @@ -0,0 +1,43 @@ +langcode: en +status: true +dependencies: + config: + - field.storage.node.field_image + module: + - image + - node +id: foo +label: Foo +module: views +description: '' +tag: '' +base_table: node_field_data +base_field: nid +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + fields: + field_image: + id: field_image + table: node__field_image + field: field_image + type: image + settings: + image_style: thumbnail + image_link: '' + plugin_id: field + field_image_1: + id: field_image_1 + table: node__field_image + field: field_image + type: image + settings: + # This field's formatter is using a non-existent image style. + image_style: nonexistent + image_link: '' + plugin_id: field diff --git a/core/modules/system/tests/modules/cron_queue_test/src/Plugin/QueueWorker/CronQueueTestRequeueException.php b/core/modules/system/tests/modules/cron_queue_test/src/Plugin/QueueWorker/CronQueueTestRequeueException.php new file mode 100644 index 0000000..4478f79 --- /dev/null +++ b/core/modules/system/tests/modules/cron_queue_test/src/Plugin/QueueWorker/CronQueueTestRequeueException.php @@ -0,0 +1,36 @@ +get('cron_queue_test_requeue_exception')) { + $state->set('cron_queue_test_requeue_exception', 1); + throw new RequeueException('I am not done yet!'); + } + else { + $state->set('cron_queue_test_requeue_exception', 2); + } + } + +} diff --git a/core/modules/system/tests/modules/database_test/database_test.module b/core/modules/system/tests/modules/database_test/database_test.module index b5ee9c8..460480a 100644 --- a/core/modules/system/tests/modules/database_test/database_test.module +++ b/core/modules/system/tests/modules/database_test/database_test.module @@ -1,5 +1,10 @@ $entity_type, 'field_name' => 'field_test_text', 'type' => 'text', 'cardinality' => 1, ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'entity_type' => $entity_type, 'field_name' => 'field_test_text', 'bundle' => $entity_type, 'label' => 'Test text-field', 'translatable' => FALSE, - ))->save(); + ])->save(); entity_get_form_display($entity_type, $entity_type, 'default') ->setComponent('field_test_text', array('type' => 'text_textfield')) diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module index 0eec45d..9d9d63c 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.module +++ b/core/modules/system/tests/modules/entity_test/entity_test.module @@ -67,6 +67,8 @@ function entity_test_entity_types($filter = NULL) { } if ($filter === ENTITY_TEST_TYPES_ROUTING) { $types[] = 'entity_test_base_field_display'; + $types[] = 'entity_test_string_id'; + $types[] = 'entity_test_no_id'; } $types[] = 'entity_test_mulrev'; $types[] = 'entity_test_mulrev_changed'; @@ -194,7 +196,7 @@ function entity_test_entity_bundle_info() { $bundles = array(); $entity_types = \Drupal::entityManager()->getDefinitions(); foreach ($entity_types as $entity_type_id => $entity_type) { - if ($entity_type->getProvider() == 'entity_test') { + if ($entity_type->getProvider() == 'entity_test' && $entity_type_id != 'entity_test_with_bundle') { $bundles[$entity_type_id] = \Drupal::state()->get($entity_type_id . '.bundles') ?: array($entity_type_id => array('label' => 'Entity Test Bundle')); } } @@ -248,8 +250,8 @@ function entity_test_entity_extra_field_info() { $extra['entity_test']['bundle_with_extra_fields'] = array( 'display' => array( // Note: those extra fields do not currently display anything, they are - // just used in \Drupal\field_ui\Tests\EntityDisplayTest to test the - // behavior of entity display objects. + // just used in \Drupal\Tests\field_ui\Kernel\EntityDisplayTest to test + // the behavior of entity display objects. 'display_extra_field' => array( 'label' => t('Display extra field'), 'description' => t('An extra field on the display side.'), @@ -416,7 +418,7 @@ function entity_test_entity_field_access($operation, FieldDefinitionInterface $f */ function entity_test_entity_field_access_alter(array &$grants, array $context) { if ($context['field_definition']->getName() == 'field_test_text' && $context['items']->value == 'access alter value') { - $grants[':default'] = AccessResult::forbidden()->inheritCacheability($grants[':default'])->cacheUntilEntityChanges($context['items']->getEntity()); + $grants[':default'] = AccessResult::forbidden()->inheritCacheability($grants[':default'])->addCacheableDependency($context['items']->getEntity()); } } diff --git a/core/modules/system/tests/modules/entity_test/entity_test.permissions.yml b/core/modules/system/tests/modules/entity_test/entity_test.permissions.yml index ff9810b..eb0d974 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.permissions.yml +++ b/core/modules/system/tests/modules/entity_test/entity_test.permissions.yml @@ -7,3 +7,6 @@ view test entity translations: title: 'View translations of test entities' view test entity field: title: 'View test entity field' +administer entity_test_with_bundle content: + title: 'administer entity_test_with_bundle content' + description: 'administer entity_test_with_bundle content' diff --git a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml index 542a14e..e4353c8 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml +++ b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml @@ -58,7 +58,13 @@ entity.entity_test.collection: entity.entity_test_rev.revision: path: '/entity_test_rev/{entity_test_rev}/revision/{entity_test_rev_revision}/view' defaults: - _entity_view: 'entity_test_rev' + _controller: '\Drupal\Core\Entity\Controller\EntityViewController::viewRevision' + options: + parameters: + entity_test_rev: + type: entity:entity_test_rev + entity_test_rev_revision: + type: entity_revision:entity_test_rev requirements: _access: 'TRUE' diff --git a/core/modules/system/tests/modules/entity_test/entity_test.views.inc b/core/modules/system/tests/modules/entity_test/entity_test.views.inc index d74af47..53d37da 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.views.inc +++ b/core/modules/system/tests/modules/entity_test/entity_test.views.inc @@ -1,6 +1,11 @@ entityFormBuilder()->getForm($entity); - $form['#title'] = $this->t('Create an @type', array('@type' => $entity_type_id)); - return $form; - } - - /** * Returns an empty page. * * @see \Drupal\entity_test\Routing\EntityTestRoutes::routes() diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php index 1197c2c..c5320e6 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php @@ -47,6 +47,7 @@ * }, * links = { * "canonical" = "/entity_test/{entity_test}", + * "add-form" = "/entity_test/add", * "edit-form" = "/entity_test/manage/{entity_test}/edit", * "delete-form" = "/entity_test/delete/entity_test/{entity_test}", * }, @@ -69,21 +70,7 @@ public static function preCreate(EntityStorageInterface $storage, array &$values * {@inheritdoc} */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { - $fields['id'] = BaseFieldDefinition::create('integer') - ->setLabel(t('ID')) - ->setDescription(t('The ID of the test entity.')) - ->setReadOnly(TRUE) - ->setSetting('unsigned', TRUE); - - $fields['uuid'] = BaseFieldDefinition::create('uuid') - ->setLabel(t('UUID')) - ->setDescription(t('The UUID of the test entity.')) - ->setReadOnly(TRUE); - - $fields['langcode'] = BaseFieldDefinition::create('language') - ->setLabel(t('Language code')) - ->setDescription(t('The language code of the test entity.')) - ->setTranslatable(TRUE); + $fields = parent::baseFieldDefinitions($entity_type); $fields['name'] = BaseFieldDefinition::create('string') ->setLabel(t('Name')) @@ -100,12 +87,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { 'weight' => -5, )); - // @todo: Add allowed values validation. - $fields['type'] = BaseFieldDefinition::create('string') - ->setLabel(t('Type')) - ->setDescription(t('The bundle of the test entity.')) - ->setRequired(TRUE); - $fields['created'] = BaseFieldDefinition::create('created') ->setLabel(t('Authored on')) ->setDescription(t('Time the entity was created')) diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php index d2f8378..4cc7cc3 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php @@ -33,10 +33,12 @@ * "id" = "id", * "label" = "name", * "uuid" = "uuid", - * "bundle" = "type" + * "bundle" = "type", + * "langcode" = "langcode", * }, * links = { * "canonical" = "/entity_test_base_field_display/{entity_test_base_field_display}/edit", + * "add-form" = "/entity_test_base_field_display/add", * "edit-form" = "/entity_test_base_field_display/manage/{entity_test_base_field_display}", * "delete-form" = "/entity_test/delete/entity_test_base_field_display/{entity_test_base_field_display}/edit", * }, diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBundle.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBundle.php new file mode 100644 index 0000000..f9abd9b --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBundle.php @@ -0,0 +1,83 @@ +description; + } + + /** + * {@inheritdoc} + */ + public function setDescription($description) { + $this->description = $description; + return $this; + } + +} diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabel.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabel.php index 1ff0264..6d95066 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabel.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabel.php @@ -20,9 +20,11 @@ * base_table = "entity_test", * render_cache = FALSE, * entity_keys = { + * "uuid" = "uuid", * "id" = "id", * "label" = "name", - * "bundle" = "type" + * "bundle" = "type", + * "langcode" = "langcode", * } * ) */ diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabelCallback.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabelCallback.php index 1088114..632ae0c 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabelCallback.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabelCallback.php @@ -18,7 +18,9 @@ * label_callback = "entity_test_label_callback", * entity_keys = { * "id" = "id", - * "bundle" = "type" + * "bundle" = "type", + * "uuid" = "uuid", + * "langcode" = "langcode", * } * ) */ diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php index d1908d3..256f031 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php @@ -38,6 +38,8 @@ * "langcode" = "langcode", * }, * links = { + * "add-page" = "/entity_test_mul/add", + * "add-form" = "/entity_test_mul/add/{type}", * "canonical" = "/entity_test_mul/manage/{entity_test_mul}", * "edit-form" = "/entity_test_mul/manage/{entity_test_mul}/edit", * "delete-form" = "/entity_test/delete/entity_test_mul/{entity_test_mul}", diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php index 055e290..887535a 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php @@ -42,6 +42,7 @@ * "langcode" = "langcode" * }, * links = { + * "add-form" = "/entity_test_mul_changed/add", * "canonical" = "/entity_test_mul_changed/manage/{entity_test_mul_changed}", * "edit-form" = "/entity_test_mul_changed/manage/{entity_test_mul_changed}/edit", * "delete-form" = "/entity_test/delete/entity_test_mul_changed/{entity_test_mul_changed}", diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php index 3f794e5..56e3bba 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php @@ -7,8 +7,6 @@ namespace Drupal\entity_test\Entity; -use Drupal\Core\Entity\EntityTypeInterface; - /** * Defines a test entity class using a custom langcode entity key. * @@ -41,6 +39,7 @@ * "default_langcode" = "custom_default_langcode_key", * }, * links = { + * "add-form" = "/entity_test_mul_langcode_key/add", * "canonical" = "/entity_test_mul_langcode_key/manage/{entity_test_mul_langcode_key}", * "edit-form" = "/entity_test_mul_langcode_key/manage/{entity_test_mul_langcode_key}/edit", * "delete-form" = "/entity_test/delete/entity_test_mul_langcode_key/{entity_test_mul_langcode_key}", @@ -50,14 +49,4 @@ */ class EntityTestMulLangcodeKey extends EntityTest { - /** - * {@inheritdoc} - */ - public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { - $fields = parent::baseFieldDefinitions($entity_type); - $fields['custom_langcode_key'] = $fields['langcode']; - unset($fields['langcode']); - return $fields; - } - } diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php index 1eb7715..eaab7ac 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php @@ -41,6 +41,7 @@ * "langcode" = "langcode", * }, * links = { + * "add-form" = "/entity_test_mulrev/add", * "canonical" = "/entity_test_mulrev/manage/{entity_test_mulrev}", * "delete-form" = "/entity_test/delete/entity_test_mulrev/{entity_test_mulrev}", * "edit-form" = "/entity_test_mulrev/manage/{entity_test_mulrev}/edit", diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChanged.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChanged.php index f2a9667..316e206 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChanged.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChanged.php @@ -43,6 +43,7 @@ * "langcode" = "langcode", * }, * links = { + * "add-form" = "/entity_test_mulrev_changed/add", * "canonical" = "/entity_test_mulrev_changed/manage/{entity_test_mulrev_changed}", * "delete-form" = "/entity_test/delete/entity_test_mulrev_changed/{entity_test_mulrev_changed}", * "edit-form" = "/entity_test_mulrev_changed/manage/{entity_test_mulrev_changed}/edit", diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestNoId.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestNoId.php index a29c1e2..bc0661e 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestNoId.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestNoId.php @@ -19,7 +19,11 @@ * entity_keys = { * "bundle" = "type", * }, + * admin_permission = "administer entity_test content", * field_ui_base_route = "entity.entity_test_no_id.admin_form", + * links = { + * "add-form" = "/entity_test_no_id/add", + * }, * ) */ class EntityTestNoId extends EntityTest { diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php index 047b85d..7aed50a 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php @@ -42,6 +42,7 @@ * "langcode" = "langcode", * }, * links = { + * "add-form" = "/entity_test_rev/add", * "canonical" = "/entity_test_rev/manage/{entity_test_rev}", * "delete-form" = "/entity_test/delete/entity_test_rev/{entity_test_rev}", * "edit-form" = "/entity_test_rev/manage/{entity_test_rev}/edit", diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestStringId.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestStringId.php index 1f53e04..41f793c 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestStringId.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestStringId.php @@ -34,6 +34,7 @@ * }, * links = { * "canonical" = "/entity_test_string_id/manage/{entity_test_string_id}", + * "add-form" = "/entity_test_string_id/add", * "edit-form" = "/entity_test_string_id/manage/{entity_test_string_id}", * }, * field_ui_base_route = "entity.entity_test_string_id.admin_form", diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestViewBuilder.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestViewBuilder.php index bc83ceb..7381587 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestViewBuilder.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestViewBuilder.php @@ -21,8 +21,10 @@ * render_cache = FALSE, * entity_keys = { * "id" = "id", + * "uuid" = "uuid", * "label" = "name", - * "bundle" = "type" + * "bundle" = "type", + * "langcode" = "langcode", * } * ) */ diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithBundle.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithBundle.php new file mode 100644 index 0000000..c06c8e3 --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithBundle.php @@ -0,0 +1,77 @@ +setLabel(t('Name')) + ->setDescription(t('The name of the test entity.')) + ->setTranslatable(TRUE) + ->setSetting('max_length', 32) + ->setDisplayOptions('view', [ + 'label' => 'hidden', + 'type' => 'string', + 'weight' => -5, + ]) + ->setDisplayOptions('form', [ + 'type' => 'string_textfield', + 'weight' => -5, + ]); + return $fields; + } + +} diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php new file mode 100644 index 0000000..2216f1f --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php @@ -0,0 +1,53 @@ +isDefaultTranslation()) { return AccessResult::allowedIfHasPermission($account, 'view test entity translations'); } diff --git a/core/modules/system/tests/modules/entity_test/src/Routing/EntityTestRoutes.php b/core/modules/system/tests/modules/entity_test/src/Routing/EntityTestRoutes.php index 8992550..b607f9f 100644 --- a/core/modules/system/tests/modules/entity_test/src/Routing/EntityTestRoutes.php +++ b/core/modules/system/tests/modules/entity_test/src/Routing/EntityTestRoutes.php @@ -22,17 +22,9 @@ class EntityTestRoutes { */ public function routes() { $types = entity_test_entity_types(ENTITY_TEST_TYPES_ROUTING); - $types[] = 'entity_test_string_id'; - $types[] = 'entity_test_no_id'; $routes = array(); foreach ($types as $entity_type_id) { - $routes["entity.$entity_type_id.add_form"] = new Route( - "$entity_type_id/add", - array('_controller' => '\Drupal\entity_test\Controller\EntityTestController::testAdd', 'entity_type_id' => $entity_type_id), - array('_permission' => 'administer entity_test content') - ); - $routes["entity.$entity_type_id.admin_form"] = new Route( "$entity_type_id/structure/{bundle}", array('_controller' => '\Drupal\entity_test\Controller\EntityTestController::testAdmin'), diff --git a/core/modules/system/tests/modules/experimental_module_dependency_test/experimental_module_dependency_test.info.yml b/core/modules/system/tests/modules/experimental_module_dependency_test/experimental_module_dependency_test.info.yml new file mode 100644 index 0000000..665417f --- /dev/null +++ b/core/modules/system/tests/modules/experimental_module_dependency_test/experimental_module_dependency_test.info.yml @@ -0,0 +1,8 @@ +name: 'Experimental Dependency Test' +type: module +description: 'Module with a dependency in the experimental package.' +package: Testing +dependencies: + - experimental_module_test +version: VERSION +core: 8.x diff --git a/core/modules/system/tests/modules/experimental_module_dependency_test/experimental_module_dependency_test.module b/core/modules/system/tests/modules/experimental_module_dependency_test/experimental_module_dependency_test.module new file mode 100644 index 0000000..6136004 --- /dev/null +++ b/core/modules/system/tests/modules/experimental_module_dependency_test/experimental_module_dependency_test.module @@ -0,0 +1,6 @@ +online documentation for the Experimental Test module.', [':url' => 'http://www.example.com']); + } + +} diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module index ee4c137..7114a29 100644 --- a/core/modules/system/tests/modules/form_test/form_test.module +++ b/core/modules/system/tests/modules/form_test/form_test.module @@ -104,3 +104,10 @@ function form_test_form_form_test_vertical_tabs_access_form_alter(&$form, &$form $form['fieldset1']['#access'] = FALSE; $form['container']['#access'] = FALSE; } + +/** + * Ajax callback that returns the form element. + */ +function form_test_tableselect_ajax_callback($form, FormStateInterface $form_state) { + return $form['tableselect']; +} diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestLabelForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestLabelForm.php index 75234b0..ae57d4e 100644 --- a/core/modules/system/tests/modules/form_test/src/Form/FormTestLabelForm.php +++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestLabelForm.php @@ -108,7 +108,26 @@ public function buildForm(array $form, FormStateInterface $form_state) { ), '#required' => TRUE, ); - + $form['form_checkboxes_title_invisible'] = array( + '#type' => 'checkboxes', + '#title' => 'Checkboxes test invisible', + '#title_display' => 'invisible', + '#options' => array( + 'first-checkbox' => 'First checkbox', + 'second-checkbox' => 'Second checkbox', + ), + '#required' => TRUE, + ); + $form['form_radios_title_invisible'] = array( + '#type' => 'radios', + '#title' => 'Radios test invisible', + '#title_display' => 'invisible', + '#options' => array( + 'first-radio' => 'First radio', + 'second-radio' => 'Second radio', + ), + '#required' => TRUE, + ); return $form; } diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestTableSelectFormBase.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestTableSelectFormBase.php index f618c92..5c3b383 100644 --- a/core/modules/system/tests/modules/form_test/src/Form/FormTestTableSelectFormBase.php +++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestTableSelectFormBase.php @@ -34,11 +34,17 @@ function tableselectFormBuilder($form, FormStateInterface $form_state, $element_ $form['tableselect'] = $element_properties; $form['tableselect'] += array( + '#prefix' => '
                          ', + '#suffix' => '
                          ', '#type' => 'tableselect', '#header' => $header, '#options' => $options, '#multiple' => FALSE, '#empty' => t('Empty text.'), + '#ajax' => array( + 'callback' => 'form_test_tableselect_ajax_callback', + 'wrapper' => 'tableselect-wrapper', + ), ); $form['submit'] = array( diff --git a/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/DerivedToolkit.php b/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/DerivedToolkit.php new file mode 100644 index 0000000..2df5c2b --- /dev/null +++ b/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/DerivedToolkit.php @@ -0,0 +1,18 @@ +

                          '); } + public function test25() { + return [ + '#cache' => [ + 'url', + ], + '#markup' => \Drupal::requestStack()->getCurrentRequest()->getUri(), + ]; + } + /** * Throws an exception. * diff --git a/core/modules/system/tests/modules/session_exists_cache_context_test/session_exists_cache_context_test.info.yml b/core/modules/system/tests/modules/session_exists_cache_context_test/session_exists_cache_context_test.info.yml new file mode 100644 index 0000000..b376a57 --- /dev/null +++ b/core/modules/system/tests/modules/session_exists_cache_context_test/session_exists_cache_context_test.info.yml @@ -0,0 +1,6 @@ +name: 'session.exists cache context test' +type: module +description: 'Support module for session.exists cache context testing.' +package: Testing +version: VERSION +core: 8.x diff --git a/core/modules/system/tests/modules/session_exists_cache_context_test/session_exists_cache_context_test.module b/core/modules/system/tests/modules/session_exists_cache_context_test/session_exists_cache_context_test.module new file mode 100644 index 0000000..60d2452 --- /dev/null +++ b/core/modules/system/tests/modules/session_exists_cache_context_test/session_exists_cache_context_test.module @@ -0,0 +1,29 @@ +hasSession($request); + $page_top['session_exists_cache_context_test'] = [ + 'label' => [ + '#markup' => '

                          ' . ($session_exists ? 'Session exists!' : 'Session does not exist!') . '

                          ', + ], + 'cache_context_value' => [ + '#markup' => '[session.exists]=' . \Drupal::service('cache_context.session.exists')->getContext() . '', + ], + ]; + + if (\Drupal::request()->query->get('trigger_session')) { + $_SESSION['session_exists_cache_context_test'] = TRUE; + } +} diff --git a/core/modules/system/tests/modules/session_test/session_test.module b/core/modules/system/tests/modules/session_test/session_test.module index 6a08211..b3ceff8 100644 --- a/core/modules/system/tests/modules/session_test/session_test.module +++ b/core/modules/system/tests/modules/session_test/session_test.module @@ -1,6 +1,11 @@ TRUE))->toString(); - system_authorized_init('system_test_authorize_run', drupal_get_path('module', 'system_test') . '/system_test.module', array(), $page_title); + system_authorized_init('system_test_authorize_run', __DIR__ . '/../../system_test.module', array(), $page_title); return new RedirectResponse($authorize_url); } diff --git a/core/modules/system/tests/modules/system_test/system_test.module b/core/modules/system/tests/modules/system_test/system_test.module index eef3516..04fa2da 100644 --- a/core/modules/system/tests/modules/system_test/system_test.module +++ b/core/modules/system/tests/modules/system_test/system_test.module @@ -1,5 +1,10 @@ container->get('module_handler')->getModuleList()); $this->assertEqual($expected_values, $enabled_modules, format_string('@condition: extension handler returns correct results', array('@condition' => $condition))); @@ -238,7 +239,7 @@ function testUninstallContentDependency() { drupal_static_reset('system_rebuild_module_data'); // Create an entity so that the modules can not be disabled. - $entity = entity_create('entity_test', array('name' => $this->randomString())); + $entity = EntityTest::create(array('name' => $this->randomString())); $entity->save(); // Uninstalling entity_test is not possible when there is content. diff --git a/core/modules/system/tests/src/Kernel/Migrate/MigrateMenuTest.php b/core/modules/system/tests/src/Kernel/Migrate/MigrateMenuTest.php new file mode 100644 index 0000000..0c2f85b --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/MigrateMenuTest.php @@ -0,0 +1,58 @@ +executeMigration('menu'); + } + + /** + * Tests the Drupal 6 menu to Drupal 8 migration. + */ + public function testMenu() { + $navigation_menu = Menu::load('navigation'); + $this->assertIdentical('navigation', $navigation_menu->id()); + $this->assertIdentical('Navigation', $navigation_menu->label()); + $expected = <<assertIdentical($expected, $navigation_menu->getDescription()); + + // Test that we can re-import using the ConfigEntityBase destination. + Database::getConnection('default', 'migrate') + ->update('menu_custom') + ->fields(array('title' => 'Home Navigation')) + ->condition('menu_name', 'navigation') + ->execute(); + + $migration = $this->getMigration('menu'); + \Drupal::database() + ->truncate($migration->getIdMap()->mapTableName()) + ->execute(); + $this->executeMigration($migration); + + $navigation_menu = Menu::load('navigation'); + $this->assertIdentical('Home Navigation', $navigation_menu->label()); + } + +} diff --git a/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateDateFormatTest.php b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateDateFormatTest.php new file mode 100644 index 0000000..19dfe26 --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateDateFormatTest.php @@ -0,0 +1,61 @@ +executeMigration('d6_date_formats'); + } + + /** + * Tests the Drupal 6 date formats to Drupal 8 migration. + */ + public function testDateFormats() { + $short_date_format = DateFormat::load('short'); + $this->assertIdentical('\S\H\O\R\T m/d/Y - H:i', $short_date_format->getPattern()); + + $medium_date_format = DateFormat::load('medium'); + $this->assertIdentical('\M\E\D\I\U\M D, m/d/Y - H:i', $medium_date_format->getPattern()); + + $long_date_format = DateFormat::load('long'); + $this->assertIdentical('\L\O\N\G l, F j, Y - H:i', $long_date_format->getPattern()); + + // Test that we can re-import using the EntityDateFormat destination. + Database::getConnection('default', 'migrate') + ->update('variable') + ->fields(array('value' => serialize('\S\H\O\R\T d/m/Y - H:i'))) + ->condition('name', 'date_format_short') + ->execute(); + + $migration = $this->getMigration('d6_date_formats'); + \Drupal::database() + ->truncate($migration->getIdMap()->mapTableName()) + ->execute(); + + $this->executeMigration($migration); + + $short_date_format = DateFormat::load('short'); + $this->assertIdentical('\S\H\O\R\T d/m/Y - H:i', $short_date_format->getPattern()); + + } + +} diff --git a/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemCronTest.php b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemCronTest.php new file mode 100644 index 0000000..29b5522 --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemCronTest.php @@ -0,0 +1,36 @@ +executeMigration('d6_system_cron'); + } + + /** + * Tests migration of system (cron) variables to system.cron.yml. + */ + public function testSystemCron() { + $config = $this->config('system.cron'); + $this->assertIdentical(172800, $config->get('threshold.requirements_warning')); + $this->assertIdentical(1209600, $config->get('threshold.requirements_error')); + } + +} diff --git a/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemDateTest.php b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemDateTest.php new file mode 100644 index 0000000..71d4ef3 --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemDateTest.php @@ -0,0 +1,37 @@ +executeMigration('d6_system_date'); + } + + /** + * Tests migration of user variables to system_date.yml. + */ + public function testSystemDate() { + $config = $this->config('system.date'); + $this->assertIdentical(4, $config->get('first_day')); + $this->assertIdentical(FALSE, $config->get('timezone.user.configurable')); + $this->assertIdentical("Europe/Paris", $config->get('timezone.default')); + } + +} diff --git a/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemFileTest.php b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemFileTest.php new file mode 100644 index 0000000..7312168 --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemFileTest.php @@ -0,0 +1,36 @@ +executeMigration('d6_system_file'); + } + + /** + * Tests migration of system (file) variables to system.file.yml. + */ + public function testSystemFile() { + $config = \Drupal::configFactory()->getEditable('system.file'); + $this->assertIdentical('files/temp', $config->get('path.temporary')); + $this->assertIdentical(TRUE, $config->get('allow_insecure_uploads')); + } + +} diff --git a/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemImageGdTest.php b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemImageGdTest.php new file mode 100644 index 0000000..1b8ed5d --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemImageGdTest.php @@ -0,0 +1,35 @@ +executeMigration('d6_system_image_gd'); + } + + /** + * Tests migration of system (image GD) variables to system.image.gd.yml. + */ + public function testSystemImageGd() { + $config = $this->config('system.image.gd'); + $this->assertIdentical(75, $config->get('jpeg_quality')); + } + +} diff --git a/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemImageTest.php b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemImageTest.php new file mode 100644 index 0000000..655c1df --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemImageTest.php @@ -0,0 +1,35 @@ +executeMigration('d6_system_image'); + } + + /** + * Tests migration of system (image) variables to system.image.yml. + */ + public function testSystemImage() { + $config = $this->config('system.image'); + $this->assertIdentical('gd', $config->get('toolkit')); + } + +} diff --git a/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemLoggingTest.php b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemLoggingTest.php new file mode 100644 index 0000000..36c01ff --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemLoggingTest.php @@ -0,0 +1,39 @@ +executeMigration('d6_system_logging'); + } + + /** + * Tests migration of system error_level variables to system.logging.yml. + */ + public function testSystemLogging() { + $config = $this->config('system.logging'); + $this->assertIdentical('some', $config->get('error_level')); + $this->assertConfigSchema(\Drupal::service('config.typed'), 'system.logging', $config->get()); + } + +} diff --git a/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemMaintenanceTest.php b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemMaintenanceTest.php new file mode 100644 index 0000000..03be109 --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemMaintenanceTest.php @@ -0,0 +1,35 @@ +executeMigration('d6_system_maintenance'); + } + + /** + * Tests migration of system (maintenance) variables to system.maintenance.yml. + */ + public function testSystemMaintenance() { + $config = $this->config('system.maintenance'); + $this->assertIdentical('Drupal is currently under maintenance. We should be back shortly. Thank you for your patience.', $config->get('message')); + } + +} diff --git a/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemPerformanceTest.php b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemPerformanceTest.php new file mode 100644 index 0000000..33f8b50 --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemPerformanceTest.php @@ -0,0 +1,37 @@ +executeMigration('d6_system_performance'); + } + + /** + * Tests migration of system (Performance) variables to system.performance.yml. + */ + public function testSystemPerformance() { + $config = $this->config('system.performance'); + $this->assertIdentical(FALSE, $config->get('css.preprocess')); + $this->assertIdentical(FALSE, $config->get('js.preprocess')); + $this->assertIdentical(0, $config->get('cache.page.max_age')); + } + +} diff --git a/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemRssTest.php b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemRssTest.php new file mode 100644 index 0000000..0c4a75a --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemRssTest.php @@ -0,0 +1,36 @@ +executeMigration('d6_system_rss'); + } + + /** + * Tests migration of system (rss) variables to system.rss.yml. + */ + public function testSystemRss() { + $config = $this->config('system.rss'); + $this->assertIdentical(10, $config->get('items.limit')); + $this->assertIdentical('title', $config->get('items.view_mode')); + } + +} diff --git a/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemSiteTest.php b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemSiteTest.php new file mode 100644 index 0000000..eac379e --- /dev/null +++ b/core/modules/system/tests/src/Kernel/Migrate/d6/MigrateSystemSiteTest.php @@ -0,0 +1,41 @@ +executeMigration('d6_system_site'); + } + + /** + * Tests migration of system (site) variables to system.site.yml. + */ + public function testSystemSite() { + $config = $this->config('system.site'); + $this->assertIdentical('site_name', $config->get('name')); + $this->assertIdentical('site_mail@example.com', $config->get('mail')); + $this->assertIdentical('Migrate rocks', $config->get('slogan')); + $this->assertIdentical('/user', $config->get('page.403')); + $this->assertIdentical('/page-not-found', $config->get('page.404')); + $this->assertIdentical('/node', $config->get('page.front')); + $this->assertIdentical(FALSE, $config->get('admin_compact_mode')); + } + +} diff --git a/core/modules/system/tests/src/Kernel/PathHooksTest.php b/core/modules/system/tests/src/Kernel/PathHooksTest.php index 272875b..594a6fe 100644 --- a/core/modules/system/tests/src/Kernel/PathHooksTest.php +++ b/core/modules/system/tests/src/Kernel/PathHooksTest.php @@ -26,8 +26,6 @@ class PathHooksTest extends KernelTestBase { * Test system_path_*() correctly clears caches. */ public function testPathHooks() { - $this->installSchema('system', ['url_alias']); - $source = '/' . $this->randomMachineName(); $alias = '/' . $this->randomMachineName(); diff --git a/core/modules/system/tests/src/Kernel/PhpStorage/PhpStorageFactoryTest.php b/core/modules/system/tests/src/Kernel/PhpStorage/PhpStorageFactoryTest.php index 47804ab..de76f49 100644 --- a/core/modules/system/tests/src/Kernel/PhpStorage/PhpStorageFactoryTest.php +++ b/core/modules/system/tests/src/Kernel/PhpStorage/PhpStorageFactoryTest.php @@ -65,18 +65,28 @@ public function testGetOverride() { $this->setSettings('test', array('bin' => NULL)); $php = PhpStorageFactory::get('test'); $this->assertTrue($php instanceof MockPhpStorage, 'An MockPhpStorage instance was returned from overridden settings.'); - $this->assertIdentical('test', $php->getConfigurationValue('bin'), 'Name value was used for bin.'); + $this->assertSame('test', $php->getConfigurationValue('bin'), 'Name value was used for bin.'); // Test that a default directory is set if it's empty. $this->setSettings('test', array('directory' => NULL)); $php = PhpStorageFactory::get('test'); $this->assertTrue($php instanceof MockPhpStorage, 'An MockPhpStorage instance was returned from overridden settings.'); - $this->assertIdentical(PublicStream::basePath() . '/php', $php->getConfigurationValue('directory'), 'Default file directory was used.'); + $this->assertSame(PublicStream::basePath() . '/php', $php->getConfigurationValue('directory'), 'Default file directory was used.'); // Test that a default storage class is set if it's empty. $this->setSettings('test', array('class' => NULL)); $php = PhpStorageFactory::get('test'); $this->assertTrue($php instanceof MTimeProtectedFileStorage, 'An MTimeProtectedFileStorage instance was returned from overridden settings with no class.'); + + // Test that a default secret is not returned if it's set in the override. + $this->setSettings('test'); + $php = PhpStorageFactory::get('test'); + $this->assertNotEquals('mock hash salt', $php->getConfigurationValue('secret'), 'The default secret is not used if a secret is set in the overridden settings.'); + + // Test that a default secret is set if it's empty. + $this->setSettings('test', array('secret' => NULL)); + $php = PhpStorageFactory::get('test'); + $this->assertSame('mock hash salt', $php->getConfigurationValue('secret'), 'The default secret is used if one is not set in the overridden settings.'); } /** @@ -94,6 +104,7 @@ protected function setSettings($name = 'default', array $configuration = array() 'secret' => $this->randomString(), 'bin' => 'test', ); + $settings['hash_salt'] = 'mock hash salt'; new Settings($settings); } diff --git a/core/modules/system/tests/src/Kernel/Scripts/DbDumpCommandTest.php b/core/modules/system/tests/src/Kernel/Scripts/DbDumpCommandTest.php index b58d408..885314e 100644 --- a/core/modules/system/tests/src/Kernel/Scripts/DbDumpCommandTest.php +++ b/core/modules/system/tests/src/Kernel/Scripts/DbDumpCommandTest.php @@ -35,7 +35,8 @@ protected function setUp() { $this->markTestSkipped("Skipping test since the DbDumpCommand is currently only compatible with MySQL"); } - $this->installSchema('system', 'router'); + // Rebuild the router to ensure a routing table. + \Drupal::service('router.builder')->rebuild(); /** @var \Drupal\Core\Database\Connection $connection */ $connection = $this->container->get('database'); diff --git a/core/modules/system/tests/src/Kernel/Scripts/DbImportCommandTest.php b/core/modules/system/tests/src/Kernel/Scripts/DbImportCommandTest.php index 8cf23d5..446d3d8 100644 --- a/core/modules/system/tests/src/Kernel/Scripts/DbImportCommandTest.php +++ b/core/modules/system/tests/src/Kernel/Scripts/DbImportCommandTest.php @@ -42,7 +42,6 @@ class DbImportCommandTest extends KernelTestBase { 'key_value_expire', 'menu_link_content', 'menu_link_content_data', - 'semaphore', 'sessions', 'url_alias', 'user__roles', diff --git a/core/modules/system/tests/themes/test_basetheme/test_basetheme.theme b/core/modules/system/tests/themes/test_basetheme/test_basetheme.theme index 1467e49..42e543b 100644 --- a/core/modules/system/tests/themes/test_basetheme/test_basetheme.theme +++ b/core/modules/system/tests/themes/test_basetheme/test_basetheme.theme @@ -4,6 +4,7 @@ * @file * Add hooks for tests to use. */ + use Drupal\views\Plugin\views\cache\CachePluginBase; use Drupal\views\ViewExecutable; diff --git a/core/modules/system/tests/themes/test_theme/test_theme.info.yml b/core/modules/system/tests/themes/test_theme/test_theme.info.yml index fca49c2..1c9745f 100644 --- a/core/modules/system/tests/themes/test_theme/test_theme.info.yml +++ b/core/modules/system/tests/themes/test_theme/test_theme.info.yml @@ -15,7 +15,7 @@ version: VERSION base theme: classy core: 8.x stylesheets-remove: - - '@system/css/system.module.css' + - '@system/css/js.module.css' libraries: - test_theme/global-styling libraries-override: diff --git a/core/modules/system/tests/themes/test_theme_nyan_cat_engine/test_theme_nyan_cat_engine.theme b/core/modules/system/tests/themes/test_theme_nyan_cat_engine/test_theme_nyan_cat_engine.theme index 5b69a12..ee2748a 100644 --- a/core/modules/system/tests/themes/test_theme_nyan_cat_engine/test_theme_nyan_cat_engine.theme +++ b/core/modules/system/tests/themes/test_theme_nyan_cat_engine/test_theme_nyan_cat_engine.theme @@ -1,6 +1,11 @@ t('Vocabularies'); - // @todo: Currently allow auto-create only on taxonomy terms. - $form['auto_create'] = array( - '#type' => 'checkbox', - '#title' => $this->t("Create referenced entities if they don't already exist"), - '#default_value' => isset($this->configuration['handler_settings']['auto_create']) ? $this->configuration['handler_settings']['auto_create'] : FALSE, - ); + $form['target_bundles']['#title'] = $this->t('Available Vocabularies'); // Sorting is not possible for taxonomy terms because we use // \Drupal\taxonomy\TermStorageInterface::loadTree() to retrieve matches. diff --git a/core/modules/taxonomy/src/Plugin/migrate/D6TermNodeDeriver.php b/core/modules/taxonomy/src/Plugin/migrate/D6TermNodeDeriver.php new file mode 100644 index 0000000..245c026 --- /dev/null +++ b/core/modules/taxonomy/src/Plugin/migrate/D6TermNodeDeriver.php @@ -0,0 +1,78 @@ +basePluginId = $base_plugin_id; + $this->migrationPluginManager = $migration_plugin_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static( + $base_plugin_id, + $container->get('plugin.manager.migration') + ); + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions($base_plugin_definition, $base_plugin_definitions = NULL) { + try { + foreach (static::getSourcePlugin('d6_taxonomy_vocabulary') as $row) { + $source_vid = $row->getSourceProperty('vid'); + $definition = $base_plugin_definition; + $definition['source']['vid'] = $source_vid; + // migrate_drupal_migration_plugins_alter() adds to this definition. + $this->derivatives[$source_vid] = $definition; + } + } + catch (\Exception $e) { + // It is possible no D6 tables are loaded so just eat exceptions. + } + return $this->derivatives; + } + +} diff --git a/core/modules/taxonomy/src/Plugin/migrate/builder/d6/TermNode.php b/core/modules/taxonomy/src/Plugin/migrate/builder/d6/TermNode.php deleted file mode 100644 index d64ee23..0000000 --- a/core/modules/taxonomy/src/Plugin/migrate/builder/d6/TermNode.php +++ /dev/null @@ -1,107 +0,0 @@ -templateStorage = $template_storage; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('migrate.template_storage') - ); - } - - /** - * Builds a map of source vocabulary IDs to expected destination IDs. - * - * @param array $source - * Additional configuration for the d6_taxonomy_vocabulary source. - * - * @return array - * The vid map. The keys are the source IDs and the values are the - * (expected) destination IDs. - */ - protected function getVocabularyIdMap(array $source) { - $map = []; - - $template = $this->templateStorage->getTemplateByName('d6_taxonomy_vocabulary'); - $template['source'] += $source; - - $migration = Migration::create($template); - $executable = new MigrateExecutable($migration, new MigrateMessage()); - // Only process the destination ID properties. - $process = array_intersect_key($template['process'], $migration->getDestinationPlugin()->getIds()); - - foreach ($migration->getSourcePlugin() as $source_row) { - // Process the row to generate the expected destination ID. - $executable->processRow($source_row, $process); - $map[$source_row->getSourceProperty('vid')] = $source_row->getDestinationProperty('vid'); - } - - return $map; - } - - /** - * {@inheritdoc} - */ - public function buildMigrations(array $template) { - $migrations = []; - - foreach ($this->getVocabularyIdMap($template['source']) as $source_vid => $destination_vid) { - $values = $template; - $values['id'] .= '__' . $source_vid; - $values['source']['vid'] = $source_vid; - $migration = Migration::create($values); - $migration->setProcessOfProperty($destination_vid, 'tid'); - $migrations[] = $migration; - } - - return $migrations; - } - -} diff --git a/core/modules/taxonomy/src/Plugin/migrate/cckfield/TaxonomyTermReference.php b/core/modules/taxonomy/src/Plugin/migrate/cckfield/TaxonomyTermReference.php index d6de1ec..098cc2c 100644 --- a/core/modules/taxonomy/src/Plugin/migrate/cckfield/TaxonomyTermReference.php +++ b/core/modules/taxonomy/src/Plugin/migrate/cckfield/TaxonomyTermReference.php @@ -7,7 +7,7 @@ namespace Drupal\taxonomy\Plugin\migrate\cckfield; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate_drupal\Plugin\migrate\cckfield\CckFieldPluginBase; /** diff --git a/core/modules/taxonomy/src/TermViewsData.php b/core/modules/taxonomy/src/TermViewsData.php index 9a42a84..618b4d7 100644 --- a/core/modules/taxonomy/src/TermViewsData.php +++ b/core/modules/taxonomy/src/TermViewsData.php @@ -20,7 +20,7 @@ class TermViewsData extends EntityViewsData { public function getViewsData() { $data = parent::getViewsData(); - $data['taxonomy_term_field_data']['table']['base']['help'] = t('Taxonomy terms are attached to nodes.'); + $data['taxonomy_term_field_data']['table']['base']['help'] = $this->t('Taxonomy terms are attached to nodes.'); $data['taxonomy_term_field_data']['table']['base']['access query tag'] = 'term_access'; $data['taxonomy_term_field_data']['table']['wizard_id'] = 'taxonomy_term'; @@ -32,21 +32,21 @@ public function getViewsData() { ), ); - $data['taxonomy_term_field_data']['tid']['help'] = t('The tid of a taxonomy term.'); + $data['taxonomy_term_field_data']['tid']['help'] = $this->t('The tid of a taxonomy term.'); $data['taxonomy_term_field_data']['tid']['argument']['id'] = 'taxonomy'; $data['taxonomy_term_field_data']['tid']['argument']['name field'] = 'name'; $data['taxonomy_term_field_data']['tid']['argument']['zero is null'] = TRUE; $data['taxonomy_term_field_data']['tid']['filter']['id'] = 'taxonomy_index_tid'; - $data['taxonomy_term_field_data']['tid']['filter']['title'] = t('Term'); - $data['taxonomy_term_field_data']['tid']['filter']['help'] = t('Taxonomy term chosen from autocomplete or select widget.'); + $data['taxonomy_term_field_data']['tid']['filter']['title'] = $this->t('Term'); + $data['taxonomy_term_field_data']['tid']['filter']['help'] = $this->t('Taxonomy term chosen from autocomplete or select widget.'); $data['taxonomy_term_field_data']['tid']['filter']['hierarchy table'] = 'taxonomy_term_hierarchy'; $data['taxonomy_term_field_data']['tid']['filter']['numeric'] = TRUE; $data['taxonomy_term_field_data']['tid_raw'] = array( - 'title' => t('Term ID'), - 'help' => t('The tid of a taxonomy term.'), + 'title' => $this->t('Term ID'), + 'help' => $this->t('The tid of a taxonomy term.'), 'real field' => 'tid', 'filter' => array( 'id' => 'numeric', @@ -56,9 +56,9 @@ public function getViewsData() { $data['taxonomy_term_field_data']['tid_representative'] = array( 'relationship' => array( - 'title' => t('Representative node'), - 'label' => t('Representative node'), - 'help' => t('Obtains a single representative node for each term, according to a chosen sort criterion.'), + 'title' => $this->t('Representative node'), + 'label' => $this->t('Representative node'), + 'help' => $this->t('Obtains a single representative node for each term, according to a chosen sort criterion.'), 'id' => 'groupwise_max', 'relationship field' => 'tid', 'outer field' => 'taxonomy_term_field_data.tid', @@ -70,23 +70,23 @@ public function getViewsData() { ), ); - $data['taxonomy_term_field_data']['vid']['help'] = t('Filter the results of "Taxonomy: Term" to a particular vocabulary.'); + $data['taxonomy_term_field_data']['vid']['help'] = $this->t('Filter the results of "Taxonomy: Term" to a particular vocabulary.'); unset($data['taxonomy_term_field_data']['vid']['field']); unset($data['taxonomy_term_field_data']['vid']['argument']); unset($data['taxonomy_term_field_data']['vid']['sort']); $data['taxonomy_term_field_data']['name']['field']['id'] = 'term_name'; $data['taxonomy_term_field_data']['name']['argument']['many to one'] = TRUE; - $data['taxonomy_term_field_data']['name']['argument']['empty field name'] = t('Uncategorized'); + $data['taxonomy_term_field_data']['name']['argument']['empty field name'] = $this->t('Uncategorized'); $data['taxonomy_term_field_data']['description__value']['field']['click sortable'] = FALSE; - $data['taxonomy_term_field_data']['changed']['title'] = t('Updated date'); - $data['taxonomy_term_field_data']['changed']['help'] = t('The date the term was last updated.'); + $data['taxonomy_term_field_data']['changed']['title'] = $this->t('Updated date'); + $data['taxonomy_term_field_data']['changed']['help'] = $this->t('The date the term was last updated.'); $data['taxonomy_term_field_data']['changed_fulldate'] = array( - 'title' => t('Updated date'), - 'help' => t('Date in the form of CCYYMMDD.'), + 'title' => $this->t('Updated date'), + 'help' => $this->t('Date in the form of CCYYMMDD.'), 'argument' => array( 'field' => 'changed', 'id' => 'date_fulldate', @@ -94,8 +94,8 @@ public function getViewsData() { ); $data['taxonomy_term_field_data']['changed_year_month'] = array( - 'title' => t('Updated year + month'), - 'help' => t('Date in the form of YYYYMM.'), + 'title' => $this->t('Updated year + month'), + 'help' => $this->t('Date in the form of YYYYMM.'), 'argument' => array( 'field' => 'changed', 'id' => 'date_year_month', @@ -103,8 +103,8 @@ public function getViewsData() { ); $data['taxonomy_term_field_data']['changed_year'] = array( - 'title' => t('Updated year'), - 'help' => t('Date in the form of YYYY.'), + 'title' => $this->t('Updated year'), + 'help' => $this->t('Date in the form of YYYY.'), 'argument' => array( 'field' => 'changed', 'id' => 'date_year', @@ -112,8 +112,8 @@ public function getViewsData() { ); $data['taxonomy_term_field_data']['changed_month'] = array( - 'title' => t('Updated month'), - 'help' => t('Date in the form of MM (01 - 12).'), + 'title' => $this->t('Updated month'), + 'help' => $this->t('Date in the form of MM (01 - 12).'), 'argument' => array( 'field' => 'changed', 'id' => 'date_month', @@ -121,8 +121,8 @@ public function getViewsData() { ); $data['taxonomy_term_field_data']['changed_day'] = array( - 'title' => t('Updated day'), - 'help' => t('Date in the form of DD (01 - 31).'), + 'title' => $this->t('Updated day'), + 'help' => $this->t('Date in the form of DD (01 - 31).'), 'argument' => array( 'field' => 'changed', 'id' => 'date_day', @@ -130,15 +130,15 @@ public function getViewsData() { ); $data['taxonomy_term_field_data']['changed_week'] = array( - 'title' => t('Updated week'), - 'help' => t('Date in the form of WW (01 - 53).'), + 'title' => $this->t('Updated week'), + 'help' => $this->t('Date in the form of WW (01 - 53).'), 'argument' => array( 'field' => 'changed', 'id' => 'date_week', ), ); - $data['taxonomy_index']['table']['group'] = t('Taxonomy term'); + $data['taxonomy_index']['table']['group'] = $this->t('Taxonomy term'); $data['taxonomy_index']['table']['join'] = array( 'taxonomy_term_field_data' => array( @@ -158,13 +158,13 @@ public function getViewsData() { ); $data['taxonomy_index']['nid'] = array( - 'title' => t('Content with term'), - 'help' => t('Relate all content tagged with a term.'), + 'title' => $this->t('Content with term'), + 'help' => $this->t('Relate all content tagged with a term.'), 'relationship' => array( 'id' => 'standard', 'base' => 'node', 'base field' => 'nid', - 'label' => t('node'), + 'label' => $this->t('node'), 'skip base' => 'node', ), ); @@ -172,19 +172,19 @@ public function getViewsData() { // @todo This stuff needs to move to a node field since really it's all // about nodes. $data['taxonomy_index']['tid'] = array( - 'group' => t('Content'), - 'title' => t('Has taxonomy term ID'), - 'help' => t('Display content if it has the selected taxonomy terms.'), + 'group' => $this->t('Content'), + 'title' => $this->t('Has taxonomy term ID'), + 'help' => $this->t('Display content if it has the selected taxonomy terms.'), 'argument' => array( 'id' => 'taxonomy_index_tid', 'name table' => 'taxonomy_term_field_data', 'name field' => 'name', - 'empty field name' => t('Uncategorized'), + 'empty field name' => $this->t('Uncategorized'), 'numeric' => TRUE, 'skip base' => 'taxonomy_term_field_data', ), 'filter' => array( - 'title' => t('Has taxonomy term'), + 'title' => $this->t('Has taxonomy term'), 'id' => 'taxonomy_index_tid', 'hierarchy table' => 'taxonomy_term_hierarchy', 'numeric' => TRUE, @@ -194,32 +194,32 @@ public function getViewsData() { ); $data['taxonomy_index']['status'] = [ - 'title' => t('Publish status'), - 'help' => t('Whether or not the content related to a term is published.'), + 'title' => $this->t('Publish status'), + 'help' => $this->t('Whether or not the content related to a term is published.'), 'filter' => [ 'id' => 'boolean', - 'label' => t('Published status'), + 'label' => $this->t('Published status'), 'type' => 'yes-no', ], ]; $data['taxonomy_index']['sticky'] = [ - 'title' => t('Sticky status'), - 'help' => t('Whether or not the content related to a term is sticky.'), + 'title' => $this->t('Sticky status'), + 'help' => $this->t('Whether or not the content related to a term is sticky.'), 'filter' => [ 'id' => 'boolean', - 'label' => t('Sticky status'), + 'label' => $this->t('Sticky status'), 'type' => 'yes-no', ], 'sort' => [ 'id' => 'standard', - 'help' => t('Whether or not the content related to a term is sticky. To list sticky content first, set this to descending.'), + 'help' => $this->t('Whether or not the content related to a term is sticky. To list sticky content first, set this to descending.'), ], ]; $data['taxonomy_index']['created'] = [ - 'title' => t('Post date'), - 'help' => t('The date the content related to a term was posted.'), + 'title' => $this->t('Post date'), + 'help' => $this->t('The date the content related to a term was posted.'), 'sort' => [ 'id' => 'date' ], @@ -228,7 +228,7 @@ public function getViewsData() { ], ]; - $data['taxonomy_term_hierarchy']['table']['group'] = t('Taxonomy term'); + $data['taxonomy_term_hierarchy']['table']['group'] = $this->t('Taxonomy term'); $data['taxonomy_term_hierarchy']['table']['provider'] = 'taxonomy'; $data['taxonomy_term_hierarchy']['table']['join'] = array( @@ -245,20 +245,20 @@ public function getViewsData() { ); $data['taxonomy_term_hierarchy']['parent'] = array( - 'title' => t('Parent term'), - 'help' => t('The parent term of the term. This can produce duplicate entries if you are using a vocabulary that allows multiple parents.'), + 'title' => $this->t('Parent term'), + 'help' => $this->t('The parent term of the term. This can produce duplicate entries if you are using a vocabulary that allows multiple parents.'), 'relationship' => array( 'base' => 'taxonomy_term_field_data', 'field' => 'parent', - 'label' => t('Parent'), + 'label' => $this->t('Parent'), 'id' => 'standard', ), 'filter' => array( - 'help' => t('Filter the results of "Taxonomy: Term" by the parent pid.'), + 'help' => $this->t('Filter the results of "Taxonomy: Term" by the parent pid.'), 'id' => 'numeric', ), 'argument' => array( - 'help' => t('The parent term of the term.'), + 'help' => $this->t('The parent term of the term.'), 'id' => 'taxonomy', ), ); diff --git a/core/modules/taxonomy/src/Tests/LegacyTest.php b/core/modules/taxonomy/src/Tests/LegacyTest.php index 06966b8..00216a1 100644 --- a/core/modules/taxonomy/src/Tests/LegacyTest.php +++ b/core/modules/taxonomy/src/Tests/LegacyTest.php @@ -9,6 +9,7 @@ use Drupal\Core\Datetime\DrupalDateTime; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use \Drupal\taxonomy\Entity\Vocabulary; /** * Posts an article with a taxonomy term and a date prior to 1970. @@ -28,10 +29,10 @@ protected function setUp() { parent::setUp(); // Create a tags vocabulary for the 'article' content type. - $vocabulary = entity_create('taxonomy_vocabulary', array( + $vocabulary = Vocabulary::create([ 'name' => 'Tags', 'vid' => 'tags', - )); + ]); $vocabulary->save(); $field_name = 'field_' . $vocabulary->id(); @@ -69,4 +70,5 @@ function testTaxonomyLegacyNode() { $node = $this->drupalGetNodeByTitle($edit['title[0][value]']); $this->assertEqual($node->getCreatedTime(), $date->getTimestamp(), 'Legacy node was saved with the right date.'); } + } diff --git a/core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyConfigsTest.php b/core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyConfigsTest.php deleted file mode 100644 index e70efe9..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyConfigsTest.php +++ /dev/null @@ -1,45 +0,0 @@ -executeMigration('taxonomy_settings'); - } - - /** - * Tests migration of taxonomy variables to taxonomy.settings.yml. - */ - public function testTaxonomySettings() { - $config = $this->config('taxonomy.settings'); - $this->assertIdentical(100, $config->get('terms_per_page_admin')); - $this->assertIdentical(FALSE, $config->get('override_selector')); - $this->assertConfigSchema(\Drupal::service('config.typed'), 'taxonomy.settings', $config->get()); - } - -} diff --git a/core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyTermStubTest.php b/core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyTermStubTest.php deleted file mode 100644 index a7cd9ca..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyTermStubTest.php +++ /dev/null @@ -1,118 +0,0 @@ -installEntitySchema('taxonomy_term'); - } - - /** - * Tests creation of taxonomy term stubs. - */ - public function testStub() { - Vocabulary::create([ - 'vid' => 'test_vocabulary', - 'name' => 'Test vocabulary', - ])->save(); - $this->performStubTest('taxonomy_term'); - } - - /** - * Tests creation of stubs when weight is mapped. - */ - public function testStubWithWeightMapping() { - // Create a vocabulary via migration for the terms to reference. - $vocabulary_data_rows = [ - ['id' => '1', 'name' => 'tags'], - ]; - $ids = ['id' => ['type' => 'integer']]; - $config = [ - 'id' => 'vocabularies', - 'migration_tags' => ['Stub test'], - 'source' => [ - 'plugin' => 'embedded_data', - 'data_rows' => $vocabulary_data_rows, - 'ids' => $ids, - ], - 'process' => [ - 'vid' => 'id', - 'name' => 'name', - ], - 'destination' => ['plugin' => 'entity:taxonomy_vocabulary'], - ]; - $vocabulary_migration = Migration::create($config); - $vocabulary_executable = new MigrateExecutable($vocabulary_migration, $this); - $vocabulary_executable->import(); - - // We have a term referencing an unmigrated parent, forcing a stub to be - // created. - $term_data_rows = [ - ['id' => '1', 'vocab' => '1', 'name' => 'music', 'parent' => '2'], - ]; - $ids = ['id' => ['type' => 'integer']]; - $config = [ - 'id' => 'terms', - 'migration_tags' => ['Import and rollback test'], - 'source' => [ - 'plugin' => 'embedded_data', - 'data_rows' => $term_data_rows, - 'ids' => $ids, - ], - 'process' => [ - 'tid' => 'id', - 'vid' => 'vocab', - 'name' => 'name', - 'weight' => 'weight', - 'parent' => [ - 'plugin' => 'migration', - 'migration' => 'terms', - 'source' => 'parent', - ], - ], - 'destination' => ['plugin' => 'entity:taxonomy_term'], - 'migration_dependencies' => ['required' => ['vocabularies']], - ]; - - $term_migration = Migration::create($config); - $term_migration->save(); - $term_executable = new MigrateExecutable($term_migration, $this); - $term_executable->import(); - // Load the referenced term, which should exist as a stub. - /** @var \Drupal\Core\Entity\ContentEntityBase $stub_entity */ - $stub_entity = Term::load(2); - $this->assertTrue($stub_entity, 'Stub successfully created'); - if ($stub_entity) { - $this->assertIdentical(count($stub_entity->validate()), 0, 'Stub is a valid entity'); - } - } -} diff --git a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTaxonomyTermTest.php b/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTaxonomyTermTest.php deleted file mode 100644 index 952b038..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTaxonomyTermTest.php +++ /dev/null @@ -1,97 +0,0 @@ -installEntitySchema('taxonomy_term'); - $this->executeMigrations(['d6_taxonomy_vocabulary', 'd6_taxonomy_term']); - } - - /** - * Tests the Drupal 6 taxonomy term to Drupal 8 migration. - */ - public function testTaxonomyTerms() { - $expected_results = array( - '1' => array( - 'source_vid' => 1, - 'vid' => 'vocabulary_1_i_0_', - 'weight' => 0, - 'parent' => array(0), - ), - '2' => array( - 'source_vid' => 2, - 'vid' => 'vocabulary_2_i_1_', - 'weight' => 3, - 'parent' => array(0), - ), - '3' => array( - 'source_vid' => 2, - 'vid' => 'vocabulary_2_i_1_', - 'weight' => 4, - 'parent' => array(2), - ), - '4' => array( - 'source_vid' => 3, - 'vid' => 'vocabulary_3_i_2_', - 'weight' => 6, - 'parent' => array(0), - ), - '5' => array( - 'source_vid' => 3, - 'vid' => 'vocabulary_3_i_2_', - 'weight' => 7, - 'parent' => array(4), - ), - '6' => array( - 'source_vid' => 3, - 'vid' => 'vocabulary_3_i_2_', - 'weight' => 8, - 'parent' => array(4, 5), - ), - ); - $terms = Term::loadMultiple(array_keys($expected_results)); - foreach ($expected_results as $tid => $values) { - /** @var Term $term */ - $term = $terms[$tid]; - $this->assertIdentical("term {$tid} of vocabulary {$values['source_vid']}", $term->name->value); - $this->assertIdentical("description of term {$tid} of vocabulary {$values['source_vid']}", $term->description->value); - $this->assertIdentical($values['vid'], $term->vid->target_id); - $this->assertIdentical((string) $values['weight'], $term->weight->value); - if ($values['parent'] === array(0)) { - $this->assertNull($term->parent->target_id); - } - else { - $parents = array(); - foreach (\Drupal::entityManager()->getStorage('taxonomy_term')->loadParents($tid) as $parent) { - $parents[] = (int) $parent->id(); - } - $this->assertIdentical($parents, $values['parent']); - } - } - } - -} diff --git a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTaxonomyVocabularyTest.php b/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTaxonomyVocabularyTest.php deleted file mode 100644 index b4d00c3..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTaxonomyVocabularyTest.php +++ /dev/null @@ -1,54 +0,0 @@ -executeMigration('d6_taxonomy_vocabulary'); - } - - /** - * Tests the Drupal 6 taxonomy vocabularies to Drupal 8 migration. - */ - public function testTaxonomyVocabulary() { - for ($i = 0; $i < 3; $i++) { - $j = $i + 1; - $vocabulary = Vocabulary::load("vocabulary_{$j}_i_{$i}_"); - $this->assertIdentical(Migration::load('d6_taxonomy_vocabulary')->getIdMap()->lookupDestinationID(array($j)), array($vocabulary->id())); - $this->assertIdentical("vocabulary $j (i=$i)", $vocabulary->label()); - $this->assertIdentical("description of vocabulary $j (i=$i)", $vocabulary->getDescription()); - $this->assertIdentical($i, $vocabulary->getHierarchy()); - $this->assertIdentical(4 + $i, $vocabulary->get('weight')); - } - $vocabulary = Vocabulary::load('vocabulary_name_much_longer_than'); - $this->assertIdentical('vocabulary name much longer than thirty two characters', $vocabulary->label()); - $this->assertIdentical('description of vocabulary name much longer than thirty two characters', $vocabulary->getDescription()); - $this->assertIdentical(3, $vocabulary->getHierarchy()); - $this->assertIdentical(7, $vocabulary->get('weight')); - } - -} diff --git a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTermNodeRevisionTest.php b/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTermNodeRevisionTest.php deleted file mode 100644 index 5dc9962..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTermNodeRevisionTest.php +++ /dev/null @@ -1,45 +0,0 @@ -installSchema('node', ['node_access']); - $this->migrateContent(TRUE); - $this->migrateTaxonomy(); - $this->executeMigrations(['d6_term_node:*', 'd6_term_node_revision:*']); - } - - /** - * Tests the Drupal 6 term-node revision association to Drupal 8 migration. - */ - public function testTermRevisionNode() { - $node = \Drupal::entityManager()->getStorage('node')->loadRevision(2); - $this->assertIdentical(2, count($node->vocabulary_3_i_2_)); - $this->assertIdentical('4', $node->vocabulary_3_i_2_[0]->target_id); - $this->assertIdentical('5', $node->vocabulary_3_i_2_[1]->target_id); - } - -} diff --git a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTermNodeTest.php b/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTermNodeTest.php deleted file mode 100644 index 8bf9e17..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateTermNodeTest.php +++ /dev/null @@ -1,74 +0,0 @@ -installSchema('node', ['node_access']); - $this->migrateContent(); - $this->migrateTaxonomy(); - } - - /** - * Tests the Drupal 6 term-node association to Drupal 8 migration. - */ - public function testTermNode() { - $this->executeMigrations(['d6_term_node:*']); - - $this->container->get('entity.manager') - ->getStorage('node') - ->resetCache([1, 2]); - - $nodes = Node::loadMultiple([1, 2]); - $node = $nodes[1]; - $this->assertIdentical(1, count($node->vocabulary_1_i_0_)); - $this->assertIdentical('1', $node->vocabulary_1_i_0_[0]->target_id); - $node = $nodes[2]; - $this->assertIdentical(2, count($node->vocabulary_2_i_1_)); - $this->assertIdentical('2', $node->vocabulary_2_i_1_[0]->target_id); - $this->assertIdentical('3', $node->vocabulary_2_i_1_[1]->target_id); - } - - /** - * Tests that term associations are ignored when they belong to nodes which - * were not migrated. - */ - public function testSkipNonExistentNode() { - // Node 2 is migrated by d6_node__story, but we need to pretend that it - // failed, so record that in the map table. - $this->mockFailure('d6_node__story', ['nid' => 2]); - - // d6_term_node__2 should skip over node 2 (a.k.a. revision 3) because, - // according to the map table, it failed. - $migration = Migration::load('d6_term_node__2'); - $this->executeMigration($migration); - $this->assertNull($migration->getIdMap()->lookupDestinationId(['vid' => 3])[0]); - } - -} diff --git a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateVocabularyEntityDisplayTest.php b/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateVocabularyEntityDisplayTest.php deleted file mode 100644 index 06ac310..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateVocabularyEntityDisplayTest.php +++ /dev/null @@ -1,46 +0,0 @@ -migrateTaxonomy(); - } - - /** - * Tests the Drupal 6 vocabulary-node type association to Drupal 8 migration. - */ - public function testVocabularyEntityDisplay() { - // Test that the field exists. - $component = EntityViewDisplay::load('node.page.default')->getComponent('tags'); - $this->assertIdentical('entity_reference_label', $component['type']); - $this->assertIdentical(20, $component['weight']); - // Test the Id map. - $this->assertIdentical(array('node', 'article', 'default', 'tags'), Migration::load('d6_vocabulary_entity_display')->getIdMap()->lookupDestinationID(array(4, 'article'))); - } - -} diff --git a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateVocabularyEntityFormDisplayTest.php b/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateVocabularyEntityFormDisplayTest.php deleted file mode 100644 index 7ecea1b..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateVocabularyEntityFormDisplayTest.php +++ /dev/null @@ -1,51 +0,0 @@ -migrateTaxonomy(); - } - - /** - * Tests the Drupal 6 vocabulary-node type association to Drupal 8 migration. - */ - public function testVocabularyEntityFormDisplay() { - // Test that the field exists. - $component = EntityFormDisplay::load('node.page.default')->getComponent('tags'); - $this->assertIdentical('options_select', $component['type']); - $this->assertIdentical(20, $component['weight']); - // Test the Id map. - $this->assertIdentical(array('node', 'article', 'default', 'tags'), Migration::load('d6_vocabulary_entity_form_display')->getIdMap()->lookupDestinationID(array(4, 'article'))); - - // Test the term widget tags setting. - $entity_form_display = EntityFormDisplay::load('node.story.default'); - $this->assertIdentical($entity_form_display->getComponent('vocabulary_1_i_0_')['type'], 'options_select'); - $this->assertIdentical($entity_form_display->getComponent('vocabulary_2_i_1_')['type'], 'entity_reference_autocomplete_tags'); - } - -} diff --git a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateVocabularyFieldInstanceTest.php b/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateVocabularyFieldInstanceTest.php deleted file mode 100644 index 8f2307d..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateVocabularyFieldInstanceTest.php +++ /dev/null @@ -1,66 +0,0 @@ -migrateTaxonomy(); - } - - /** - * Tests the Drupal 6 vocabulary-node type association to Drupal 8 migration. - */ - public function testVocabularyFieldInstance() { - // Test that the field exists. - $field_id = 'node.article.tags'; - $field = FieldConfig::load($field_id); - $this->assertIdentical($field_id, $field->id(), 'Field instance exists on article bundle.'); - $this->assertIdentical('Tags', $field->label()); - $this->assertTrue($field->isRequired(), 'Field is required'); - - // Test the page bundle as well. - $field_id = 'node.page.tags'; - $field = FieldConfig::load($field_id); - $this->assertIdentical($field_id, $field->id(), 'Field instance exists on page bundle.'); - $this->assertIdentical('Tags', $field->label()); - $this->assertTrue($field->isRequired(), 'Field is required'); - - $settings = $field->getSettings(); - $this->assertIdentical('default:taxonomy_term', $settings['handler'], 'The handler plugin ID is correct.'); - $this->assertIdentical(['tags'], $settings['handler_settings']['target_bundles'], 'The target_bundles handler setting is correct.'); - $this->assertIdentical(TRUE, $settings['handler_settings']['auto_create'], 'The "auto_create" setting is correct.'); - - $this->assertIdentical(array('node', 'article', 'tags'), Migration::load('d6_vocabulary_field_instance')->getIdMap()->lookupDestinationID(array(4, 'article'))); - - // Test the the field vocabulary_1_i_0_ - $field_id = 'node.story.vocabulary_1_i_0_'; - $field = FieldConfig::load($field_id); - $this->assertFalse($field->isRequired(), 'Field is not required'); - } - -} diff --git a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateVocabularyFieldTest.php b/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateVocabularyFieldTest.php deleted file mode 100644 index b8d99ed..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/d6/MigrateVocabularyFieldTest.php +++ /dev/null @@ -1,51 +0,0 @@ -migrateTaxonomy(); - } - - /** - * Tests the Drupal 6 vocabulary-node type association to Drupal 8 migration. - */ - public function testVocabularyField() { - // Test that the field exists. - $field_storage_id = 'node.tags'; - /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */ - $field_storage = FieldStorageConfig::load($field_storage_id); - $this->assertIdentical($field_storage_id, $field_storage->id()); - - $settings = $field_storage->getSettings(); - $this->assertIdentical('taxonomy_term', $settings['target_type'], "Target type is correct."); - $this->assertIdentical(1, $field_storage->getCardinality(), "Field cardinality in 1."); - - $this->assertIdentical(array('node', 'tags'), Migration::load('d6_vocabulary_field')->getIdMap()->lookupDestinationID(array(4)), "Test IdMap"); - } - -} diff --git a/core/modules/taxonomy/src/Tests/Migrate/d7/MigrateNodeTaxonomyTest.php b/core/modules/taxonomy/src/Tests/Migrate/d7/MigrateNodeTaxonomyTest.php deleted file mode 100644 index 8088c25..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/d7/MigrateNodeTaxonomyTest.php +++ /dev/null @@ -1,84 +0,0 @@ -installEntitySchema('node'); - $this->installEntitySchema('taxonomy_term'); - $this->installConfig(static::$modules); - $this->installSchema('node', ['node_access']); - $this->installSchema('system', ['sequences']); - - $this->executeMigration('d7_node_type'); - - FieldStorageConfig::create(array( - 'type' => 'entity_reference', - 'field_name' => 'field_tags', - 'entity_type' => 'node', - 'settings' => array( - 'target_type' => 'taxonomy_term', - ), - 'cardinality' => FieldStorageConfigInterface::CARDINALITY_UNLIMITED, - ))->save(); - - FieldConfig::create(array( - 'entity_type' => 'node', - 'field_name' => 'field_tags', - 'bundle' => 'article', - ))->save(); - - $this->executeMigrations([ - 'd7_taxonomy_vocabulary', - 'd7_taxonomy_term', - 'd7_user_role', - 'd7_user', - 'd7_node__article', - ]); - } - - /** - * Test node migration from Drupal 7 to 8. - */ - public function testMigration() { - $node = Node::load(2); - $this->assertTrue($node instanceof NodeInterface); - $this->assertEqual(9, $node->field_tags[0]->target_id); - $this->assertEqual(14, $node->field_tags[1]->target_id); - $this->assertEqual(17, $node->field_tags[2]->target_id); - } - -} diff --git a/core/modules/taxonomy/src/Tests/Migrate/d7/MigrateTaxonomyTermTest.php b/core/modules/taxonomy/src/Tests/Migrate/d7/MigrateTaxonomyTermTest.php deleted file mode 100644 index 64dabde..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/d7/MigrateTaxonomyTermTest.php +++ /dev/null @@ -1,86 +0,0 @@ -installEntitySchema('taxonomy_term'); - $this->executeMigrations(['d7_taxonomy_vocabulary', 'd7_taxonomy_term']); - } - - /** - * Validate a migrated term contains the expected values. - * - * @param $id - * Entity ID to load and check. - * @param $expected_label - * The label the migrated entity should have. - * @param $expected_vid - * The parent vocabulary the migrated entity should have. - * @param string $expected_description - * The description the migrated entity should have. - * @param int $expected_weight - * The weight the migrated entity should have. - * @param array $expected_parents - * The parent terms the migrated entity should have. - */ - protected function assertEntity($id, $expected_label, $expected_vid, $expected_description = '', $expected_weight = 0, $expected_parents = []) { - /** @var \Drupal\taxonomy\TermInterface $entity */ - $entity = Term::load($id); - $this->assertTrue($entity instanceof TermInterface); - $this->assertIdentical($expected_label, $entity->label()); - $this->assertIdentical($expected_vid, $entity->getVocabularyId()); - $this->assertEqual($expected_description, $entity->getDescription()); - $this->assertEqual($expected_weight, $entity->getWeight()); - $this->assertIdentical($expected_parents, $this->getParentIDs($id)); - } - - /** - * Tests the Drupal 7 taxonomy term to Drupal 8 migration. - */ - public function testTaxonomyTerms() { - $this->assertEntity(1, 'General discussion', 'forums', '', 2); - $this->assertEntity(2, 'Term1', 'test_vocabulary', 'The first term.'); - $this->assertEntity(3, 'Term2', 'test_vocabulary', 'The second term.'); - $this->assertEntity(4, 'Term3', 'test_vocabulary', 'The third term.', 0, [3]); - $this->assertEntity(5, 'Custom Forum', 'forums', 'Where the cool kids are.', 3); - $this->assertEntity(6, 'Games', 'forums', '', 4); - $this->assertEntity(7, 'Minecraft', 'forums', '', 1, [6]); - $this->assertEntity(8, 'Half Life 3', 'forums', '', 0, [6]); - } - - /** - * Retrieves the parent term IDs for a given term. - * - * @param $tid - * ID of the term to check. - * - * @return array - * List of parent term IDs. - */ - protected function getParentIDs($tid) { - return array_keys(\Drupal::entityManager()->getStorage('taxonomy_term')->loadParents($tid)); - } - -} diff --git a/core/modules/taxonomy/src/Tests/Migrate/d7/MigrateTaxonomyVocabularyTest.php b/core/modules/taxonomy/src/Tests/Migrate/d7/MigrateTaxonomyVocabularyTest.php deleted file mode 100644 index 5f4f6cd..0000000 --- a/core/modules/taxonomy/src/Tests/Migrate/d7/MigrateTaxonomyVocabularyTest.php +++ /dev/null @@ -1,67 +0,0 @@ -executeMigration('d7_taxonomy_vocabulary'); - } - - /** - * Validate a migrated vocabulary contains the expected values. - * - * @param $id - * Entity ID to load and check. - * @param $expected_label - * The label the migrated entity should have. - * @param $expected_description - * The description the migrated entity should have. - * @param $expected_hierarchy - * The hierarchy setting the migrated entity should have. - * @param $expected_weight - * The weight the migrated entity should have. - */ - protected function assertEntity($id, $expected_label, $expected_description, $expected_hierarchy, $expected_weight) { - /** @var \Drupal\taxonomy\VocabularyInterface $entity */ - $entity = Vocabulary::load($id); - $this->assertTrue($entity instanceof VocabularyInterface); - $this->assertIdentical($expected_label, $entity->label()); - $this->assertIdentical($expected_description, $entity->getDescription()); - $this->assertIdentical($expected_hierarchy, $entity->getHierarchy()); - $this->assertIdentical($expected_weight, $entity->get('weight')); - } - - /** - * Tests the Drupal 7 taxonomy vocabularies to Drupal 8 migration. - */ - public function testTaxonomyVocabulary() { - $this->assertEntity('tags', 'Tags', 'Use tags to group articles on similar topics into categories.', TAXONOMY_HIERARCHY_DISABLED, 0); - $this->assertEntity('forums', 'Forums', 'Forum navigation vocabulary', TAXONOMY_HIERARCHY_SINGLE, -10); - $this->assertEntity('test_vocabulary', 'Test Vocabulary', 'This is the vocabulary description', TAXONOMY_HIERARCHY_SINGLE, 0); - } - -} diff --git a/core/modules/taxonomy/src/Tests/TaxonomyImageTest.php b/core/modules/taxonomy/src/Tests/TaxonomyImageTest.php index 550c5f5..faceca2 100644 --- a/core/modules/taxonomy/src/Tests/TaxonomyImageTest.php +++ b/core/modules/taxonomy/src/Tests/TaxonomyImageTest.php @@ -7,8 +7,10 @@ namespace Drupal\taxonomy\Tests; +use Drupal\field\Entity\FieldConfig; use Drupal\user\RoleInterface; use Drupal\file\Entity\File; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests access checks of private image fields. @@ -41,7 +43,7 @@ protected function setUp() { // Add a field to the vocabulary. $entity_type = 'taxonomy_term'; $name = 'field_test'; - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'field_name' => $name, 'entity_type' => $entity_type, 'type' => 'image', @@ -49,12 +51,12 @@ protected function setUp() { 'uri_scheme' => 'private', ), ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_name' => $name, 'entity_type' => $entity_type, 'bundle' => $this->vocabulary->id(), 'settings' => array(), - ))->save(); + ])->save(); entity_get_display($entity_type, $this->vocabulary->id(), 'default') ->setComponent($name, array( 'type' => 'image', diff --git a/core/modules/taxonomy/src/Tests/TaxonomyTestTrait.php b/core/modules/taxonomy/src/Tests/TaxonomyTestTrait.php index 34971db..50f64d6 100644 --- a/core/modules/taxonomy/src/Tests/TaxonomyTestTrait.php +++ b/core/modules/taxonomy/src/Tests/TaxonomyTestTrait.php @@ -10,6 +10,7 @@ use Drupal\Component\Utility\Unicode; use Drupal\Core\Language\LanguageInterface; use Drupal\taxonomy\Entity\Vocabulary; +use Drupal\taxonomy\Entity\Term; /** * Provides common helper methods for Taxonomy module tests. @@ -21,13 +22,13 @@ */ function createVocabulary() { // Create a vocabulary. - $vocabulary = entity_create('taxonomy_vocabulary', array( + $vocabulary = Vocabulary::create([ 'name' => $this->randomMachineName(), 'description' => $this->randomMachineName(), 'vid' => Unicode::strtolower($this->randomMachineName()), 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, 'weight' => mt_rand(0, 10), - )); + ]); $vocabulary->save(); return $vocabulary; } @@ -47,16 +48,16 @@ function createVocabulary() { function createTerm(Vocabulary $vocabulary, $values = array()) { $filter_formats = filter_formats(); $format = array_pop($filter_formats); - $term = entity_create('taxonomy_term', $values + array( + $term = Term::create($values + [ 'name' => $this->randomMachineName(), - 'description' => array( + 'description' => [ 'value' => $this->randomMachineName(), // Use the first available text format. 'format' => $format->id(), - ), + ], 'vid' => $vocabulary->id(), 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - )); + ]); $term->save(); return $term; } diff --git a/core/modules/taxonomy/src/Tests/TermCacheTagsTest.php b/core/modules/taxonomy/src/Tests/TermCacheTagsTest.php index 23bcef1..723f442 100644 --- a/core/modules/taxonomy/src/Tests/TermCacheTagsTest.php +++ b/core/modules/taxonomy/src/Tests/TermCacheTagsTest.php @@ -8,6 +8,8 @@ namespace Drupal\taxonomy\Tests; use Drupal\system\Tests\Entity\EntityWithUriCacheTagsTestBase; +use Drupal\taxonomy\Entity\Vocabulary; +use Drupal\taxonomy\Entity\Term; /** * Tests the Taxonomy term entity's cache tags. @@ -26,17 +28,17 @@ class TermCacheTagsTest extends EntityWithUriCacheTagsTestBase { */ protected function createEntity() { // Create a "Camelids" vocabulary. - $vocabulary = entity_create('taxonomy_vocabulary', array( + $vocabulary = Vocabulary::create([ 'name' => 'Camelids', 'vid' => 'camelids', - )); + ]); $vocabulary->save(); // Create a "Llama" taxonomy term. - $term = entity_create('taxonomy_term', array( + $term = Term::create([ 'name' => 'Llama', 'vid' => $vocabulary->id(), - )); + ]); $term->save(); return $term; diff --git a/core/modules/taxonomy/src/Tests/TermTest.php b/core/modules/taxonomy/src/Tests/TermTest.php index 7165722..da78d41 100644 --- a/core/modules/taxonomy/src/Tests/TermTest.php +++ b/core/modules/taxonomy/src/Tests/TermTest.php @@ -495,10 +495,10 @@ function testTaxonomyGetTermByName() { // Create a new term in a different vocabulary with the same name. $new_vocabulary = $this->createVocabulary(); - $new_term = entity_create('taxonomy_term', array( + $new_term = Term::create([ 'name' => $term->getName(), 'vid' => $new_vocabulary->id(), - )); + ]); $new_term->save(); // Load multiple terms with the same name. diff --git a/core/modules/taxonomy/src/Tests/TermTranslationFieldViewTest.php b/core/modules/taxonomy/src/Tests/TermTranslationFieldViewTest.php index d807d5e..f9d6bce 100644 --- a/core/modules/taxonomy/src/Tests/TermTranslationFieldViewTest.php +++ b/core/modules/taxonomy/src/Tests/TermTranslationFieldViewTest.php @@ -7,6 +7,8 @@ namespace Drupal\taxonomy\Tests; +use Drupal\node\Entity\Node; + /** * Tests the translation of taxonomy terms field on nodes. * @@ -74,16 +76,16 @@ public function testTranslatedTaxonomyTermReferenceDisplay() { */ protected function setUpNode() { /** @var \Drupal\node\Entity\Node $node */ - $node = entity_create('node', array( + $node = Node::create([ 'title' => $this->randomMachineName(), 'type' => 'article', - 'description' => array( + 'description' => [[ 'value' => $this->randomMachineName(), - 'format' => 'basic_html', - ), + 'format' => 'basic_html' + ]], $this->termFieldName => array(array('target_id' => $this->term->id())), 'langcode' => $this->baseLangcode, - )); + ]); $node->save(); $node->addTranslation($this->translateToLangcode, $node->toArray()); $node->save(); diff --git a/core/modules/taxonomy/src/Tests/TermTranslationUITest.php b/core/modules/taxonomy/src/Tests/TermTranslationUITest.php index ed84144..88c59b6 100644 --- a/core/modules/taxonomy/src/Tests/TermTranslationUITest.php +++ b/core/modules/taxonomy/src/Tests/TermTranslationUITest.php @@ -9,6 +9,7 @@ use Drupal\content_translation\Tests\ContentTranslationUITestBase; use Drupal\Core\Language\LanguageInterface; +use Drupal\taxonomy\Entity\Vocabulary; /** * Tests the Term Translation UI. @@ -44,13 +45,13 @@ protected function setupBundle() { parent::setupBundle(); // Create a vocabulary. - $this->vocabulary = entity_create('taxonomy_vocabulary', array( + $this->vocabulary = Vocabulary::create([ 'name' => $this->bundle, 'description' => $this->randomMachineName(), 'vid' => $this->bundle, 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, 'weight' => mt_rand(0, 10), - )); + ]); $this->vocabulary->save(); } @@ -113,13 +114,13 @@ function testTranslateLinkVocabularyAdminPage() { $translatable_tid = $this->createEntity($values, $this->langcodes[0], $this->vocabulary->id()); // Create an untranslatable vocabulary. - $untranslatable_vocabulary = entity_create('taxonomy_vocabulary', array( + $untranslatable_vocabulary = Vocabulary::create([ 'name' => 'untranslatable_voc', 'description' => $this->randomMachineName(), 'vid' => 'untranslatable_voc', 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, 'weight' => mt_rand(0, 10), - )); + ]); $untranslatable_vocabulary->save(); $values = array( diff --git a/core/modules/taxonomy/src/Tests/Views/TaxonomyFieldFilterTest.php b/core/modules/taxonomy/src/Tests/Views/TaxonomyFieldFilterTest.php index 5dec796..b26664f 100644 --- a/core/modules/taxonomy/src/Tests/Views/TaxonomyFieldFilterTest.php +++ b/core/modules/taxonomy/src/Tests/Views/TaxonomyFieldFilterTest.php @@ -8,10 +8,14 @@ namespace Drupal\taxonomy\Tests\Views; use Drupal\Core\Language\LanguageInterface; +use Drupal\field\Entity\FieldConfig; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\views\Tests\ViewTestBase; use Drupal\views\Tests\ViewTestData; use Drupal\views\Views; +use Drupal\field\Entity\FieldStorageConfig; +use Drupal\taxonomy\Entity\Vocabulary; +use Drupal\taxonomy\Entity\Term; /** * Tests taxonomy field filters with translations. @@ -61,25 +65,25 @@ function setUp() { ); // Create a vocabulary. - $this->vocabulary = entity_create('taxonomy_vocabulary', array( + $this->vocabulary = Vocabulary::create([ 'name' => 'Views testing tags', 'vid' => 'views_testing_tags', - )); + ]); $this->vocabulary->save(); // Add a translatable field to the vocabulary. - $field = entity_create('field_storage_config', array( + $field = FieldStorageConfig::create(array( 'field_name' => 'field_foo', 'entity_type' => 'taxonomy_term', 'type' => 'text', )); $field->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_name' => 'field_foo', 'entity_type' => 'taxonomy_term', 'label' => 'Foo', 'bundle' => 'views_testing_tags', - ))->save(); + ])->save(); // Create term with translations. $taxonomy = $this->createTermWithProperties(array('name' => $this->termNames['en'], 'langcode' => 'en', 'description' => $this->termNames['en'], 'field_foo' => $this->termNames['en'])); @@ -171,13 +175,13 @@ protected function createTermWithProperties($properties) { 'field_foo' => $this->randomMachineName(), ); - $term = entity_create('taxonomy_term', array( + $term = Term::create([ 'name' => $properties['name'], 'description' => $properties['description'], 'format' => $format->id(), 'vid' => $this->vocabulary->id(), 'langcode' => $properties['langcode'], - )); + ]); $term->field_foo->value = $properties['field_foo']; $term->save(); return $term; diff --git a/core/modules/taxonomy/src/Tests/Views/TaxonomyTestBase.php b/core/modules/taxonomy/src/Tests/Views/TaxonomyTestBase.php index cc83adb..7e7bc14 100644 --- a/core/modules/taxonomy/src/Tests/Views/TaxonomyTestBase.php +++ b/core/modules/taxonomy/src/Tests/Views/TaxonomyTestBase.php @@ -12,6 +12,8 @@ use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait; use Drupal\views\Tests\ViewTestBase; use Drupal\views\Tests\ViewTestData; +use Drupal\taxonomy\Entity\Vocabulary; +use Drupal\taxonomy\Entity\Term; /** * Base class for all taxonomy tests. @@ -85,10 +87,10 @@ protected function mockStandardInstall() { 'type' => 'article', )); // Create the vocabulary for the tag field. - $this->vocabulary = entity_create('taxonomy_vocabulary', array( + $this->vocabulary = Vocabulary::create([ 'name' => 'Views testing tags', 'vid' => 'views_testing_tags', - )); + ]); $this->vocabulary->save(); $field_name = 'field_' . $this->vocabulary->id(); @@ -148,7 +150,7 @@ protected function createTerm(array $settings = []) { 'vid' => $this->vocabulary->id(), 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, ]; - $term = entity_create('taxonomy_term', $settings); + $term = Term::create($settings); $term->save(); return $term; } diff --git a/core/modules/taxonomy/src/Tests/Views/TaxonomyViewsFieldAccessTest.php b/core/modules/taxonomy/src/Tests/Views/TaxonomyViewsFieldAccessTest.php deleted file mode 100644 index 132e8ed..0000000 --- a/core/modules/taxonomy/src/Tests/Views/TaxonomyViewsFieldAccessTest.php +++ /dev/null @@ -1,73 +0,0 @@ -installEntitySchema('taxonomy_term'); - } - - /** - * Check access for taxonomy fields. - */ - public function testTermFields() { - $vocab = Vocabulary::create([ - 'vid' => 'random', - 'name' => 'Randomness', - ]); - $vocab->save(); - $term1 = Term::create([ - 'name' => 'Semi random', - 'vid' => $vocab->id(), - ]); - $term1->save(); - - $term2 = Term::create([ - 'name' => 'Majorly random', - 'vid' => $vocab->id(), - ]); - $term2->save(); - - $term3 = Term::create([ - 'name' => 'Not really random', - 'vid' => $vocab->id(), - ]); - $term3->save(); - - $this->assertFieldAccess('taxonomy_term', 'name', 'Majorly random'); - $this->assertFieldAccess('taxonomy_term', 'name', 'Semi random'); - $this->assertFieldAccess('taxonomy_term', 'name', 'Not really random'); - $this->assertFieldAccess('taxonomy_term', 'tid', $term1->id()); - $this->assertFieldAccess('taxonomy_term', 'tid', $term2->id()); - $this->assertFieldAccess('taxonomy_term', 'tid', $term3->id()); - $this->assertFieldAccess('taxonomy_term', 'uuid', $term1->uuid()); - $this->assertFieldAccess('taxonomy_term', 'uuid', $term2->uuid()); - $this->assertFieldAccess('taxonomy_term', 'uuid', $term3->uuid()); - } - -} diff --git a/core/modules/taxonomy/src/Tests/VocabularyCrudTest.php b/core/modules/taxonomy/src/Tests/VocabularyCrudTest.php index 6596b8e..d30bc3f 100644 --- a/core/modules/taxonomy/src/Tests/VocabularyCrudTest.php +++ b/core/modules/taxonomy/src/Tests/VocabularyCrudTest.php @@ -8,7 +8,9 @@ namespace Drupal\taxonomy\Tests; use Drupal\Component\Utility\Unicode; +use Drupal\field\Entity\FieldConfig; use Drupal\taxonomy\Entity\Vocabulary; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests loading, saving and deleting vocabularies. @@ -154,14 +156,14 @@ function testUninstallReinstall() { 'type' => 'text', 'cardinality' => 4 ); - entity_create('field_storage_config', $storage_definition)->save(); + FieldStorageConfig::create($storage_definition)->save(); $field_definition = array( 'field_name' => $field_name, 'entity_type' => 'taxonomy_term', 'bundle' => $this->vocabulary->id(), 'label' => $this->randomMachineName() . '_label', ); - entity_create('field_config', $field_definition)->save(); + FieldConfig::create($field_definition)->save(); // Remove the third party setting from the memory copy of the vocabulary. // We keep this invalid copy around while the taxonomy module is not even @@ -178,7 +180,7 @@ function testUninstallReinstall() { // an instance of this field on the same bundle name should be successful. $this->vocabulary->enforceIsNew(); $this->vocabulary->save(); - entity_create('field_storage_config', $storage_definition)->save(); - entity_create('field_config', $field_definition)->save(); + FieldStorageConfig::create($storage_definition)->save(); + FieldConfig::create($field_definition)->save(); } } diff --git a/core/modules/taxonomy/src/Tests/VocabularyTranslationTest.php b/core/modules/taxonomy/src/Tests/VocabularyTranslationTest.php index 4152135..b0e442e 100644 --- a/core/modules/taxonomy/src/Tests/VocabularyTranslationTest.php +++ b/core/modules/taxonomy/src/Tests/VocabularyTranslationTest.php @@ -4,6 +4,7 @@ * @file * Contains \Drupal\taxonomy\Tests\VocabularyTranslationTest. */ + namespace Drupal\taxonomy\Tests; use Drupal\Component\Utility\Unicode; diff --git a/core/modules/taxonomy/taxonomy.js b/core/modules/taxonomy/taxonomy.js index 09e5b46..99338d5 100644 --- a/core/modules/taxonomy/taxonomy.js +++ b/core/modules/taxonomy/taxonomy.js @@ -3,7 +3,7 @@ * Taxonomy behaviors. */ -(function ($) { +(function ($, Drupal) { 'use strict'; @@ -53,4 +53,4 @@ } }; -})(jQuery); +})(jQuery, Drupal); diff --git a/core/modules/taxonomy/templates/taxonomy-term.html.twig b/core/modules/taxonomy/templates/taxonomy-term.html.twig index d6fb9bd..6636b79 100644 --- a/core/modules/taxonomy/templates/taxonomy-term.html.twig +++ b/core/modules/taxonomy/templates/taxonomy-term.html.twig @@ -25,7 +25,7 @@ * @ingroup themeable */ #} - + {{ title_prefix }} {% if not page %}

                          {{ name }}

                          diff --git a/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/migrations/taxonomy_term_stub_test.yml b/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/migrations/taxonomy_term_stub_test.yml new file mode 100644 index 0000000..ddca901 --- /dev/null +++ b/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/migrations/taxonomy_term_stub_test.yml @@ -0,0 +1,29 @@ +id: taxonomy_term_stub_test +label: Taxonomy term stub +migration_tags: + - Import and rollback test +source: + plugin: embedded_data + data_rows: + - + id: 1 + vocab: 1 + name: music + parent: 2 + ids: + id: + type: integer +process: + tid: id + vid: vocab + name: name + weight: weight + parent: + plugin: migration + migration: taxonomy_term_stub_test + source: parent +destination: + plugin: entity:taxonomy_term +migration_dependencies: + required: + - vocabularies diff --git a/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/taxonomy_term_stub_test.info.yml b/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/taxonomy_term_stub_test.info.yml new file mode 100644 index 0000000..fac0381 --- /dev/null +++ b/core/modules/taxonomy/tests/modules/taxonomy_term_stub_test/taxonomy_term_stub_test.info.yml @@ -0,0 +1,9 @@ +name: 'Taxonomy Migrate stub test' +type: module +description: 'Provides a migration plugin for stub testing.' +package: Testing +version: VERSION +core: 8.x +dependencies: + - taxonomy + - migrate diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/MigrateTaxonomyConfigsTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/MigrateTaxonomyConfigsTest.php new file mode 100644 index 0000000..7f66c9c --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/MigrateTaxonomyConfigsTest.php @@ -0,0 +1,45 @@ +executeMigration('taxonomy_settings'); + } + + /** + * Tests migration of taxonomy variables to taxonomy.settings.yml. + */ + public function testTaxonomySettings() { + $config = $this->config('taxonomy.settings'); + $this->assertIdentical(100, $config->get('terms_per_page_admin')); + $this->assertIdentical(FALSE, $config->get('override_selector')); + $this->assertConfigSchema(\Drupal::service('config.typed'), 'taxonomy.settings', $config->get()); + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/MigrateTaxonomyTermStubTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/MigrateTaxonomyTermStubTest.php new file mode 100644 index 0000000..05277f9 --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/MigrateTaxonomyTermStubTest.php @@ -0,0 +1,90 @@ +installEntitySchema('taxonomy_term'); + } + + /** + * Tests creation of taxonomy term stubs. + */ + public function testStub() { + Vocabulary::create([ + 'vid' => 'test_vocabulary', + 'name' => 'Test vocabulary', + ])->save(); + $this->performStubTest('taxonomy_term'); + } + + /** + * Tests creation of stubs when weight is mapped. + */ + public function testStubWithWeightMapping() { + // Create a vocabulary via migration for the terms to reference. + $vocabulary_data_rows = [ + ['id' => '1', 'name' => 'tags'], + ]; + $ids = ['id' => ['type' => 'integer']]; + $definition = [ + 'migration_tags' => ['Stub test'], + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => $vocabulary_data_rows, + 'ids' => $ids, + ], + 'process' => [ + 'vid' => 'id', + 'name' => 'name', + ], + 'destination' => ['plugin' => 'entity:taxonomy_vocabulary'], + ]; + $vocabulary_migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); + $vocabulary_executable = new MigrateExecutable($vocabulary_migration, $this); + $vocabulary_executable->import(); + + // We have a term referencing an unmigrated parent, forcing a stub to be + // created. + $migration = $this->getMigration('taxonomy_term_stub_test'); + $term_executable = new MigrateExecutable($migration, $this); + $term_executable->import(); + $this->assertTrue($migration->getIdMap()->getRowBySource(['2']), 'Stub row exists in the ID map table'); + + // Load the referenced term, which should exist as a stub. + /** @var \Drupal\Core\Entity\ContentEntityBase $stub_entity */ + $stub_entity = Term::load(2); + $this->assertTrue($stub_entity, 'Stub successfully created'); + if ($stub_entity) { + $this->assertIdentical(count($stub_entity->validate()), 0, 'Stub is a valid entity'); + } + } +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php new file mode 100644 index 0000000..0d2a226 --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyTermTest.php @@ -0,0 +1,97 @@ +installEntitySchema('taxonomy_term'); + $this->executeMigrations(['d6_taxonomy_vocabulary', 'd6_taxonomy_term']); + } + + /** + * Tests the Drupal 6 taxonomy term to Drupal 8 migration. + */ + public function testTaxonomyTerms() { + $expected_results = array( + '1' => array( + 'source_vid' => 1, + 'vid' => 'vocabulary_1_i_0_', + 'weight' => 0, + 'parent' => array(0), + ), + '2' => array( + 'source_vid' => 2, + 'vid' => 'vocabulary_2_i_1_', + 'weight' => 3, + 'parent' => array(0), + ), + '3' => array( + 'source_vid' => 2, + 'vid' => 'vocabulary_2_i_1_', + 'weight' => 4, + 'parent' => array(2), + ), + '4' => array( + 'source_vid' => 3, + 'vid' => 'vocabulary_3_i_2_', + 'weight' => 6, + 'parent' => array(0), + ), + '5' => array( + 'source_vid' => 3, + 'vid' => 'vocabulary_3_i_2_', + 'weight' => 7, + 'parent' => array(4), + ), + '6' => array( + 'source_vid' => 3, + 'vid' => 'vocabulary_3_i_2_', + 'weight' => 8, + 'parent' => array(4, 5), + ), + ); + $terms = Term::loadMultiple(array_keys($expected_results)); + foreach ($expected_results as $tid => $values) { + /** @var Term $term */ + $term = $terms[$tid]; + $this->assertIdentical("term {$tid} of vocabulary {$values['source_vid']}", $term->name->value); + $this->assertIdentical("description of term {$tid} of vocabulary {$values['source_vid']}", $term->description->value); + $this->assertIdentical($values['vid'], $term->vid->target_id); + $this->assertIdentical((string) $values['weight'], $term->weight->value); + if ($values['parent'] === array(0)) { + $this->assertNull($term->parent->target_id); + } + else { + $parents = array(); + foreach (\Drupal::entityManager()->getStorage('taxonomy_term')->loadParents($tid) as $parent) { + $parents[] = (int) $parent->id(); + } + $this->assertIdentical($parents, $values['parent']); + } + } + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyVocabularyTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyVocabularyTest.php new file mode 100644 index 0000000..9411f70 --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTaxonomyVocabularyTest.php @@ -0,0 +1,53 @@ +executeMigration('d6_taxonomy_vocabulary'); + } + + /** + * Tests the Drupal 6 taxonomy vocabularies to Drupal 8 migration. + */ + public function testTaxonomyVocabulary() { + for ($i = 0; $i < 3; $i++) { + $j = $i + 1; + $vocabulary = Vocabulary::load("vocabulary_{$j}_i_{$i}_"); + $this->assertIdentical($this->getMigration('d6_taxonomy_vocabulary')->getIdMap()->lookupDestinationID(array($j)), array($vocabulary->id())); + $this->assertIdentical("vocabulary $j (i=$i)", $vocabulary->label()); + $this->assertIdentical("description of vocabulary $j (i=$i)", $vocabulary->getDescription()); + $this->assertIdentical($i, $vocabulary->getHierarchy()); + $this->assertIdentical(4 + $i, $vocabulary->get('weight')); + } + $vocabulary = Vocabulary::load('vocabulary_name_much_longer_than'); + $this->assertIdentical('vocabulary name much longer than thirty two characters', $vocabulary->label()); + $this->assertIdentical('description of vocabulary name much longer than thirty two characters', $vocabulary->getDescription()); + $this->assertIdentical(3, $vocabulary->getHierarchy()); + $this->assertIdentical(7, $vocabulary->get('weight')); + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeRevisionTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeRevisionTest.php new file mode 100644 index 0000000..30495ae --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeRevisionTest.php @@ -0,0 +1,45 @@ +installSchema('node', ['node_access']); + $this->migrateContent(TRUE); + $this->migrateTaxonomy(); + $this->executeMigrations(['d6_term_node', 'd6_term_node_revision']); + } + + /** + * Tests the Drupal 6 term-node revision association to Drupal 8 migration. + */ + public function testTermRevisionNode() { + $node = \Drupal::entityManager()->getStorage('node')->loadRevision(2); + $this->assertIdentical(2, count($node->vocabulary_3_i_2_)); + $this->assertIdentical('4', $node->vocabulary_3_i_2_[0]->target_id); + $this->assertIdentical('5', $node->vocabulary_3_i_2_[1]->target_id); + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeTest.php new file mode 100644 index 0000000..82ca56b --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateTermNodeTest.php @@ -0,0 +1,72 @@ +installSchema('node', ['node_access']); + $this->migrateContent(); + $this->migrateTaxonomy(); + } + + /** + * Tests the Drupal 6 term-node association to Drupal 8 migration. + */ + public function testTermNode() { + // This is a base plugin id and we want to run all derivatives. + $this->executeMigrations(['d6_term_node']); + + $this->container->get('entity.manager') + ->getStorage('node') + ->resetCache([1, 2]); + + $nodes = Node::loadMultiple([1, 2]); + $node = $nodes[1]; + $this->assertIdentical(1, count($node->vocabulary_1_i_0_)); + $this->assertIdentical('1', $node->vocabulary_1_i_0_[0]->target_id); + $node = $nodes[2]; + $this->assertIdentical(2, count($node->vocabulary_2_i_1_)); + $this->assertIdentical('2', $node->vocabulary_2_i_1_[0]->target_id); + $this->assertIdentical('3', $node->vocabulary_2_i_1_[1]->target_id); + } + + /** + * Tests that term associations are ignored when they belong to nodes which + * were not migrated. + */ + public function testSkipNonExistentNode() { + // Node 2 is migrated by d6_node__story, but we need to pretend that it + // failed, so record that in the map table. + $this->mockFailure('d6_node:story', ['nid' => 2]); + + // d6_term_node__2 should skip over node 2 (a.k.a. revision 3) because, + // according to the map table, it failed. + $migration = $this->getMigration('d6_term_node:2'); + $this->executeMigration($migration); + $this->assertNull($migration->getIdMap()->lookupDestinationId(['vid' => 3])[0]); + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityDisplayTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityDisplayTest.php new file mode 100644 index 0000000..c11a33b --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityDisplayTest.php @@ -0,0 +1,45 @@ +migrateTaxonomy(); + } + + /** + * Tests the Drupal 6 vocabulary-node type association to Drupal 8 migration. + */ + public function testVocabularyEntityDisplay() { + // Test that the field exists. + $component = EntityViewDisplay::load('node.page.default')->getComponent('tags'); + $this->assertIdentical('entity_reference_label', $component['type']); + $this->assertIdentical(20, $component['weight']); + // Test the Id map. + $this->assertIdentical(array('node', 'article', 'default', 'tags'), $this->getMigration('d6_vocabulary_entity_display')->getIdMap()->lookupDestinationID(array(4, 'article'))); + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityFormDisplayTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityFormDisplayTest.php new file mode 100644 index 0000000..9bfc9c2 --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyEntityFormDisplayTest.php @@ -0,0 +1,50 @@ +migrateTaxonomy(); + } + + /** + * Tests the Drupal 6 vocabulary-node type association to Drupal 8 migration. + */ + public function testVocabularyEntityFormDisplay() { + // Test that the field exists. + $component = EntityFormDisplay::load('node.page.default')->getComponent('tags'); + $this->assertIdentical('options_select', $component['type']); + $this->assertIdentical(20, $component['weight']); + // Test the Id map. + $this->assertIdentical(array('node', 'article', 'default', 'tags'), $this->getMigration('d6_vocabulary_entity_form_display')->getIdMap()->lookupDestinationID(array(4, 'article'))); + + // Test the term widget tags setting. + $entity_form_display = EntityFormDisplay::load('node.story.default'); + $this->assertIdentical($entity_form_display->getComponent('vocabulary_1_i_0_')['type'], 'options_select'); + $this->assertIdentical($entity_form_display->getComponent('vocabulary_2_i_1_')['type'], 'entity_reference_autocomplete_tags'); + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldInstanceTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldInstanceTest.php new file mode 100644 index 0000000..ba0321f --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldInstanceTest.php @@ -0,0 +1,64 @@ +migrateTaxonomy(); + } + + /** + * Tests the Drupal 6 vocabulary-node type association to Drupal 8 migration. + */ + public function testVocabularyFieldInstance() { + // Test that the field exists. + $field_id = 'node.article.tags'; + $field = FieldConfig::load($field_id); + $this->assertIdentical($field_id, $field->id(), 'Field instance exists on article bundle.'); + $this->assertIdentical('Tags', $field->label()); + $this->assertTrue($field->isRequired(), 'Field is required'); + + // Test the page bundle as well. + $field_id = 'node.page.tags'; + $field = FieldConfig::load($field_id); + $this->assertIdentical($field_id, $field->id(), 'Field instance exists on page bundle.'); + $this->assertIdentical('Tags', $field->label()); + $this->assertTrue($field->isRequired(), 'Field is required'); + + $settings = $field->getSettings(); + $this->assertIdentical('default:taxonomy_term', $settings['handler'], 'The handler plugin ID is correct.'); + $this->assertIdentical(['tags'], $settings['handler_settings']['target_bundles'], 'The target_bundles handler setting is correct.'); + $this->assertIdentical(TRUE, $settings['handler_settings']['auto_create'], 'The "auto_create" setting is correct.'); + + $this->assertIdentical(array('node', 'article', 'tags'), $this->getMigration('d6_vocabulary_field_instance')->getIdMap()->lookupDestinationID(array(4, 'article'))); + + // Test the the field vocabulary_1_i_0_ + $field_id = 'node.story.vocabulary_1_i_0_'; + $field = FieldConfig::load($field_id); + $this->assertFalse($field->isRequired(), 'Field is not required'); + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldTest.php new file mode 100644 index 0000000..4646544 --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d6/MigrateVocabularyFieldTest.php @@ -0,0 +1,50 @@ +migrateTaxonomy(); + } + + /** + * Tests the Drupal 6 vocabulary-node type association to Drupal 8 migration. + */ + public function testVocabularyField() { + // Test that the field exists. + $field_storage_id = 'node.tags'; + /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */ + $field_storage = FieldStorageConfig::load($field_storage_id); + $this->assertIdentical($field_storage_id, $field_storage->id()); + + $settings = $field_storage->getSettings(); + $this->assertIdentical('taxonomy_term', $settings['target_type'], "Target type is correct."); + $this->assertIdentical(1, $field_storage->getCardinality(), "Field cardinality in 1."); + + $this->assertIdentical(array('node', 'tags'), $this->getMigration('d6_vocabulary_field')->getIdMap()->lookupDestinationID(array(4)), "Test IdMap"); + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateNodeTaxonomyTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateNodeTaxonomyTest.php new file mode 100644 index 0000000..76d301e --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateNodeTaxonomyTest.php @@ -0,0 +1,84 @@ +installEntitySchema('node'); + $this->installEntitySchema('taxonomy_term'); + $this->installConfig(static::$modules); + $this->installSchema('node', ['node_access']); + $this->installSchema('system', ['sequences']); + + $this->executeMigration('d7_node_type'); + + FieldStorageConfig::create(array( + 'type' => 'entity_reference', + 'field_name' => 'field_tags', + 'entity_type' => 'node', + 'settings' => array( + 'target_type' => 'taxonomy_term', + ), + 'cardinality' => FieldStorageConfigInterface::CARDINALITY_UNLIMITED, + ))->save(); + + FieldConfig::create(array( + 'entity_type' => 'node', + 'field_name' => 'field_tags', + 'bundle' => 'article', + ))->save(); + + $this->executeMigrations([ + 'd7_taxonomy_vocabulary', + 'd7_taxonomy_term', + 'd7_user_role', + 'd7_user', + 'd7_node:article', + ]); + } + + /** + * Test node migration from Drupal 7 to 8. + */ + public function testMigration() { + $node = Node::load(2); + $this->assertTrue($node instanceof NodeInterface); + $this->assertEqual(9, $node->field_tags[0]->target_id); + $this->assertEqual(14, $node->field_tags[1]->target_id); + $this->assertEqual(17, $node->field_tags[2]->target_id); + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyTermTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyTermTest.php new file mode 100644 index 0000000..0161a05 --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyTermTest.php @@ -0,0 +1,86 @@ +installEntitySchema('taxonomy_term'); + $this->executeMigrations(['d7_taxonomy_vocabulary', 'd7_taxonomy_term']); + } + + /** + * Validate a migrated term contains the expected values. + * + * @param $id + * Entity ID to load and check. + * @param $expected_label + * The label the migrated entity should have. + * @param $expected_vid + * The parent vocabulary the migrated entity should have. + * @param string $expected_description + * The description the migrated entity should have. + * @param int $expected_weight + * The weight the migrated entity should have. + * @param array $expected_parents + * The parent terms the migrated entity should have. + */ + protected function assertEntity($id, $expected_label, $expected_vid, $expected_description = '', $expected_weight = 0, $expected_parents = []) { + /** @var \Drupal\taxonomy\TermInterface $entity */ + $entity = Term::load($id); + $this->assertTrue($entity instanceof TermInterface); + $this->assertIdentical($expected_label, $entity->label()); + $this->assertIdentical($expected_vid, $entity->getVocabularyId()); + $this->assertEqual($expected_description, $entity->getDescription()); + $this->assertEqual($expected_weight, $entity->getWeight()); + $this->assertIdentical($expected_parents, $this->getParentIDs($id)); + } + + /** + * Tests the Drupal 7 taxonomy term to Drupal 8 migration. + */ + public function testTaxonomyTerms() { + $this->assertEntity(1, 'General discussion', 'forums', '', 2); + $this->assertEntity(2, 'Term1', 'test_vocabulary', 'The first term.'); + $this->assertEntity(3, 'Term2', 'test_vocabulary', 'The second term.'); + $this->assertEntity(4, 'Term3', 'test_vocabulary', 'The third term.', 0, [3]); + $this->assertEntity(5, 'Custom Forum', 'forums', 'Where the cool kids are.', 3); + $this->assertEntity(6, 'Games', 'forums', '', 4); + $this->assertEntity(7, 'Minecraft', 'forums', '', 1, [6]); + $this->assertEntity(8, 'Half Life 3', 'forums', '', 0, [6]); + } + + /** + * Retrieves the parent term IDs for a given term. + * + * @param $tid + * ID of the term to check. + * + * @return array + * List of parent term IDs. + */ + protected function getParentIDs($tid) { + return array_keys(\Drupal::entityManager()->getStorage('taxonomy_term')->loadParents($tid)); + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyVocabularyTest.php b/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyVocabularyTest.php new file mode 100644 index 0000000..7144c2a --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Migrate/d7/MigrateTaxonomyVocabularyTest.php @@ -0,0 +1,67 @@ +executeMigration('d7_taxonomy_vocabulary'); + } + + /** + * Validate a migrated vocabulary contains the expected values. + * + * @param $id + * Entity ID to load and check. + * @param $expected_label + * The label the migrated entity should have. + * @param $expected_description + * The description the migrated entity should have. + * @param $expected_hierarchy + * The hierarchy setting the migrated entity should have. + * @param $expected_weight + * The weight the migrated entity should have. + */ + protected function assertEntity($id, $expected_label, $expected_description, $expected_hierarchy, $expected_weight) { + /** @var \Drupal\taxonomy\VocabularyInterface $entity */ + $entity = Vocabulary::load($id); + $this->assertTrue($entity instanceof VocabularyInterface); + $this->assertIdentical($expected_label, $entity->label()); + $this->assertIdentical($expected_description, $entity->getDescription()); + $this->assertIdentical($expected_hierarchy, $entity->getHierarchy()); + $this->assertIdentical($expected_weight, $entity->get('weight')); + } + + /** + * Tests the Drupal 7 taxonomy vocabularies to Drupal 8 migration. + */ + public function testTaxonomyVocabulary() { + $this->assertEntity('tags', 'Tags', 'Use tags to group articles on similar topics into categories.', TAXONOMY_HIERARCHY_DISABLED, 0); + $this->assertEntity('forums', 'Forums', 'Forum navigation vocabulary', TAXONOMY_HIERARCHY_SINGLE, -10); + $this->assertEntity('test_vocabulary', 'Test Vocabulary', 'This is the vocabulary description', TAXONOMY_HIERARCHY_SINGLE, 0); + } + +} diff --git a/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyViewsFieldAccessTest.php b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyViewsFieldAccessTest.php new file mode 100644 index 0000000..a8aec54 --- /dev/null +++ b/core/modules/taxonomy/tests/src/Kernel/Views/TaxonomyViewsFieldAccessTest.php @@ -0,0 +1,73 @@ +installEntitySchema('taxonomy_term'); + } + + /** + * Check access for taxonomy fields. + */ + public function testTermFields() { + $vocab = Vocabulary::create([ + 'vid' => 'random', + 'name' => 'Randomness', + ]); + $vocab->save(); + $term1 = Term::create([ + 'name' => 'Semi random', + 'vid' => $vocab->id(), + ]); + $term1->save(); + + $term2 = Term::create([ + 'name' => 'Majorly random', + 'vid' => $vocab->id(), + ]); + $term2->save(); + + $term3 = Term::create([ + 'name' => 'Not really random', + 'vid' => $vocab->id(), + ]); + $term3->save(); + + $this->assertFieldAccess('taxonomy_term', 'name', 'Majorly random'); + $this->assertFieldAccess('taxonomy_term', 'name', 'Semi random'); + $this->assertFieldAccess('taxonomy_term', 'name', 'Not really random'); + $this->assertFieldAccess('taxonomy_term', 'tid', $term1->id()); + $this->assertFieldAccess('taxonomy_term', 'tid', $term2->id()); + $this->assertFieldAccess('taxonomy_term', 'tid', $term3->id()); + $this->assertFieldAccess('taxonomy_term', 'uuid', $term1->uuid()); + $this->assertFieldAccess('taxonomy_term', 'uuid', $term2->uuid()); + $this->assertFieldAccess('taxonomy_term', 'uuid', $term3->uuid()); + } + +} diff --git a/core/modules/telephone/src/Tests/TelephoneFieldTest.php b/core/modules/telephone/src/Tests/TelephoneFieldTest.php index 515d7e0..8928e8d 100644 --- a/core/modules/telephone/src/Tests/TelephoneFieldTest.php +++ b/core/modules/telephone/src/Tests/TelephoneFieldTest.php @@ -7,7 +7,9 @@ namespace Drupal\telephone\Tests; +use Drupal\field\Entity\FieldConfig; use Drupal\simpletest\WebTestBase; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests the creation of telephone fields. @@ -50,17 +52,17 @@ protected function setUp() { function testTelephoneField() { // Add the telephone field to the article content type. - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'field_name' => 'field_telephone', 'entity_type' => 'node', 'type' => 'telephone', ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_name' => 'field_telephone', 'label' => 'Telephone Number', 'entity_type' => 'node', 'bundle' => 'article', - ))->save(); + ])->save(); entity_get_form_display('node', 'article', 'default') ->setComponent('field_telephone', array( diff --git a/core/modules/telephone/src/Tests/TelephoneItemTest.php b/core/modules/telephone/src/Tests/TelephoneItemTest.php deleted file mode 100644 index 42b7973..0000000 --- a/core/modules/telephone/src/Tests/TelephoneItemTest.php +++ /dev/null @@ -1,79 +0,0 @@ - 'field_test', - 'entity_type' => 'entity_test', - 'type' => 'telephone', - ))->save(); - entity_create('field_config', array( - 'entity_type' => 'entity_test', - 'field_name' => 'field_test', - 'bundle' => 'entity_test', - ))->save(); - } - - /** - * Tests using entity fields of the telephone field type. - */ - public function testTestItem() { - // Verify entity creation. - $entity = entity_create('entity_test'); - $value = '+0123456789'; - $entity->field_test = $value; - $entity->name->value = $this->randomMachineName(); - $entity->save(); - - // Verify entity has been created properly. - $id = $entity->id(); - $entity = entity_load('entity_test', $id); - $this->assertTrue($entity->field_test instanceof FieldItemListInterface, 'Field implements interface.'); - $this->assertTrue($entity->field_test[0] instanceof FieldItemInterface, 'Field item implements interface.'); - $this->assertEqual($entity->field_test->value, $value); - $this->assertEqual($entity->field_test[0]->value, $value); - - // Verify changing the field value. - $new_value = '+41' . rand(1000000, 9999999); - $entity->field_test->value = $new_value; - $this->assertEqual($entity->field_test->value, $new_value); - - // Read changed entity and assert changed values. - $entity->save(); - $entity = entity_load('entity_test', $id); - $this->assertEqual($entity->field_test->value, $new_value); - - // Test sample item generation. - $entity = entity_create('entity_test'); - $entity->field_test->generateSampleItems(); - $this->entityValidateAndSave($entity); - } - -} diff --git a/core/modules/telephone/tests/src/Kernel/TelephoneItemTest.php b/core/modules/telephone/tests/src/Kernel/TelephoneItemTest.php new file mode 100644 index 0000000..2935607 --- /dev/null +++ b/core/modules/telephone/tests/src/Kernel/TelephoneItemTest.php @@ -0,0 +1,82 @@ + 'field_test', + 'entity_type' => 'entity_test', + 'type' => 'telephone', + ))->save(); + FieldConfig::create([ + 'entity_type' => 'entity_test', + 'field_name' => 'field_test', + 'bundle' => 'entity_test', + ])->save(); + } + + /** + * Tests using entity fields of the telephone field type. + */ + public function testTestItem() { + // Verify entity creation. + $entity = EntityTest::create(); + $value = '+0123456789'; + $entity->field_test = $value; + $entity->name->value = $this->randomMachineName(); + $entity->save(); + + // Verify entity has been created properly. + $id = $entity->id(); + $entity = entity_load('entity_test', $id); + $this->assertTrue($entity->field_test instanceof FieldItemListInterface, 'Field implements interface.'); + $this->assertTrue($entity->field_test[0] instanceof FieldItemInterface, 'Field item implements interface.'); + $this->assertEqual($entity->field_test->value, $value); + $this->assertEqual($entity->field_test[0]->value, $value); + + // Verify changing the field value. + $new_value = '+41' . rand(1000000, 9999999); + $entity->field_test->value = $new_value; + $this->assertEqual($entity->field_test->value, $new_value); + + // Read changed entity and assert changed values. + $entity->save(); + $entity = entity_load('entity_test', $id); + $this->assertEqual($entity->field_test->value, $new_value); + + // Test sample item generation. + $entity = EntityTest::create(); + $entity->field_test->generateSampleItems(); + $this->entityValidateAndSave($entity); + } + +} diff --git a/core/modules/text/src/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php b/core/modules/text/src/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php index 9cb3038..85abaf4 100644 --- a/core/modules/text/src/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php +++ b/core/modules/text/src/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php @@ -4,6 +4,7 @@ * @file * Contains \Drupal\text\Plugin\Field\FieldFormatter\TextTrimmedFormatter. */ + namespace Drupal\text\Plugin\Field\FieldFormatter; use Drupal\Core\Field\FormatterBase; diff --git a/core/modules/text/src/Plugin/migrate/cckfield/TextField.php b/core/modules/text/src/Plugin/migrate/cckfield/TextField.php index a386b5f..080757e 100644 --- a/core/modules/text/src/Plugin/migrate/cckfield/TextField.php +++ b/core/modules/text/src/Plugin/migrate/cckfield/TextField.php @@ -7,7 +7,7 @@ namespace Drupal\text\Plugin\migrate\cckfield; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; use Drupal\migrate_drupal\Plugin\migrate\cckfield\CckFieldPluginBase; diff --git a/core/modules/text/src/Tests/Formatter/TextFormatterTest.php b/core/modules/text/src/Tests/Formatter/TextFormatterTest.php index 324f2cf..437b8b6 100644 --- a/core/modules/text/src/Tests/Formatter/TextFormatterTest.php +++ b/core/modules/text/src/Tests/Formatter/TextFormatterTest.php @@ -7,8 +7,10 @@ namespace Drupal\text\Tests\Formatter; +use Drupal\field\Entity\FieldConfig; use Drupal\filter\Entity\FilterFormat; use Drupal\system\Tests\Entity\EntityUnitTestBase; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests the text formatters functionality. @@ -44,7 +46,7 @@ class TextFormatterTest extends EntityUnitTestBase { protected function setUp() { parent::setUp(); - entity_create('filter_format', array( + FilterFormat::create(array( 'format' => 'my_text_format', 'name' => 'My text format', 'filters' => array( @@ -55,18 +57,18 @@ protected function setUp() { ), ))->save(); - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'field_name' => 'formatted_text', 'entity_type' => $this->entityType, 'type' => 'text', 'settings' => array(), ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'entity_type' => $this->entityType, 'bundle' => $this->bundle, 'field_name' => 'formatted_text', 'label' => 'Filtered text', - ))->save(); + ])->save(); } /** @@ -80,7 +82,9 @@ public function testFormatters() { ); // Create the entity to be referenced. - $entity = entity_create($this->entityType, array('name' => $this->randomMachineName())); + $entity = $this->container->get('entity_type.manager') + ->getStorage($this->entityType) + ->create(array('name' => $this->randomMachineName())); $entity->formatted_text = array( 'value' => 'Hello, world!', 'format' => 'my_text_format', diff --git a/core/modules/text/src/Tests/Migrate/MigrateTextConfigsTest.php b/core/modules/text/src/Tests/Migrate/MigrateTextConfigsTest.php deleted file mode 100644 index 02eefb9..0000000 --- a/core/modules/text/src/Tests/Migrate/MigrateTextConfigsTest.php +++ /dev/null @@ -1,39 +0,0 @@ -executeMigration('text_settings'); - } - - /** - * Tests migration of text variables to text.settings.yml. - */ - public function testTextSettings() { - $config = $this->config('text.settings'); - $this->assertIdentical(456, $config->get('default_summary_length')); - $this->assertConfigSchema(\Drupal::service('config.typed'), 'text.settings', $config->get()); - } - -} diff --git a/core/modules/text/src/Tests/TextFieldTest.php b/core/modules/text/src/Tests/TextFieldTest.php index 8279a6d..9d44003 100644 --- a/core/modules/text/src/Tests/TextFieldTest.php +++ b/core/modules/text/src/Tests/TextFieldTest.php @@ -8,7 +8,10 @@ namespace Drupal\text\Tests; use Drupal\Component\Utility\Unicode; +use Drupal\entity_test\Entity\EntityTest; +use Drupal\field\Entity\FieldConfig; use Drupal\field\Tests\String\StringFieldTest; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests the creation of text fields. @@ -39,7 +42,7 @@ function testTextFieldValidation() { // Create a field with settings to validate. $max_length = 3; $field_name = Unicode::strtolower($this->randomMachineName()); - $field_storage = entity_create('field_storage_config', array( + $field_storage = FieldStorageConfig::create(array( 'field_name' => $field_name, 'entity_type' => 'entity_test', 'type' => 'text', @@ -48,13 +51,13 @@ function testTextFieldValidation() { ) )); $field_storage->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_storage' => $field_storage, 'bundle' => 'entity_test', - ))->save(); + ])->save(); // Test validation with valid and invalid values. - $entity = entity_create('entity_test'); + $entity = EntityTest::create(); for ($i = 0; $i <= $max_length + 2; $i++) { $entity->{$field_name}->value = str_repeat('x', $i); $violations = $entity->{$field_name}->validate(); @@ -73,32 +76,32 @@ function testTextFieldValidation() { function testRequiredLongTextWithFileUpload() { // Create a text field. $text_field_name = 'text_long'; - $field_storage = entity_create('field_storage_config', array( + $field_storage = FieldStorageConfig::create(array( 'field_name' => $text_field_name, 'entity_type' => 'entity_test', 'type' => 'text_with_summary', )); $field_storage->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_storage' => $field_storage, 'bundle' => 'entity_test', 'label' => $this->randomMachineName() . '_label', 'required' => TRUE, - ))->save(); + ])->save(); // Create a file field. $file_field_name = 'file_field'; - $field_storage = entity_create('field_storage_config', array( + $field_storage = FieldStorageConfig::create(array( 'field_name' => $file_field_name, 'entity_type' => 'entity_test', 'type' => 'file' )); $field_storage->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_storage' => $field_storage, 'bundle' => 'entity_test', 'label' => $this->randomMachineName() . '_label', - ))->save(); + ])->save(); entity_get_form_display('entity_test', 'entity_test', 'default') ->setComponent($text_field_name, array( @@ -151,17 +154,17 @@ function _testTextfieldWidgetsFormatted($field_type, $widget_type) { // Create a field. $field_name = Unicode::strtolower($this->randomMachineName()); - $field_storage = entity_create('field_storage_config', array( + $field_storage = FieldStorageConfig::create(array( 'field_name' => $field_name, 'entity_type' => 'entity_test', 'type' => $field_type )); $field_storage->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_storage' => $field_storage, 'bundle' => 'entity_test', 'label' => $this->randomMachineName() . '_label', - ))->save(); + ])->save(); entity_get_form_display('entity_test', 'entity_test', 'default') ->setComponent($field_name, array( 'type' => $widget_type, diff --git a/core/modules/text/src/Tests/TextSummaryTest.php b/core/modules/text/src/Tests/TextSummaryTest.php index 7b1dfab..c5431a8 100644 --- a/core/modules/text/src/Tests/TextSummaryTest.php +++ b/core/modules/text/src/Tests/TextSummaryTest.php @@ -8,6 +8,7 @@ namespace Drupal\text\Tests; use Drupal\simpletest\KernelTestBase; +use Drupal\filter\Entity\FilterFormat; /** * Tests text_summary() with different strings and lengths. @@ -21,7 +22,6 @@ class TextSummaryTest extends KernelTestBase { protected function setUp() { parent::setUp(); - $this->installSchema('system', 'url_alias'); $this->installConfig(array('text')); } @@ -55,7 +55,7 @@ function testLongSentence() { * Test various summary length edge cases. */ function testLength() { - entity_create('filter_format', array( + FilterFormat::create(array( 'format' => 'autop', 'filters' => array( 'filter_autop' => array( @@ -63,7 +63,7 @@ function testLength() { ), ), ))->save(); - entity_create('filter_format', array( + FilterFormat::create(array( 'format' => 'autop_correct', 'filters' => array( 'filter_autop' => array( diff --git a/core/modules/text/src/Tests/TextWithSummaryItemTest.php b/core/modules/text/src/Tests/TextWithSummaryItemTest.php deleted file mode 100644 index e292ffd..0000000 --- a/core/modules/text/src/Tests/TextWithSummaryItemTest.php +++ /dev/null @@ -1,117 +0,0 @@ -installEntitySchema('entity_test_rev'); - - // Create the necessary formats. - $this->installConfig(array('filter')); - entity_create('filter_format', array( - 'format' => 'no_filters', - 'filters' => array(), - ))->save(); - } - - /** - * Tests processed properties. - */ - public function testCrudAndUpdate() { - $entity_type = 'entity_test'; - $this->createField($entity_type); - - // Create an entity with a summary and no text format. - $entity = entity_create($entity_type); - $entity->summary_field->value = $value = $this->randomMachineName(); - $entity->summary_field->summary = $summary = $this->randomMachineName(); - $entity->summary_field->format = NULL; - $entity->name->value = $this->randomMachineName(); - $entity->save(); - - $entity = entity_load($entity_type, $entity->id()); - $this->assertTrue($entity->summary_field instanceof FieldItemListInterface, 'Field implements interface.'); - $this->assertTrue($entity->summary_field[0] instanceof FieldItemInterface, 'Field item implements interface.'); - $this->assertEqual($entity->summary_field->value, $value); - $this->assertEqual($entity->summary_field->summary, $summary); - $this->assertNull($entity->summary_field->format); - // Even if no format is given, if text processing is enabled, the default - // format is used. - $this->assertEqual($entity->summary_field->processed, "

                          $value

                          \n"); - $this->assertEqual($entity->summary_field->summary_processed, "

                          $summary

                          \n"); - - // Change the format, this should update the processed properties. - $entity->summary_field->format = 'no_filters'; - $this->assertEqual($entity->summary_field->processed, $value); - $this->assertEqual($entity->summary_field->summary_processed, $summary); - - // Test the generateSampleValue() method. - $entity = entity_create($entity_type); - $entity->summary_field->generateSampleItems(); - $this->entityValidateAndSave($entity); - } - - /** - * Creates a text_with_summary field storage and field. - * - * @param string $entity_type - * Entity type for which the field should be created. - */ - protected function createField($entity_type) { - // Create a field . - $this->fieldStorage = entity_create('field_storage_config', array( - 'field_name' => 'summary_field', - 'entity_type' => $entity_type, - 'type' => 'text_with_summary', - 'settings' => array( - 'max_length' => 10, - ) - )); - $this->fieldStorage->save(); - $this->field = entity_create('field_config', array( - 'field_storage' => $this->fieldStorage, - 'bundle' => $entity_type, - )); - $this->field->save(); - } - -} diff --git a/core/modules/text/tests/src/Kernel/Migrate/MigrateTextConfigsTest.php b/core/modules/text/tests/src/Kernel/Migrate/MigrateTextConfigsTest.php new file mode 100644 index 0000000..fd2c15d --- /dev/null +++ b/core/modules/text/tests/src/Kernel/Migrate/MigrateTextConfigsTest.php @@ -0,0 +1,39 @@ +executeMigration('text_settings'); + } + + /** + * Tests migration of text variables to text.settings.yml. + */ + public function testTextSettings() { + $config = $this->config('text.settings'); + $this->assertIdentical(456, $config->get('default_summary_length')); + $this->assertConfigSchema(\Drupal::service('config.typed'), 'text.settings', $config->get()); + } + +} diff --git a/core/modules/text/tests/src/Kernel/TextWithSummaryItemTest.php b/core/modules/text/tests/src/Kernel/TextWithSummaryItemTest.php new file mode 100644 index 0000000..cb3519a --- /dev/null +++ b/core/modules/text/tests/src/Kernel/TextWithSummaryItemTest.php @@ -0,0 +1,124 @@ +installEntitySchema('entity_test_rev'); + + // Create the necessary formats. + $this->installConfig(array('filter')); + FilterFormat::create(array( + 'format' => 'no_filters', + 'filters' => array(), + ))->save(); + } + + /** + * Tests processed properties. + */ + public function testCrudAndUpdate() { + $entity_type = 'entity_test'; + $this->createField($entity_type); + + // Create an entity with a summary and no text format. + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(); + $entity->summary_field->value = $value = $this->randomMachineName(); + $entity->summary_field->summary = $summary = $this->randomMachineName(); + $entity->summary_field->format = NULL; + $entity->name->value = $this->randomMachineName(); + $entity->save(); + + $entity = entity_load($entity_type, $entity->id()); + $this->assertTrue($entity->summary_field instanceof FieldItemListInterface, 'Field implements interface.'); + $this->assertTrue($entity->summary_field[0] instanceof FieldItemInterface, 'Field item implements interface.'); + $this->assertEqual($entity->summary_field->value, $value); + $this->assertEqual($entity->summary_field->summary, $summary); + $this->assertNull($entity->summary_field->format); + // Even if no format is given, if text processing is enabled, the default + // format is used. + $this->assertEqual($entity->summary_field->processed, "

                          $value

                          \n"); + $this->assertEqual($entity->summary_field->summary_processed, "

                          $summary

                          \n"); + + // Change the format, this should update the processed properties. + $entity->summary_field->format = 'no_filters'; + $this->assertEqual($entity->summary_field->processed, $value); + $this->assertEqual($entity->summary_field->summary_processed, $summary); + + // Test the generateSampleValue() method. + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(); + $entity->summary_field->generateSampleItems(); + $this->entityValidateAndSave($entity); + } + + /** + * Creates a text_with_summary field storage and field. + * + * @param string $entity_type + * Entity type for which the field should be created. + */ + protected function createField($entity_type) { + // Create a field . + $this->fieldStorage = FieldStorageConfig::create(array( + 'field_name' => 'summary_field', + 'entity_type' => $entity_type, + 'type' => 'text_with_summary', + 'settings' => array( + 'max_length' => 10, + ) + )); + $this->fieldStorage->save(); + $this->field = FieldConfig::create([ + 'field_storage' => $this->fieldStorage, + 'bundle' => $entity_type, + ]); + $this->field->save(); + } + +} diff --git a/core/modules/text/tests/src/Unit/Migrate/TextFieldTest.php b/core/modules/text/tests/src/Unit/Migrate/TextFieldTest.php index 78060d6..38ee0d1 100644 --- a/core/modules/text/tests/src/Unit/Migrate/TextFieldTest.php +++ b/core/modules/text/tests/src/Unit/Migrate/TextFieldTest.php @@ -7,7 +7,7 @@ namespace Drupal\Tests\text\Unit\Migrate; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; use Drupal\Tests\UnitTestCase; use Drupal\text\Plugin\migrate\cckfield\TextField; @@ -25,7 +25,7 @@ class TextFieldTest extends UnitTestCase { protected $plugin; /** - * @var \Drupal\migrate\Entity\MigrationInterface + * @var \Drupal\migrate\Plugin\MigrationInterface */ protected $migration; diff --git a/core/modules/text/text.js b/core/modules/text/text.js index 615a1e6..ad89bd8 100644 --- a/core/modules/text/text.js +++ b/core/modules/text/text.js @@ -3,7 +3,7 @@ * Text behaviors. */ -(function ($) { +(function ($, Drupal) { 'use strict'; @@ -58,4 +58,4 @@ } }; -})(jQuery); +})(jQuery, Drupal); diff --git a/core/modules/toolbar/tests/modules/toolbar_disable_user_toolbar/toolbar_disable_user_toolbar.module b/core/modules/toolbar/tests/modules/toolbar_disable_user_toolbar/toolbar_disable_user_toolbar.module index 0678998..27ce3e0 100644 --- a/core/modules/toolbar/tests/modules/toolbar_disable_user_toolbar/toolbar_disable_user_toolbar.module +++ b/core/modules/toolbar/tests/modules/toolbar_disable_user_toolbar/toolbar_disable_user_toolbar.module @@ -1,6 +1,11 @@ entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function getCacheMaxAge() { + // The calculation of which URL (if any) gets put on which tour depends + // on a route access check. This can have a lot of inputs, including user + // permissions and other factors. Rather than doing a complicated + // accounting of the cache metadata for all of these possible factors, set + // the max age of the cache to zero to prevent using incorrect cached + // information. + return 0; + } + + /** + * {@inheritdoc} + */ + public function listTopics() { + /** @var \Drupal\tour\TourInterface[] $tours */ + $tours = $this->entityTypeManager->getStorage('tour')->loadMultiple(); + // Sort in the manner defined by Tour. + uasort($tours, ['Drupal\tour\Entity\Tour', 'sort']); + + // Make a link to each tour, using the first of its routes that can + // be linked to by this user, if any. + $topics = []; + foreach ($tours as $tour) { + $title = $tour->label(); + $id = $tour->id(); + $routes = $tour->getRoutes(); + $made_link = FALSE; + foreach ($routes as $route) { + // Some tours are for routes with parameters. For instance, there is + // currently a tour in the Language module for the language edit page, + // which appears on all pages with URLs like: + // /admin/config/regional/language/edit/LANGCODE. + // There is no way to make a link to the page that displays the tour, + // because it is a set of pages. The easiest way to detect this is to + // use a try/catch exception -- try to make a link, and it will error + // out with a missing parameter exception if the route leads to a set + // of pages instead of a single page. + try { + $params = isset($route['route_params']) ? $route['route_params'] : []; + $url = Url::fromRoute($route['route_name'], $params); + // Skip this route if the current user cannot access it. + if (!$url->access()) { + continue; + } + + // Generate the link HTML directly, using toString(), to catch + // missing parameter exceptions now instead of at render time. + $topics[$id] = Link::fromTextAndUrl($title, $url)->toString(); + // If the line above didn't generate an exception, we have a good + // link that the user can access. + $made_link = TRUE; + break; + } + catch (\Exception $e) { + // Exceptions are normally due to routes that need parameters. If + // there is an exception, just try the next route and see if we can + // find one that will work for us. + } + } + if (!$made_link) { + // None of the routes worked to make a link, so at least display the + // tour title. + $topics[$id] = $title; + } + } + + return $topics; + } + +} diff --git a/core/modules/tour/src/Tests/TourHelpPageTest.php b/core/modules/tour/src/Tests/TourHelpPageTest.php new file mode 100644 index 0000000..a39d6a6 --- /dev/null +++ b/core/modules/tour/src/Tests/TourHelpPageTest.php @@ -0,0 +1,146 @@ +tourUser = $this->drupalCreateUser(['access administration pages', 'access tour', 'administer languages']); + $this->noTourUser = $this->drupalCreateUser(['access administration pages']); + } + + /** + * Logs in users, tests help pages. + */ + public function testHelp() { + $this->drupalLogin($this->tourUser); + $this->verifyHelp(); + + $this->drupalLogin($this->noTourUser); + $this->verifyHelp(FALSE); + } + + /** + * Verifies the logged in user has access to the help properly. + * + * @param bool $tours_ok + * (optional) TRUE (default) if the user should see tours, FALSE if not. + */ + protected function verifyHelp($tours_ok = TRUE) { + $this->drupalGet('admin/help'); + + // All users should be able to see the module section. + $this->assertText('Module overviews are provided by modules'); + foreach ($this->getModuleList() as $name) { + $this->assertLink($name); + } + + // Some users should be able to see the tour section. + if ($tours_ok) { + $this->assertText('Tours guide you through workflows'); + } + else { + $this->assertNoText('Tours guide you through workflows'); + } + + $titles = $this->getTourList(); + + // Test the titles that should be links. + foreach ($titles[0] as $title) { + if ($tours_ok) { + $this->assertLink($title); + } + else { + $this->assertNoLink($title); + // Just test the first item in the list of links that should not + // be there, because the second matches the name of a module that is + // in the Module overviews section, so the link will be there and + // this test will fail. Testing one should be sufficient to verify + // the page is working correctly. + break; + } + } + + // Test the titles that should not be links. + foreach ($titles[1] as $title) { + if ($tours_ok) { + $this->assertText($title); + $this->assertNoLink($title); + } + else { + $this->assertNoText($title); + // Just test the first item in the list of text that should not + // be there, because the second matches part of the name of a module + // that is in the Module overviews section, so the text will be there + // and this test will fail. Testing one should be sufficient to verify + // the page is working correctly. + break; + } + } + } + + /** + * Gets a list of modules to test for hook_help() pages. + * + * @return array + * A list of module names to test. + */ + protected function getModuleList() { + return ['Help', 'Tour']; + } + + /** + * Gets a list of tours to test. + * + * @return array + * A list of tour titles to test. The first array element is a list of tours + * with links, and the second is a list of tours without links. Assumes + * that the user being tested has 'administer languages' permission but + * not 'translate interface'. + */ + protected function getTourList() { + return [['Adding languages', 'Language'], ['Editing languages', 'Translation']]; + } + +} diff --git a/core/modules/tour/src/Tests/TourTest.php b/core/modules/tour/src/Tests/TourTest.php index 7f656d3..cfa311b 100644 --- a/core/modules/tour/src/Tests/TourTest.php +++ b/core/modules/tour/src/Tests/TourTest.php @@ -8,6 +8,7 @@ namespace Drupal\tour\Tests; use Drupal\language\Entity\ConfigurableLanguage; +use Drupal\tour\Entity\Tour; /** * Tests the functionality of tour tips. @@ -108,7 +109,7 @@ public function testTourFunctionality() { $this->assertNotEqual(count($elements), 1, 'Did not find English variant of tip 1.'); // Programmatically create a tour for use through the remainder of the test. - $tour = entity_create('tour', array( + $tour = Tour::create(array( 'id' => 'tour-entity-create-test-en', 'label' => 'Tour test english', 'langcode' => 'en', diff --git a/core/modules/tracker/src/Tests/Migrate/d7/MigrateTrackerNodeTest.php b/core/modules/tracker/src/Tests/Migrate/d7/MigrateTrackerNodeTest.php deleted file mode 100644 index 521e74d..0000000 --- a/core/modules/tracker/src/Tests/Migrate/d7/MigrateTrackerNodeTest.php +++ /dev/null @@ -1,73 +0,0 @@ -installEntitySchema('node'); - $this->installConfig(static::$modules); - $this->installSchema('node', ['node_access']); - $this->installSchema('tracker', ['tracker_node', 'tracker_user']); - - $this->executeMigrations([ - 'd7_user_role', - 'd7_user', - 'd7_node_type', - 'd7_node__test_content_type', - 'd7_tracker_node', - ]); - } - - /** - * Tests migration of tracker node table. - */ - public function testMigrateTrackerNode() { - $connection = Database::getConnection('default', 'migrate'); - $num_rows = $connection - ->select('tracker_node', 'tn') - ->fields('tn', ['nid', 'published', 'changed']) - ->countQuery() - ->execute() - ->fetchField(); - $this->assertIdentical('1', $num_rows); - - $tracker_nodes = $connection - ->select('tracker_node', 'tn') - ->fields('tn', ['nid', 'published', 'changed']) - ->execute(); - $row = $tracker_nodes->fetchAssoc(); - $this->assertIdentical('1', $row['nid']); - $this->assertIdentical('1', $row['published']); - $this->assertIdentical('1421727536', $row['changed']); - } - -} - diff --git a/core/modules/tracker/src/Tests/Migrate/d7/MigrateTrackerSettingsTest.php b/core/modules/tracker/src/Tests/Migrate/d7/MigrateTrackerSettingsTest.php deleted file mode 100644 index f37871e..0000000 --- a/core/modules/tracker/src/Tests/Migrate/d7/MigrateTrackerSettingsTest.php +++ /dev/null @@ -1,37 +0,0 @@ -installConfig(['tracker']); - $this->executeMigration('d7_tracker_settings'); - } - - /** - * Tests migration of tracker's variables to configuration. - */ - public function testMigration() { - $this->assertIdentical(999, \Drupal::config('tracker.settings')->get('cron_index_limit')); - } - -} diff --git a/core/modules/tracker/src/Tests/Migrate/d7/MigrateTrackerUserTest.php b/core/modules/tracker/src/Tests/Migrate/d7/MigrateTrackerUserTest.php deleted file mode 100644 index a9b4a39..0000000 --- a/core/modules/tracker/src/Tests/Migrate/d7/MigrateTrackerUserTest.php +++ /dev/null @@ -1,73 +0,0 @@ -installEntitySchema('node'); - $this->installConfig(static::$modules); - $this->installSchema('node', ['node_access']); - $this->installSchema('tracker', ['tracker_node', 'tracker_user']); - - $this->executeMigrations([ - 'd7_user_role', - 'd7_user', - 'd7_node_type', - 'd7_node__test_content_type', - 'd7_tracker_node', - ]); - } - - /** - * Tests migration of tracker user table. - */ - public function testMigrateTrackerUser() { - $connection = Database::getConnection('default', 'migrate'); - $num_rows = $connection - ->select('tracker_user', 'tn') - ->fields('tu', ['nid', 'uid', 'published', 'changed']) - ->countQuery() - ->execute() - ->fetchField(); - $this->assertIdentical('1', $num_rows); - - $tracker_nodes = $connection - ->select('tracker_user', 'tu') - ->fields('tu', ['nid', 'uid', 'published', 'changed']) - ->execute(); - $row = $tracker_nodes->fetchAssoc(); - $this->assertIdentical('1', $row['nid']); - $this->assertIdentical('2', $row['uid']); - $this->assertIdentical('1', $row['published']); - $this->assertIdentical('1421727536', $row['changed']); - } - -} diff --git a/core/modules/tracker/src/Tests/Views/TrackerTestBase.php b/core/modules/tracker/src/Tests/Views/TrackerTestBase.php index a8b36e8..923fb0c 100644 --- a/core/modules/tracker/src/Tests/Views/TrackerTestBase.php +++ b/core/modules/tracker/src/Tests/Views/TrackerTestBase.php @@ -11,6 +11,7 @@ use Drupal\Core\Language\LanguageInterface; use Drupal\views\Tests\ViewTestBase; use Drupal\views\Tests\ViewTestData; +use Drupal\comment\Entity\Comment; /** * Base class for all tracker tests. @@ -60,7 +61,7 @@ protected function setUp() { 'status' => 1, )); - $this->comment = entity_create('comment', array( + $this->comment = Comment::create(array( 'entity_id' => $this->node->id(), 'entity_type' => 'node', 'field_name' => 'comment', diff --git a/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerNodeTest.php b/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerNodeTest.php new file mode 100644 index 0000000..fcf50c8 --- /dev/null +++ b/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerNodeTest.php @@ -0,0 +1,73 @@ +installEntitySchema('node'); + $this->installConfig(static::$modules); + $this->installSchema('node', ['node_access']); + $this->installSchema('tracker', ['tracker_node', 'tracker_user']); + + $this->executeMigrations([ + 'd7_user_role', + 'd7_user', + 'd7_node_type', + 'd7_node:test_content_type', + 'd7_tracker_node', + ]); + } + + /** + * Tests migration of tracker node table. + */ + public function testMigrateTrackerNode() { + $connection = Database::getConnection('default', 'migrate'); + $num_rows = $connection + ->select('tracker_node', 'tn') + ->fields('tn', ['nid', 'published', 'changed']) + ->countQuery() + ->execute() + ->fetchField(); + $this->assertIdentical('1', $num_rows); + + $tracker_nodes = $connection + ->select('tracker_node', 'tn') + ->fields('tn', ['nid', 'published', 'changed']) + ->execute(); + $row = $tracker_nodes->fetchAssoc(); + $this->assertIdentical('1', $row['nid']); + $this->assertIdentical('1', $row['published']); + $this->assertIdentical('1421727536', $row['changed']); + } + +} + diff --git a/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerSettingsTest.php b/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerSettingsTest.php new file mode 100644 index 0000000..9613959 --- /dev/null +++ b/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerSettingsTest.php @@ -0,0 +1,37 @@ +installConfig(['tracker']); + $this->executeMigration('d7_tracker_settings'); + } + + /** + * Tests migration of tracker's variables to configuration. + */ + public function testMigration() { + $this->assertIdentical(999, \Drupal::config('tracker.settings')->get('cron_index_limit')); + } + +} diff --git a/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerUserTest.php b/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerUserTest.php new file mode 100644 index 0000000..4751c36 --- /dev/null +++ b/core/modules/tracker/tests/src/Kernel/Migrate/d7/MigrateTrackerUserTest.php @@ -0,0 +1,73 @@ +installEntitySchema('node'); + $this->installConfig(static::$modules); + $this->installSchema('node', ['node_access']); + $this->installSchema('tracker', ['tracker_node', 'tracker_user']); + + $this->executeMigrations([ + 'd7_user_role', + 'd7_user', + 'd7_node_type', + 'd7_node:test_content_type', + 'd7_tracker_node', + ]); + } + + /** + * Tests migration of tracker user table. + */ + public function testMigrateTrackerUser() { + $connection = Database::getConnection('default', 'migrate'); + $num_rows = $connection + ->select('tracker_user', 'tn') + ->fields('tu', ['nid', 'uid', 'published', 'changed']) + ->countQuery() + ->execute() + ->fetchField(); + $this->assertIdentical('1', $num_rows); + + $tracker_nodes = $connection + ->select('tracker_user', 'tu') + ->fields('tu', ['nid', 'uid', 'published', 'changed']) + ->execute(); + $row = $tracker_nodes->fetchAssoc(); + $this->assertIdentical('1', $row['nid']); + $this->assertIdentical('2', $row['uid']); + $this->assertIdentical('1', $row['published']); + $this->assertIdentical('1421727536', $row['changed']); + } + +} diff --git a/core/modules/update/src/Form/UpdateManagerInstall.php b/core/modules/update/src/Form/UpdateManagerInstall.php index 28e0507..f47e3ee 100644 --- a/core/modules/update/src/Form/UpdateManagerInstall.php +++ b/core/modules/update/src/Form/UpdateManagerInstall.php @@ -128,7 +128,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { */ public function validateForm(array &$form, FormStateInterface $form_state) { $uploaded_file = $this->getRequest()->files->get('files[project_upload]', NULL, TRUE); - if (!($form_state->getValue('project_url') XOR !empty($uploaded_file))) { + if (!($form_state->getValue('project_url') xor !empty($uploaded_file))) { $form_state->setErrorByName('project_url', $this->t('You must either provide a URL or upload an archive file to install.')); } } @@ -245,7 +245,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // The page title must be passed here to ensure it is initially used when // authorize.php loads for the first time with the FTP/SSH credentials // form. - system_authorized_init('update_authorize_run_install', drupal_get_path('module', 'update') . '/update.authorize.inc', $arguments, $this->t('Update manager')); + system_authorized_init('update_authorize_run_install', __DIR__ . '/../../update.authorize.inc', $arguments, $this->t('Update manager')); $form_state->setRedirectUrl(system_authorized_get_url()); } } diff --git a/core/modules/update/src/Form/UpdateReady.php b/core/modules/update/src/Form/UpdateReady.php index d74d5c4..d1298ca 100644 --- a/core/modules/update/src/Form/UpdateReady.php +++ b/core/modules/update/src/Form/UpdateReady.php @@ -169,7 +169,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { // The page title must be passed here to ensure it is initially used // when authorize.php loads for the first time with the FTP/SSH // credentials form. - system_authorized_init('update_authorize_run_update', drupal_get_path('module', 'update') . '/update.authorize.inc', array($updates), $this->t('Update manager')); + system_authorized_init('update_authorize_run_update', __DIR__ . '/../../update.authorize.inc', array($updates), $this->t('Update manager')); $form_state->setRedirectUrl(system_authorized_get_url()); } } diff --git a/core/modules/update/src/Tests/Migrate/d6/MigrateUpdateConfigsTest.php b/core/modules/update/src/Tests/Migrate/d6/MigrateUpdateConfigsTest.php deleted file mode 100644 index 29cf502..0000000 --- a/core/modules/update/src/Tests/Migrate/d6/MigrateUpdateConfigsTest.php +++ /dev/null @@ -1,48 +0,0 @@ -executeMigration('update_settings'); - } - - /** - * Tests migration of update variables to update.settings.yml. - */ - public function testUpdateSettings() { - $config = $this->config('update.settings'); - $this->assertIdentical(2, $config->get('fetch.max_attempts')); - $this->assertIdentical('http://updates.drupal.org/release-history', $config->get('fetch.url')); - $this->assertIdentical('all', $config->get('notification.threshold')); - $this->assertIdentical(array(), $config->get('notification.emails')); - $this->assertIdentical(7, $config->get('check.interval_days')); - $this->assertConfigSchema(\Drupal::service('config.typed'), 'update.settings', $config->get()); - } - -} diff --git a/core/modules/update/src/Tests/UpdateCoreTest.php b/core/modules/update/src/Tests/UpdateCoreTest.php index b105780..d76cfed 100644 --- a/core/modules/update/src/Tests/UpdateCoreTest.php +++ b/core/modules/update/src/Tests/UpdateCoreTest.php @@ -176,7 +176,7 @@ function testDatestampMismatch() { $system_info = array( '#all' => array( // We need to think we're running a -dev snapshot to see dates. - 'version' => '8.0.0-dev', + 'version' => '8.1.0-dev', 'datestamp' => time(), ), 'block' => array( diff --git a/core/modules/update/src/Tests/UpdateUploadTest.php b/core/modules/update/src/Tests/UpdateUploadTest.php index cad7d78..b9635e1 100644 --- a/core/modules/update/src/Tests/UpdateUploadTest.php +++ b/core/modules/update/src/Tests/UpdateUploadTest.php @@ -52,7 +52,7 @@ public function testUploadModule() { // Check to ensure an existing module can't be reinstalled. Also checks that // the archive was extracted since we can't know if the module is already // installed until after extraction. - $validArchiveFile = drupal_get_path('module', 'update') . '/tests/aaa_update_test.tar.gz'; + $validArchiveFile = __DIR__ . '/../../tests/aaa_update_test.tar.gz'; $edit = array( 'files[project_upload]' => $validArchiveFile, ); @@ -65,7 +65,7 @@ public function testUploadModule() { $moduleUpdater = $updaters['module']['class']; $installedInfoFilePath = $this->container->get('update.root') . '/' . $moduleUpdater::getRootDirectoryRelativePath() . '/update_test_new_module/update_test_new_module.info.yml'; $this->assertFalse(file_exists($installedInfoFilePath), 'The new module does not exist in the filesystem before it is installed with the Update Manager.'); - $validArchiveFile = drupal_get_path('module', 'update') . '/tests/update_test_new_module/8.x-1.0/update_test_new_module.tar.gz'; + $validArchiveFile = __DIR__ . '/../../tests/update_test_new_module/8.x-1.0/update_test_new_module.tar.gz'; $edit = array( 'files[project_upload]' => $validArchiveFile, ); diff --git a/core/modules/update/tests/modules/update_test/src/Controller/UpdateTestController.php b/core/modules/update/tests/modules/update_test/src/Controller/UpdateTestController.php index 600d8bf..b834bbe 100644 --- a/core/modules/update/tests/modules/update_test/src/Controller/UpdateTestController.php +++ b/core/modules/update/tests/modules/update_test/src/Controller/UpdateTestController.php @@ -68,8 +68,7 @@ public function updateTest($project_name, $version) { $availability_scenario = '#broken#'; } - $path = drupal_get_path('module', 'update_test'); - $file = "$path/$project_name.$availability_scenario.xml"; + $file = __DIR__ . "/../../$project_name.$availability_scenario.xml"; $headers = array('Content-Type' => 'text/xml; charset=utf-8'); if (!is_file($file)) { // Return an empty response. diff --git a/core/modules/update/tests/modules/update_test/update_test.module b/core/modules/update/tests/modules/update_test/update_test.module index f8de3cd..04a77e9 100644 --- a/core/modules/update/tests/modules/update_test/update_test.module +++ b/core/modules/update/tests/modules/update_test/update_test.module @@ -1,12 +1,12 @@ executeMigration('update_settings'); + } + + /** + * Tests migration of update variables to update.settings.yml. + */ + public function testUpdateSettings() { + $config = $this->config('update.settings'); + $this->assertIdentical(2, $config->get('fetch.max_attempts')); + $this->assertIdentical('http://updates.drupal.org/release-history', $config->get('fetch.url')); + $this->assertIdentical('all', $config->get('notification.threshold')); + $this->assertIdentical(array(), $config->get('notification.emails')); + $this->assertIdentical(7, $config->get('check.interval_days')); + $this->assertConfigSchema(\Drupal::service('config.typed'), 'update.settings', $config->get()); + } + +} diff --git a/core/modules/user/config/install/user.mail.yml b/core/modules/user/config/install/user.mail.yml index f8e41ce..9f2ad67 100644 --- a/core/modules/user/config/install/user.mail.yml +++ b/core/modules/user/config/install/user.mail.yml @@ -20,7 +20,7 @@ status_activated: body: "[user:display-name],\n\nYour account at [site:name] has been activated.\n\nYou may now log in by clicking this link or copying and pasting it into your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:account-name]\npassword: Your password\n\n-- [site:name] team" subject: 'Account details for [user:display-name] at [site:name] (approved)' status_blocked: - body: "[user:display-name],\n\nYour account on [site:account-name] has been blocked.\n\n-- [site:name] team" + body: "[user:display-name],\n\nYour account on [site:name] has been blocked.\n\n-- [site:name] team" subject: 'Account details for [user:display-name] at [site:name] (blocked)' status_canceled: body: "[user:display-name],\n\nYour account on [site:name] has been canceled.\n\n-- [site:name] team" diff --git a/core/modules/user/config/schema/user.destination.schema.yml b/core/modules/user/config/schema/user.destination.schema.yml deleted file mode 100644 index e95158e..0000000 --- a/core/modules/user/config/schema/user.destination.schema.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Schema for the migrate destination plugin. - -migrate.destination.entity:user: - type: migrate_destination - label: 'User' - mapping: - md5_passwords: - type: boolean - label: 'Passwords' diff --git a/core/modules/user/config/schema/user.source.schema.yml b/core/modules/user/config/schema/user.source.schema.yml deleted file mode 100644 index 81d3a38..0000000 --- a/core/modules/user/config/schema/user.source.schema.yml +++ /dev/null @@ -1,47 +0,0 @@ -# Schema for the user module's migration source plugins. - -migrate.source.d6_user: - type: migrate_source_sql - label: 'Drupal 6 user' - mapping: - constants: - type: mapping - label: 'Constants' - mapping: - key: - type: string - label: 'User data key' - module: - type: string - label: 'Module name' - -migrate.source.d6_user_picture_file: - type: migrate_source_sql - label: 'Drupal 6 user picture display' - mapping: - constants: - type: mapping - label: 'Constant' - mapping: - is_public: - type: boolean - label: 'Public' - -migrate.source.d6_user_picture_instance: - type: migrate_source_sql - label: 'Drupal 6 user picture display' - mapping: - provider: - type: string - label: 'Provider' - constants: - type: migrate_entity_constant - label: 'Constants' - -migrate.source.profile_field: - type: migrate_source_sql - label: 'Profile field' - mapping: - constants: - type: migrate_entity_constant - label: 'Constants' diff --git a/core/modules/user/images/icon-user-active.png b/core/modules/user/images/icon-user-active.png deleted file mode 100644 index 3c9d73b..0000000 --- a/core/modules/user/images/icon-user-active.png +++ /dev/null @@ -1,4 +0,0 @@ -PNG - - IHDRĴl;tEXtSoftwareAdobe ImageReadyqe<IDATxb`v߿@?߇6߿ -@|c q ?DžA2:OlllYp?aU H(f< )A˗/q#~ H"̑O@'?F ^9޽.XсH>@AHHo߾mR%dho޼RT FW^|a fH, \<4ddIENDB` \ No newline at end of file diff --git a/core/modules/user/images/icon-user.png b/core/modules/user/images/icon-user.png deleted file mode 100644 index 3c847c6..0000000 --- a/core/modules/user/images/icon-user.png +++ /dev/null @@ -1,6 +0,0 @@ -PNG - - IHDRĴl;tEXtSoftwareAdobe ImageReadyqe<&IDATxb`j'O -022@lx' Ǐ+ d*`~46, |2@A$СC o'"8 . -O$0(J EdȈ5pipuu} @̀#`8 `/ 7oEN$oR |}}? l^z5u@C3fƒ@zChhW\40W$|Z>le @#%K@^O0fZD 0= -`PC0q WIENDB` \ No newline at end of file diff --git a/core/modules/user/migration_templates/d6_profile_values.yml b/core/modules/user/migration_templates/d6_profile_values.yml index cb36b0e..7d4fbdd 100644 --- a/core/modules/user/migration_templates/d6_profile_values.yml +++ b/core/modules/user/migration_templates/d6_profile_values.yml @@ -1,5 +1,6 @@ id: d6_profile_values label: Profile values +class: Drupal\user\Plugin\migrate\ProfileValues migration_tags: - Drupal 6 builder: diff --git a/core/modules/user/migration_templates/d6_user.yml b/core/modules/user/migration_templates/d6_user.yml index 6d5795b..bf6ec2a 100644 --- a/core/modules/user/migration_templates/d6_user.yml +++ b/core/modules/user/migration_templates/d6_user.yml @@ -26,6 +26,7 @@ process: plugin: migration migration: d6_user_picture_file source: uid + no_stub: true destination: plugin: entity:user md5_passwords: true diff --git a/core/modules/user/migration_templates/d7_user.yml b/core/modules/user/migration_templates/d7_user.yml old mode 100755 new mode 100644 index 703b136..12147f8 --- a/core/modules/user/migration_templates/d7_user.yml +++ b/core/modules/user/migration_templates/d7_user.yml @@ -2,8 +2,7 @@ id: d7_user label: User accounts migration_tags: - Drupal 7 -builder: - plugin: d7_user +class: Drupal\user\Plugin\migrate\User source: plugin: d7_user process: diff --git a/core/modules/user/src/AccountForm.php b/core/modules/user/src/AccountForm.php index 8b0149e..9199c73 100644 --- a/core/modules/user/src/AccountForm.php +++ b/core/modules/user/src/AccountForm.php @@ -127,8 +127,9 @@ public function form(array $form, FormStateInterface $form_state) { // To skip the current password field, the user must have logged in via a // one-time link and have the token in the URL. Store this in $form_state // so it persists even on subsequent Ajax requests. - if (!$form_state->get('user_pass_reset')) { - $user_pass_reset = isset($_SESSION['pass_reset_' . $account->id()]) && Crypt::hashEquals($_SESSION['pass_reset_' . $account->id()], \Drupal::request()->query->get('pass-reset-token')); + if (!$form_state->get('user_pass_reset') && ($token = $this->getRequest()->get('pass-reset-token'))) { + $session_key = 'pass_reset_' . $account->id(); + $user_pass_reset = isset($_SESSION[$session_key]) && Crypt::hashEquals($_SESSION[$session_key], $token); $form_state->set('user_pass_reset', $user_pass_reset); } diff --git a/core/modules/user/src/EventSubscriber/AccessDeniedSubscriber.php b/core/modules/user/src/EventSubscriber/AccessDeniedSubscriber.php index 361e7ce..681abc6 100644 --- a/core/modules/user/src/EventSubscriber/AccessDeniedSubscriber.php +++ b/core/modules/user/src/EventSubscriber/AccessDeniedSubscriber.php @@ -43,7 +43,7 @@ class AccessDeniedSubscriber implements EventSubscriberInterface { * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator * The URL generator. */ - public function __construct(AccountInterface $account, URLGeneratorInterface $url_generator) { + public function __construct(AccountInterface $account, UrlGeneratorInterface $url_generator) { $this->account = $account; $this->setUrlGenerator($url_generator); } diff --git a/core/modules/user/src/PermissionHandler.php b/core/modules/user/src/PermissionHandler.php index ac6e9bb..0e5489e 100644 --- a/core/modules/user/src/PermissionHandler.php +++ b/core/modules/user/src/PermissionHandler.php @@ -163,8 +163,8 @@ protected function buildPermissionsYaml() { $callback_permission += array( 'description' => NULL, + 'provider' => $provider, ); - $callback_permission['provider'] = $provider; $all_callback_permissions[$name] = $callback_permission; } @@ -182,7 +182,7 @@ protected function buildPermissionsYaml() { } $permission['title'] = $this->t($permission['title']); $permission['description'] = isset($permission['description']) ? $this->t($permission['description']) : NULL; - $permission['provider'] = $provider; + $permission['provider'] = !empty($permission['provider']) ? $permission['provider'] : $provider; } $all_permissions += $permissions; diff --git a/core/modules/user/src/Plugin/migrate/ProfileValues.php b/core/modules/user/src/Plugin/migrate/ProfileValues.php new file mode 100644 index 0000000..0c3e51a --- /dev/null +++ b/core/modules/user/src/Plugin/migrate/ProfileValues.php @@ -0,0 +1,53 @@ +init) { + $this->init = TRUE; + $definition['source'] = [ + 'plugin' => 'profile_field', + 'ignore_map' => TRUE, + ] + $this->source; + $definition['destination']['plugin'] = 'null'; + try { + $profile_field_migration = $this->migrationPluginManager->createStubMigration($definition); + $source_plugin = $profile_field_migration->getSourcePlugin(); + $source_plugin->checkRequirements(); + foreach ($source_plugin as $row) { + $name = $row->getSourceProperty('name'); + $this->process[$name] = $name; + } + } + catch (RequirementsException $e) { + // The checkRequirements() call will fail when the profile module does + // not exist on the source site. + } + } + return parent::getProcess(); + } + +} diff --git a/core/modules/user/src/Plugin/migrate/User.php b/core/modules/user/src/Plugin/migrate/User.php new file mode 100644 index 0000000..0586016 --- /dev/null +++ b/core/modules/user/src/Plugin/migrate/User.php @@ -0,0 +1,62 @@ +init) { + $this->init = TRUE; + $definition['source'] = [ + 'entity_type' => 'user', + 'ignore_map' => TRUE, + ] + $this->source; + $definition['destination']['plugin'] = 'null'; + if (\Drupal::moduleHandler()->moduleExists('field')) { + $definition['source']['plugin'] = 'd7_field_instance'; + $field_migration = $this->migrationPluginManager->createStubMigration($definition); + foreach ($field_migration->getSourcePlugin() as $row) { + $field_name = $row->getSourceProperty('field_name'); + $this->process[$field_name] = $field_name; + } + } + try { + $definition['source']['plugin'] = 'profile_field'; + $profile_migration = $this->migrationPluginManager->createStubMigration($definition); + // Ensure that Profile is enabled in the source DB. + $profile_migration->checkRequirements(); + foreach ($profile_migration->getSourcePlugin() as $row) { + $name = $row->getSourceProperty('name'); + $this->process[$name] = $name; + } + } + catch (RequirementsException $e) { + // The checkRequirements() call will fail when the profile module does + // not exist on the source site. + } + } + return parent::getProcess(); + } + +} diff --git a/core/modules/user/src/Plugin/migrate/builder/d6/ProfileValues.php b/core/modules/user/src/Plugin/migrate/builder/d6/ProfileValues.php deleted file mode 100644 index be33173..0000000 --- a/core/modules/user/src/Plugin/migrate/builder/d6/ProfileValues.php +++ /dev/null @@ -1,42 +0,0 @@ -getSourcePlugin('profile_field', $template['source']); - try { - $source_plugin->checkRequirements(); - } - catch (RequirementsException $e) { - return []; - } - - foreach ($source_plugin as $field) { - $migration->setProcessOfProperty($field->getSourceProperty('name'), $field->getSourceProperty('name')); - } - - return [$migration]; - } - -} diff --git a/core/modules/user/src/Plugin/migrate/builder/d7/User.php b/core/modules/user/src/Plugin/migrate/builder/d7/User.php deleted file mode 100644 index 10bcb47..0000000 --- a/core/modules/user/src/Plugin/migrate/builder/d7/User.php +++ /dev/null @@ -1,90 +0,0 @@ -moduleHandler = $module_handler; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('module_handler') - ); - } - - /** - * {@inheritdoc} - */ - public function buildMigrations(array $template) { - $migration = Migration::create($template); - - if ($this->moduleHandler->moduleExists('field')) { - $template['source']['entity_type'] = 'user'; - - $source_plugin = $this->getSourcePlugin('d7_field_instance', $template['source']); - foreach ($source_plugin as $field) { - $field_name = $field->getSourceProperty('field_name'); - $migration->setProcessOfProperty($field_name, $field_name); - } - } - - try { - $profile_fields = $this->getSourcePlugin('profile_field', $template['source']); - // Ensure that Profile is enabled in the source DB. - $profile_fields->checkRequirements(); - foreach ($profile_fields as $field) { - $field_name = $field->getSourceProperty('name'); - $migration->setProcessOfProperty($field_name, $field_name); - } - } - catch (RequirementsException $e) { - // Profile is not enabled in the source DB, so don't do anything. - } - - return [$migration]; - } - -} diff --git a/core/modules/user/src/Plugin/migrate/destination/EntityUser.php b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php index beda7c8..e85d5cd 100644 --- a/core/modules/user/src/Plugin/migrate/destination/EntityUser.php +++ b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php @@ -13,7 +13,7 @@ use Drupal\Core\Field\FieldTypePluginManagerInterface; use Drupal\Core\Field\Plugin\Field\FieldType\EmailItem; use Drupal\Core\Password\PasswordInterface; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\MigrateException; use Drupal\user\MigratePassword; use Drupal\migrate\Plugin\migrate\destination\EntityContentBase; @@ -43,7 +43,7 @@ class EntityUser extends EntityContentBase { * The plugin_id for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. - * @param MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration. * @param EntityStorageInterface $storage * The storage for this entity type. diff --git a/core/modules/user/src/Plugin/migrate/destination/UserData.php b/core/modules/user/src/Plugin/migrate/destination/UserData.php index 5ae639f..6f968b8 100644 --- a/core/modules/user/src/Plugin/migrate/destination/UserData.php +++ b/core/modules/user/src/Plugin/migrate/destination/UserData.php @@ -7,7 +7,7 @@ namespace Drupal\user\Plugin\migrate\destination; -use Drupal\migrate\Entity\MigrationInterface; +use Drupal\migrate\Plugin\MigrationInterface; use Drupal\user\UserData as UserDataStorage; use Drupal\migrate\Row; use Drupal\migrate\Plugin\migrate\destination\DestinationBase; @@ -35,7 +35,7 @@ class UserData extends DestinationBase implements ContainerFactoryPluginInterfac * The plugin_id for the plugin instance. * @param mixed $plugin_definition * The plugin implementation definition. - * @param \Drupal\migrate\Entity\MigrationInterface $migration + * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration. * @param \Drupal\user\UserData $user_data * The user data service. diff --git a/core/modules/user/src/RegisterForm.php b/core/modules/user/src/RegisterForm.php index d236dd3..fc8b0ca 100644 --- a/core/modules/user/src/RegisterForm.php +++ b/core/modules/user/src/RegisterForm.php @@ -7,10 +7,7 @@ namespace Drupal\user; -use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Entity\Query\QueryFactory; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Language\LanguageManagerInterface; /** * Form handler for the user register forms. @@ -20,13 +17,6 @@ class RegisterForm extends AccountForm { /** * {@inheritdoc} */ - public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, QueryFactory $entity_query) { - parent::__construct($entity_manager, $language_manager, $entity_query); - } - - /** - * {@inheritdoc} - */ public function form(array $form, FormStateInterface $form_state) { $user = $this->currentUser(); /** @var \Drupal\user\UserInterface $account */ diff --git a/core/modules/user/src/Tests/Migrate/MigrateUserAdminPassTest.php b/core/modules/user/src/Tests/Migrate/MigrateUserAdminPassTest.php deleted file mode 100644 index 5849650..0000000 --- a/core/modules/user/src/Tests/Migrate/MigrateUserAdminPassTest.php +++ /dev/null @@ -1,116 +0,0 @@ -container->get('module_handler')->loadInclude('user', 'install'); - $this->installEntitySchema('user'); - user_install(); - /** @var \Drupal\user\Entity\User $admin_account */ - $admin_account = User::load(1); - $admin_account->setPassword('original'); - $admin_account->save(); - $this->originalPasswords[1] = $admin_account->getPassword(); - - /** @var \Drupal\user\Entity\User $user_account */ - $user_account = User::create([ - 'uid' => 2, - 'name' => 'original_username', - 'mail' => 'original_email@example.com', - 'pass' => 'original_password', - ]); - $user_account->save(); - $this->originalPasswords[2] = $user_account->getPassword(); - } - - /** - * Tests preserving the admin user's password. - */ - public function testAdminPasswordPreserved() { - $user_data_rows = [ - [ - 'id' => '1', - 'username' => 'site_admin', - 'password' => 'new_password', - 'email' => 'site_admin@example.com', - ], - [ - 'id' => '2', - 'username' => 'random_user', - 'password' => 'random_password', - 'email' => 'random_user@example.com', - ], - ]; - $ids = ['id' => ['type' => 'integer']]; - $config = [ - 'id' => 'users', - 'migration_tags' => ['Admin password test'], - 'source' => [ - 'plugin' => 'embedded_data', - 'data_rows' => $user_data_rows, - 'ids' => $ids, - ], - 'process' => [ - 'uid' => 'id', - 'name' => 'username', - 'mail' => 'email', - 'pass' => 'password', - ], - 'destination' => ['plugin' => 'entity:user'], - ]; - $migration = Migration::create($config); - $this->executeMigration($migration); - - // Verify that admin username and email were changed, but password was not. - /** @var \Drupal\user\Entity\User $admin_account */ - $admin_account = User::load(1); - $this->assertIdentical($admin_account->getUsername(), 'site_admin'); - $this->assertIdentical($admin_account->getEmail(), 'site_admin@example.com'); - $this->assertIdentical($admin_account->getPassword(), $this->originalPasswords[1]); - - // Verify that everything changed for the regular user. - /** @var \Drupal\user\Entity\User $user_account */ - $user_account = User::load(2); - $this->assertIdentical($user_account->getUsername(), 'random_user'); - $this->assertIdentical($user_account->getEmail(), 'random_user@example.com'); - $this->assertNotIdentical($user_account->getPassword(), $this->originalPasswords[2]); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/MigrateUserPictureEntityDisplayTest.php b/core/modules/user/src/Tests/Migrate/MigrateUserPictureEntityDisplayTest.php deleted file mode 100644 index bee41a4..0000000 --- a/core/modules/user/src/Tests/Migrate/MigrateUserPictureEntityDisplayTest.php +++ /dev/null @@ -1,48 +0,0 @@ -installEntitySchema('file'); - $this->executeMigrations([ - 'user_picture_field', - 'user_picture_field_instance', - 'user_picture_entity_display', - ]); - } - - /** - * Tests the Drupal 7 user picture to Drupal 8 entity display migration. - */ - public function testUserPictureEntityDisplay() { - $component = EntityViewDisplay::load('user.user.default')->getComponent('user_picture'); - $this->assertIdentical('image', $component['type']); - $this->assertIdentical('', $component['settings']['image_style']); - $this->assertIdentical('content', $component['settings']['image_link']); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/MigrateUserPictureEntityFormDisplayTest.php b/core/modules/user/src/Tests/Migrate/MigrateUserPictureEntityFormDisplayTest.php deleted file mode 100644 index f3711ff..0000000 --- a/core/modules/user/src/Tests/Migrate/MigrateUserPictureEntityFormDisplayTest.php +++ /dev/null @@ -1,44 +0,0 @@ -executeMigrations([ - 'user_picture_field', - 'user_picture_field_instance', - 'user_picture_entity_form_display', - ]); - } - - /** - * Tests the field's entity form display settings. - */ - public function testEntityFormDisplaySettings() { - $component = EntityFormDisplay::load('user.user.default')->getComponent('user_picture'); - $this->assertIdentical('image_image', $component['type']); - $this->assertIdentical('throbber', $component['settings']['progress_indicator']); - $this->assertIdentical('thumbnail', $component['settings']['preview_image_style']); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/MigrateUserPictureFieldInstanceTest.php b/core/modules/user/src/Tests/Migrate/MigrateUserPictureFieldInstanceTest.php deleted file mode 100644 index 4dbb36b..0000000 --- a/core/modules/user/src/Tests/Migrate/MigrateUserPictureFieldInstanceTest.php +++ /dev/null @@ -1,46 +0,0 @@ -executeMigrations([ - 'user_picture_field', - 'user_picture_field_instance', - ]); - } - - /** - * Test the user picture field migration. - */ - public function testUserPictureField() { - /** @var \Drupal\field\FieldConfigInterface $field */ - $field = FieldConfig::load('user.user.user_picture'); - $this->assertTrue($field instanceof FieldConfigInterface); - $this->assertIdentical('user', $field->getTargetEntityTypeId()); - $this->assertIdentical('user', $field->getTargetBundle()); - $this->assertIdentical('user_picture', $field->getName()); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/MigrateUserPictureFieldTest.php b/core/modules/user/src/Tests/Migrate/MigrateUserPictureFieldTest.php deleted file mode 100644 index 20d036a..0000000 --- a/core/modules/user/src/Tests/Migrate/MigrateUserPictureFieldTest.php +++ /dev/null @@ -1,43 +0,0 @@ -executeMigration('user_picture_field'); - } - - /** - * Test the user picture field migration. - */ - public function testUserPictureField() { - /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */ - $field_storage = FieldStorageConfig::load('user.user_picture'); - $this->assertTrue($field_storage instanceof FieldStorageConfigInterface); - $this->assertIdentical('user.user_picture', $field_storage->id()); - $this->assertIdentical('image', $field_storage->getType()); - $this->assertIdentical('user', $field_storage->getTargetEntityTypeId()); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/MigrateUserProfileEntityDisplayTest.php b/core/modules/user/src/Tests/Migrate/MigrateUserProfileEntityDisplayTest.php deleted file mode 100644 index 1814d73..0000000 --- a/core/modules/user/src/Tests/Migrate/MigrateUserProfileEntityDisplayTest.php +++ /dev/null @@ -1,57 +0,0 @@ -executeMigrations([ - 'user_profile_field', - 'user_profile_field_instance', - 'user_profile_entity_display', - ]); - } - - /** - * Tests migration of user profile fields. - */ - public function testUserProfileFields() { - $display = EntityViewDisplay::load('user.user.default'); - - // Test a text field. - $component = $display->getComponent('profile_color'); - $this->assertIdentical('text_default', $component['type']); - - // Test a list field. - $component = $display->getComponent('profile_bands'); - $this->assertIdentical('text_default', $component['type']); - - // Test a date field. - $component = $display->getComponent('profile_birthdate'); - $this->assertIdentical('datetime_default', $component['type']); - - // Test PROFILE_PRIVATE field is hidden. - $this->assertNull($display->getComponent('profile_sell_address')); - - // Test PROFILE_HIDDEN field is hidden. - $this->assertNull($display->getComponent('profile_sold_to')); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/MigrateUserProfileEntityFormDisplayTest.php b/core/modules/user/src/Tests/Migrate/MigrateUserProfileEntityFormDisplayTest.php deleted file mode 100644 index f39602e..0000000 --- a/core/modules/user/src/Tests/Migrate/MigrateUserProfileEntityFormDisplayTest.php +++ /dev/null @@ -1,62 +0,0 @@ -executeMigrations([ - 'user_profile_field', - 'user_profile_field_instance', - 'user_profile_entity_form_display', - ]); - } - - /** - * Tests migration of user profile fields. - */ - public function testUserProfileEntityFormDisplay() { - $display = EntityFormDisplay::load('user.user.default'); - - // Test a text field. - $component = $display->getComponent('profile_color'); - $this->assertIdentical('text_textfield', $component['type']); - - // Test a list field. - $component = $display->getComponent('profile_bands'); - $this->assertIdentical('text_textfield', $component['type']); - - // Test a date field. - $component = $display->getComponent('profile_birthdate'); - $this->assertIdentical('datetime_default', $component['type']); - - // Test PROFILE_PRIVATE field is hidden. - $this->assertNull($display->getComponent('profile_sell_address')); - - // Test PROFILE_HIDDEN field is hidden. - $this->assertNull($display->getComponent('profile_sold_to')); - - // Test that a checkbox field has the proper display label setting. - $component = $display->getComponent('profile_love_migrations'); - $this->assertIdentical('boolean_checkbox', $component['type']); - $this->assertIdentical(true, $component['settings']['display_label']); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/MigrateUserProfileFieldInstanceTest.php b/core/modules/user/src/Tests/Migrate/MigrateUserProfileFieldInstanceTest.php deleted file mode 100644 index 8498a36..0000000 --- a/core/modules/user/src/Tests/Migrate/MigrateUserProfileFieldInstanceTest.php +++ /dev/null @@ -1,81 +0,0 @@ -executeMigrations([ - 'user_profile_field', - 'user_profile_field_instance', - ]); - } - - /** - * Tests migration of user profile fields. - */ - public function testUserProfileFields() { - // Migrated a text field. - $field = FieldConfig::load('user.user.profile_color'); - $this->assertIdentical('Favorite color', $field->label()); - $this->assertIdentical('List your favorite color', $field->getDescription()); - - // Migrated a textarea. - $field = FieldConfig::load('user.user.profile_biography'); - $this->assertIdentical('Biography', $field->label()); - $this->assertIdentical('Tell people a little bit about yourself', $field->getDescription()); - - // Migrated checkbox field. - $field = FieldConfig::load('user.user.profile_sell_address'); - $this->assertIdentical('Sell your email address?', $field->label()); - $this->assertIdentical("If you check this box, we'll sell your address to spammers to help line the pockets of our shareholders. Thanks!", $field->getDescription()); - - // Migrated selection field. - $field = FieldConfig::load('user.user.profile_sold_to'); - $this->assertIdentical('Sales Category', $field->label()); - $this->assertIdentical("Select the sales categories to which this user's address was sold.", $field->getDescription()); - - // Migrated list field. - $field = FieldConfig::load('user.user.profile_bands'); - $this->assertIdentical('Favorite bands', $field->label()); - $this->assertIdentical("Enter your favorite bands. When you've saved your profile, you'll be able to find other people with the same favorites.", $field->getDescription()); - - // Migrated URL field. - $field = FieldConfig::load('user.user.profile_blog'); - $this->assertIdentical('Blog', $field->label()); - $this->assertIdentical("Paste the full URL, including http://, of your personal blog.", $field->getDescription()); - - // Migrated date field. - $field = FieldConfig::load('user.user.profile_birthdate'); - $this->assertIdentical('Birthdate', $field->label()); - $this->assertIdentical("Enter your birth date and we'll send you a coupon.", $field->getDescription()); - - // Another migrated checkbox field, with a different source visibility setting. - $field = FieldConfig::load('user.user.profile_love_migrations'); - $this->assertIdentical('I love migrations', $field->label()); - $this->assertIdentical("If you check this box, you love migrations.", $field->getDescription()); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/MigrateUserProfileFieldTest.php b/core/modules/user/src/Tests/Migrate/MigrateUserProfileFieldTest.php deleted file mode 100644 index 1abd419..0000000 --- a/core/modules/user/src/Tests/Migrate/MigrateUserProfileFieldTest.php +++ /dev/null @@ -1,75 +0,0 @@ -executeMigration('user_profile_field'); - } - - /** - * Tests migration of user profile fields. - */ - public function testUserProfileFields() { - // Migrated a text field. - $field_storage = FieldStorageConfig::load('user.profile_color'); - $this->assertIdentical('text', $field_storage->getType(), 'Field type is text.'); - $this->assertIdentical(1, $field_storage->getCardinality(), 'Text field has correct cardinality'); - - // Migrated a textarea. - $field_storage = FieldStorageConfig::load('user.profile_biography'); - $this->assertIdentical('text_long', $field_storage->getType(), 'Field type is text_long.'); - - // Migrated checkbox field. - $field_storage = FieldStorageConfig::load('user.profile_sell_address'); - $this->assertIdentical('boolean', $field_storage->getType(), 'Field type is boolean.'); - - // Migrated selection field. - $field_storage = FieldStorageConfig::load('user.profile_sold_to'); - $this->assertIdentical('list_string', $field_storage->getType(), 'Field type is list_string.'); - $settings = $field_storage->getSettings(); - $this->assertEqual($settings['allowed_values'], array( - 'Pill spammers' => 'Pill spammers', - 'Fitness spammers' => 'Fitness spammers', - 'Back\slash' => 'Back\slash', - 'Forward/slash' => 'Forward/slash', - 'Dot.in.the.middle' => 'Dot.in.the.middle', - 'Faithful servant' => 'Faithful servant', - 'Anonymous donor' => 'Anonymous donor', - )); - $this->assertIdentical('list_string', $field_storage->getType(), 'Field type is list_string.'); - - // Migrated list field. - $field_storage = FieldStorageConfig::load('user.profile_bands'); - $this->assertIdentical('text', $field_storage->getType(), 'Field type is text.'); - $this->assertIdentical(-1, $field_storage->getCardinality(), 'List field has correct cardinality'); - - // Migrated URL field. - $field_storage = FieldStorageConfig::load('user.profile_blog'); - $this->assertIdentical('link', $field_storage->getType(), 'Field type is link.'); - - // Migrated date field. - $field_storage = FieldStorageConfig::load('user.profile_birthdate'); - $this->assertIdentical('datetime', $field_storage->getType(), 'Field type is datetime.'); - $this->assertIdentical('date', $field_storage->getSettings()['datetime_type']); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/MigrateUserStubTest.php b/core/modules/user/src/Tests/Migrate/MigrateUserStubTest.php deleted file mode 100644 index 0a65ddf..0000000 --- a/core/modules/user/src/Tests/Migrate/MigrateUserStubTest.php +++ /dev/null @@ -1,43 +0,0 @@ -installEntitySchema('user'); - $this->installSchema('system', ['sequences']); - } - - /** - * Tests creation of user stubs. - */ - public function testStub() { - $this->performStubTest('user'); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/d6/MigrateUserConfigsTest.php b/core/modules/user/src/Tests/Migrate/d6/MigrateUserConfigsTest.php deleted file mode 100644 index dd9c618..0000000 --- a/core/modules/user/src/Tests/Migrate/d6/MigrateUserConfigsTest.php +++ /dev/null @@ -1,97 +0,0 @@ -installSchema('system', 'router'); - $this->container->get('router.builder')->rebuild(); - $this->executeMigrations(['d6_user_mail', 'd6_user_settings']); - } - - /** - * Tests migration of user variables to user.mail.yml. - */ - public function testUserMail() { - $config = $this->config('user.mail'); - - $this->assertIdentical('Account details for [user:name] at [site:name] (approved)', $config->get('status_activated.subject')); - $this->assertIdentical("[user:name],\n\nYour account at [site:name] has been activated.\n\nYou may now log in by clicking on this link or copying and pasting it in your browser:\n\n[user:one-time-login-url]\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\n\nOnce you have set your own password, you will be able to log in to [site:login-url] in the future using:\n\nusername: [user:name]\n", $config->get('status_activated.body')); - $this->assertIdentical('Replacement login information for [user:name] at [site:name]', $config->get('password_reset.subject')); - $this->assertIdentical("[user:name],\n\nA request to reset the password for your account has been made at [site:name].\n\nYou may now log in to [site:url-brief] by clicking on this link or copying and pasting it in your browser:\n\n[user:one-time-login-url]\n\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\n\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password." , $config->get('password_reset.body')); - $this->assertIdentical('Account details for [user:name] at [site:name] (deleted)', $config->get('cancel_confirm.subject')); - $this->assertIdentical("[user:name],\n\nYour account on [site:name] has been deleted.", $config->get('cancel_confirm.body')); - $this->assertIdentical('An administrator created an account for you at [site:name]', $config->get('register_admin_created.subject')); - $this->assertIdentical("[user:name],\n\nA site administrator at [site:name] has created an account for you. You may now log in to [site:login-url] using the following username and password:\n\nusername: [user:name]\npassword: \n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n[user:one-time-login-url]\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\n\n\n-- [site:name] team", $config->get('register_admin_created.body')); - $this->assertIdentical('Account details for [user:name] at [site:name]', $config->get('register_no_approval_required.subject')); - $this->assertIdentical("[user:name],\n\nThank you for registering at [site:name]. You may now log in to [site:login-url] using the following username and password:\n\nusername: [user:name]\npassword: \n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n[user:one-time-login-url]\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\n\n\n-- [site:name] team", $config->get('register_no_approval_required.body')); - $this->assertIdentical('Account details for [user:name] at [site:name] (pending admin approval)', $config->get('register_pending_approval.subject')); - $this->assertIdentical("[user:name],\n\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\n\n\n-- [site:name] team", $config->get('register_pending_approval.body')); - $this->assertIdentical('Account details for [user:name] at [site:name] (blocked)', $config->get('status_blocked.subject')); - $this->assertIdentical("[user:name],\n\nYour account on [site:name] has been blocked.", $config->get('status_blocked.body')); - $this->assertConfigSchema(\Drupal::service('config.typed'), 'user.mail', $config->get()); - } - - /** - * Tests migration of user variables to user.settings.yml. - */ - public function testUserSettings() { - $config = $this->config('user.settings'); - $this->assertIdentical(TRUE, $config->get('notify.status_blocked')); - $this->assertIdentical(FALSE, $config->get('notify.status_activated')); - $this->assertIdentical(FALSE, $config->get('verify_mail')); - $this->assertIdentical('admin_only', $config->get('register')); - $this->assertIdentical('Guest', $config->get('anonymous')); - - // Tests migration of user_register using the AccountSettingsForm. - - // Map D6 value to D8 value - $user_register_map = [ - [0, USER_REGISTER_ADMINISTRATORS_ONLY], - [1, USER_REGISTER_VISITORS], - [2, USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL], - ]; - - foreach ($user_register_map as $map) { - // Tests migration of user_register = 1 - Database::getConnection('default', 'migrate') - ->update('variable') - ->fields(['value' => serialize($map[0])]) - ->condition('name', 'user_register') - ->execute(); - - /** @var \Drupal\migrate\Entity\MigrationInterface $migration */ - $migration = \Drupal::entityManager() - ->getStorage('migration') - ->loadUnchanged('d6_user_settings'); - // Indicate we're rerunning a migration that's already run. - $migration->getIdMap()->prepareUpdate(); - $this->executeMigration($migration); - $form = $this->container->get('form_builder')->getForm(AccountSettingsForm::create($this->container)); - $this->assertIdentical($map[1], $form['registration_cancellation']['user_register']['#value']); - } - } - -} diff --git a/core/modules/user/src/Tests/Migrate/d6/MigrateUserContactSettingsTest.php b/core/modules/user/src/Tests/Migrate/d6/MigrateUserContactSettingsTest.php deleted file mode 100644 index 1bdf5a5..0000000 --- a/core/modules/user/src/Tests/Migrate/d6/MigrateUserContactSettingsTest.php +++ /dev/null @@ -1,53 +0,0 @@ -migrateUsers(FALSE); - $this->installSchema('user', ['users_data']); - $this->executeMigration('d6_user_contact_settings'); - } - - /** - * Tests the Drupal6 user contact settings migration. - */ - public function testUserContactSettings() { - $user_data = \Drupal::service('user.data'); - $module = $key = 'contact'; - $uid = 2; - $setting = $user_data->get($module, $uid, $key); - $this->assertIdentical('1', $setting); - - $uid = 8; - $setting = $user_data->get($module, $uid, $key); - $this->assertIdentical('0', $setting); - - $uid = 15; - $setting = $user_data->get($module, $uid, $key); - $this->assertIdentical(NULL, $setting); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/d6/MigrateUserPictureFileTest.php b/core/modules/user/src/Tests/Migrate/d6/MigrateUserPictureFileTest.php deleted file mode 100644 index 9f7b20a..0000000 --- a/core/modules/user/src/Tests/Migrate/d6/MigrateUserPictureFileTest.php +++ /dev/null @@ -1,61 +0,0 @@ -installEntitySchema('file'); - - /** @var \Drupal\migrate\Entity\MigrationInterface $migration */ - $migration = Migration::load('d6_user_picture_file'); - $source = $migration->get('source'); - $source['site_path'] = 'core/modules/simpletest'; - $migration->set('source', $source); - $this->executeMigration($migration); - } - - /** - * Tests the Drupal 6 user pictures to Drupal 8 migration. - */ - public function testUserPictures() { - $file_ids = array(); - foreach ($this->migration->getIdMap() as $destination_ids) { - $file_ids[] = reset($destination_ids); - } - $files = File::loadMultiple($file_ids); - /** @var \Drupal\file\FileInterface $file */ - $file = array_shift($files); - $this->assertIdentical('image-test.jpg', $file->getFilename()); - $this->assertIdentical('public://image-test.jpg', $file->getFileUri()); - $this->assertIdentical('2', $file->getOwnerId()); - $this->assertIdentical('1901', $file->getSize()); - $this->assertIdentical('image/jpeg', $file->getMimeType()); - - $file = array_shift($files); - $this->assertIdentical('image-test.png', $file->getFilename()); - $this->assertIdentical('public://image-test.png', $file->getFileUri()); - $this->assertIdentical('8', $file->getOwnerId()); - $this->assertFalse($files); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/d6/MigrateUserProfileValuesTest.php b/core/modules/user/src/Tests/Migrate/d6/MigrateUserProfileValuesTest.php deleted file mode 100644 index e4b5dd3..0000000 --- a/core/modules/user/src/Tests/Migrate/d6/MigrateUserProfileValuesTest.php +++ /dev/null @@ -1,70 +0,0 @@ -executeMigrations([ - 'user_profile_field', - 'user_profile_field_instance', - 'user_profile_entity_display', - 'user_profile_entity_form_display', - ]); - $this->migrateUsers(FALSE); - $this->executeMigration('d6_profile_values'); - } - - /** - * Tests Drupal 6 profile values to Drupal 8 migration. - */ - public function testUserProfileValues() { - $user = User::load(2); - $this->assertFalse(is_null($user)); - $this->assertIdentical('red', $user->profile_color->value); - $expected = <<assertIdentical($expected, $user->profile_biography->value); - $this->assertIdentical('1', $user->profile_sell_address->value); - $this->assertIdentical('Back\slash', $user->profile_sold_to->value); - $this->assertIdentical('AC/DC', $user->profile_bands[0]->value); - $this->assertIdentical('Eagles', $user->profile_bands[1]->value); - $this->assertIdentical('Elton John', $user->profile_bands[2]->value); - $this->assertIdentical('Lemonheads', $user->profile_bands[3]->value); - $this->assertIdentical('Rolling Stones', $user->profile_bands[4]->value); - $this->assertIdentical('Queen', $user->profile_bands[5]->value); - $this->assertIdentical('The White Stripes', $user->profile_bands[6]->value); - $this->assertIdentical('1974-06-02', $user->profile_birthdate->value); - $this->assertIdentical('http://example.com/blog', $user->profile_blog->uri); - $this->assertNull($user->profile_blog->title); - $this->assertIdentical([], $user->profile_blog->options); - $this->assertIdentical('http://example.com/blog', $user->profile_blog->uri); - $this->assertNull($user->profile_love_migrations->value); - - $user = User::load(8); - $this->assertIdentical('Forward/slash', $user->profile_sold_to->value); - - $user = User::load(15); - $this->assertIdentical('Dot.in.the.middle', $user->profile_sold_to->value); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/d6/MigrateUserRoleTest.php b/core/modules/user/src/Tests/Migrate/d6/MigrateUserRoleTest.php deleted file mode 100644 index e78bb7f..0000000 --- a/core/modules/user/src/Tests/Migrate/d6/MigrateUserRoleTest.php +++ /dev/null @@ -1,78 +0,0 @@ -executeMigrations(['d6_filter_format', 'd6_user_role']); - } - - /** - * Tests user role migration. - */ - public function testUserRole() { - /** @var \Drupal\migrate\entity\Migration $migration */ - $id_map = Migration::load('d6_user_role')->getIdMap(); - $rid = 'anonymous'; - $anonymous = Role::load($rid); - $this->assertIdentical($rid, $anonymous->id()); - $this->assertIdentical(array('migrate test anonymous permission', 'use text format filtered_html'), $anonymous->getPermissions()); - $this->assertIdentical(array($rid), $id_map->lookupDestinationId(array(1))); - $rid = 'authenticated'; - $authenticated = Role::load($rid); - $this->assertIdentical($rid, $authenticated->id()); - $this->assertIdentical(array('migrate test authenticated permission', 'use text format filtered_html'), $authenticated->getPermissions()); - $this->assertIdentical(array($rid), $id_map->lookupDestinationId(array(2))); - $rid = 'migrate_test_role_1'; - $migrate_test_role_1 = Role::load($rid); - $this->assertIdentical($rid, $migrate_test_role_1->id()); - $this->assertIdentical(array('migrate test role 1 test permission', 'use text format full_html', 'use text format php_code'), $migrate_test_role_1->getPermissions()); - $this->assertIdentical(array($rid), $id_map->lookupDestinationId(array(3))); - $rid = 'migrate_test_role_2'; - $migrate_test_role_2 = Role::load($rid); - $this->assertIdentical(array( - 'migrate test role 2 test permission', - 'use PHP for settings', - 'administer contact forms', - 'skip comment approval', - 'edit own blog content', - 'edit any blog content', - 'delete own blog content', - 'delete any blog content', - 'create forum content', - 'delete any forum content', - 'delete own forum content', - 'edit any forum content', - 'edit own forum content', - 'administer nodes', - 'access content overview', - 'use text format php_code', - ), $migrate_test_role_2->getPermissions()); - $this->assertIdentical($rid, $migrate_test_role_2->id()); - $this->assertIdentical(array($rid), $id_map->lookupDestinationId(array(4))); - $rid = 'migrate_test_role_3_that_is_long'; - $migrate_test_role_3 = Role::load($rid); - $this->assertIdentical($rid, $migrate_test_role_3->id()); - $this->assertIdentical(array($rid), $id_map->lookupDestinationId(array(5))); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/d6/MigrateUserTest.php b/core/modules/user/src/Tests/Migrate/d6/MigrateUserTest.php deleted file mode 100644 index eb94627..0000000 --- a/core/modules/user/src/Tests/Migrate/d6/MigrateUserTest.php +++ /dev/null @@ -1,147 +0,0 @@ -installEntitySchema('file'); - $this->installSchema('file', ['file_usage']); - $this->installEntitySchema('node'); - $this->installSchema('user', ['users_data']); - // Make sure uid 1 is created. - user_install(); - - $file = File::create(array( - 'fid' => 2, - 'uid' => 2, - 'filename' => 'image-test.jpg', - 'uri' => "public://image-test.jpg", - 'filemime' => 'image/jpeg', - 'created' => 1, - 'changed' => 1, - 'status' => FILE_STATUS_PERMANENT, - )); - $file->enforceIsNew(); - file_put_contents($file->getFileUri(), file_get_contents('core/modules/simpletest/files/image-1.png')); - $file->save(); - - $file = File::create(array( - 'fid' => 8, - 'uid' => 8, - 'filename' => 'image-test.png', - 'uri' => "public://image-test.png", - 'filemime' => 'image/png', - 'created' => 1, - 'changed' => 1, - 'status' => FILE_STATUS_PERMANENT, - )); - $file->enforceIsNew(); - file_put_contents($file->getFileUri(), file_get_contents('core/modules/simpletest/files/image-2.jpg')); - $file->save(); - - $this->migrateUsers(); - } - - /** - * Tests the Drupal6 user to Drupal 8 migration. - */ - public function testUser() { - $users = Database::getConnection('default', 'migrate') - ->select('users', 'u') - ->fields('u') - ->condition('uid', 0, '>') - ->execute() - ->fetchAll(); - - foreach ($users as $source) { - // Get roles directly from the source. - $rids = Database::getConnection('default', 'migrate') - ->select('users_roles', 'ur') - ->fields('ur', array('rid')) - ->condition('ur.uid', $source->uid) - ->execute() - ->fetchCol(); - $roles = array(RoleInterface::AUTHENTICATED_ID); - $id_map = Migration::load('d6_user_role')->getIdMap(); - foreach ($rids as $rid) { - $role = $id_map->lookupDestinationId(array($rid)); - $roles[] = reset($role); - } - - /** @var \Drupal\user\UserInterface $user */ - $user = User::load($source->uid); - $this->assertIdentical($source->uid, $user->id()); - $this->assertIdentical($source->name, $user->label()); - $this->assertIdentical($source->mail, $user->getEmail()); - $this->assertIdentical($source->created, $user->getCreatedTime()); - $this->assertIdentical($source->access, $user->getLastAccessedTime()); - $this->assertIdentical($source->login, $user->getLastLoginTime()); - $is_blocked = $source->status == 0; - $this->assertIdentical($is_blocked, $user->isBlocked()); - // $user->getPreferredLangcode() might fallback to default language if the - // user preferred language is not configured on the site. We just want to - // test if the value was imported correctly. - $this->assertIdentical($source->language, $user->preferred_langcode->value); - $expected_timezone_name = $source->timezone_name ?: $this->config('system.date')->get('timezone.default'); - $this->assertIdentical($expected_timezone_name, $user->getTimeZone()); - $this->assertIdentical($source->init, $user->getInitialEmail()); - $this->assertIdentical($roles, $user->getRoles()); - - // We have one empty picture in the data so don't try load that. - if (!empty($source->picture)) { - // Test the user picture. - $file = File::load($user->user_picture->target_id); - $this->assertIdentical(basename($source->picture), $file->getFilename()); - } - - // Use the API to check if the password has been salted and re-hashed to - // conform to Drupal >= 7 for non-admin users. - if ($user->id() != 1) { - $this->assertTrue(\Drupal::service('password') - ->check($source->pass_plain, $user->getPassword())); - } - } - // Rollback the migration and make sure everything is deleted but uid 1. - (new MigrateExecutable($this->migration, $this))->rollback(); - $users = Database::getConnection('default', 'migrate') - ->select('users', 'u') - ->fields('u', ['uid']) - ->condition('uid', 0, '>') - ->execute() - ->fetchCol(); - foreach ($users as $uid) { - $account = User::load($uid); - if ($uid == 1) { - $this->assertNotNull($account, 'User 1 was preserved after rollback'); - } - else { - $this->assertNull($account); - } - } - } - -} diff --git a/core/modules/user/src/Tests/Migrate/d6/ProfileValuesBuilderTest.php b/core/modules/user/src/Tests/Migrate/d6/ProfileValuesBuilderTest.php deleted file mode 100644 index fe623ae..0000000 --- a/core/modules/user/src/Tests/Migrate/d6/ProfileValuesBuilderTest.php +++ /dev/null @@ -1,34 +0,0 @@ -getTemplateByName('d6_profile_values'); - /** @var \Drupal\migrate\Entity\MigrationInterface[] $migrations */ - $migrations = \Drupal::service('plugin.manager.migrate.builder') - ->createInstance('d6_profile_values') - ->buildMigrations($template); - - $this->assertIdentical('d6_profile_values', $migrations[0]->id()); - $process = $migrations[0]->getProcess(); - $this->assertIdentical('profile_color', $process['profile_color'][0]['source']); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/d7/MigrateUserFloodTest.php b/core/modules/user/src/Tests/Migrate/d7/MigrateUserFloodTest.php deleted file mode 100644 index 020e15c..0000000 --- a/core/modules/user/src/Tests/Migrate/d7/MigrateUserFloodTest.php +++ /dev/null @@ -1,45 +0,0 @@ -installConfig(['user']); - $this->executeMigration('d7_user_flood'); - } - - /** - * Tests the migration. - */ - public function testMigration() { - $expected = [ - 'uid_only' => TRUE, - 'ip_limit' => 30, - 'ip_window' => 7200, - 'user_limit' => 22, - 'user_window' => 86400, - '_core' => [ - 'default_config_hash' => 'UYfMzeP1S8jKm9PSvxf7nQNe8DsNS-3bc2WSNNXBQWs', - ], - ]; - $this->assertIdentical($expected, $this->config('user.flood')->get()); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/d7/MigrateUserMailTest.php b/core/modules/user/src/Tests/Migrate/d7/MigrateUserMailTest.php deleted file mode 100644 index 9c88f55..0000000 --- a/core/modules/user/src/Tests/Migrate/d7/MigrateUserMailTest.php +++ /dev/null @@ -1,49 +0,0 @@ -installConfig(['user']); - $this->executeMigration('d7_user_mail'); - } - - /** - * Tests the migration. - */ - public function testMigration() { - $config = $this->config('user.mail'); - $this->assertIdentical('Your account is approved!', $config->get('status_activated.subject')); - $this->assertIdentical('Your account was activated, and there was much rejoicing.', $config->get('status_activated.body')); - $this->assertIdentical('Fix your password', $config->get('password_reset.subject')); - $this->assertIdentical("Nope! You're locked out forever.", $config->get('password_reset.body')); - $this->assertIdentical('So long, bub', $config->get('cancel_confirm.subject')); - $this->assertIdentical('The gates of Drupal are closed to you. Now you will work in the salt mines.', $config->get('cancel_confirm.body')); - $this->assertIdentical('Gawd made you an account', $config->get('register_admin_created.subject')); - $this->assertIdentical('...and she could take it away.', $config->get('register_admin_created.body')); - $this->assertIdentical('Welcome!', $config->get('register_no_approval_required.subject')); - $this->assertIdentical('You can now log in if you can figure out how to use Drupal!', $config->get('register_no_approval_required.body')); - $this->assertIdentical('Soon...', $config->get('register_pending_approval.subject')); - $this->assertIdentical('...you will join our Circle. Let the Drupal flow through you.', $config->get('register_pending_approval.body')); - $this->assertIdentical('BEGONE!', $config->get('status_blocked.subject')); - $this->assertIdentical('You no longer please the robot overlords. Go to your room and chill out.', $config->get('status_blocked.body')); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/d7/MigrateUserRoleTest.php b/core/modules/user/src/Tests/Migrate/d7/MigrateUserRoleTest.php deleted file mode 100644 index 3c94781..0000000 --- a/core/modules/user/src/Tests/Migrate/d7/MigrateUserRoleTest.php +++ /dev/null @@ -1,66 +0,0 @@ -executeMigration('d7_user_role'); - } - - /** - * Asserts aspects of a user role config entity. - * - * @param string $id - * The role ID. - * @param string $label - * The role's expected label. - * @param int|NULL $original_rid - * The original (integer) ID of the role, to check permissions. - */ - protected function assertEntity($id, $label, $original_rid) { - /** @var \Drupal\user\RoleInterface $entity */ - $entity = Role::load($id); - $this->assertTrue($entity instanceof RoleInterface); - $this->assertIdentical($label, $entity->label()); - - if (isset($original_rid)) { - $permissions = Database::getConnection('default', 'migrate') - ->select('role_permission', 'rp') - ->fields('rp', ['permission']) - ->condition('rid', $original_rid) - ->execute() - ->fetchCol(); - $this->assertIdentical($permissions, $entity->getPermissions()); - } - } - - /** - * Tests user role migration. - */ - public function testUserRole() { - $this->assertEntity('anonymous', 'anonymous user', 1); - $this->assertEntity('authenticated', 'authenticated user', 2); - $this->assertEntity('administrator', 'administrator', 3); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/d7/MigrateUserTest.php b/core/modules/user/src/Tests/Migrate/d7/MigrateUserTest.php deleted file mode 100644 index 149672b..0000000 --- a/core/modules/user/src/Tests/Migrate/d7/MigrateUserTest.php +++ /dev/null @@ -1,94 +0,0 @@ -installEntitySchema('file'); - $this->executeMigrations([ - 'user_picture_field', - 'user_picture_field_instance', - 'd7_user_role', - 'd7_user', - ]); - } - - /** - * Asserts various aspects of a user account. - * - * @param string $id - * The user ID. - * @param string $label - * The username. - * @param string $mail - * The user's email address. - * @param int $access - * The last access time. - * @param int $login - * The last login time. - * @param bool $blocked - * Whether or not the account is blocked. - * @param string $langcode - * The user account's language code. - * @param string $init - * The user's initial email address. - * @param string[] $roles - * Role IDs the user account is expected to have. - * @param bool $has_picture - * Whether the user is expected to have a picture attached. - */ - protected function assertEntity($id, $label, $mail, $access, $login, $blocked, $langcode, $init, array $roles = [RoleInterface::AUTHENTICATED_ID], $has_picture = FALSE) { - /** @var \Drupal\user\UserInterface $user */ - $user = User::load($id); - $this->assertTrue($user instanceof UserInterface); - $this->assertIdentical($label, $user->label()); - $this->assertIdentical($mail, $user->getEmail()); - $this->assertIdentical($access, $user->getLastAccessedTime()); - $this->assertIdentical($login, $user->getLastLoginTime()); - $this->assertIdentical($blocked, $user->isBlocked()); - // $user->getPreferredLangcode() might fallback to default language if the - // user preferred language is not configured on the site. We just want to - // test if the value was imported correctly. - $this->assertIdentical($langcode, $user->langcode->value); - $this->assertIdentical($langcode, $user->preferred_langcode->value); - $this->assertIdentical($langcode, $user->preferred_admin_langcode->value); - $this->assertIdentical($init, $user->getInitialEmail()); - $this->assertIdentical($roles, $user->getRoles()); - $this->assertIdentical($has_picture, !$user->user_picture->isEmpty()); - } - - /** - * Tests the Drupal 7 user to Drupal 8 migration. - */ - public function testUser() { - $this->assertEntity(2, 'Odo', 'odo@local.host', '0', '0', FALSE, '', 'odo@local.host'); - } - -} diff --git a/core/modules/user/src/Tests/Migrate/d7/UserMigrationBuilderTest.php b/core/modules/user/src/Tests/Migrate/d7/UserMigrationBuilderTest.php deleted file mode 100644 index 09fae9e..0000000 --- a/core/modules/user/src/Tests/Migrate/d7/UserMigrationBuilderTest.php +++ /dev/null @@ -1,34 +0,0 @@ -getTemplateByName('d7_user'); - /** @var \Drupal\migrate\Entity\MigrationInterface[] $migrations */ - $migrations = \Drupal::service('plugin.manager.migrate.builder') - ->createInstance('d7_user') - ->buildMigrations($template); - - $this->assertIdentical('d7_user', $migrations[0]->id()); - $process = $migrations[0]->getProcess(); - $this->assertIdentical('field_file', $process['field_file'][0]['source']); - } - -} diff --git a/core/modules/user/src/Tests/TempStoreDatabaseTest.php b/core/modules/user/src/Tests/TempStoreDatabaseTest.php index 6e4d837..7d104df 100644 --- a/core/modules/user/src/Tests/TempStoreDatabaseTest.php +++ b/core/modules/user/src/Tests/TempStoreDatabaseTest.php @@ -61,7 +61,7 @@ protected function setUp() { // Install system tables to test the key/value storage without installing a // full Drupal environment. - $this->installSchema('system', array('semaphore', 'key_value_expire')); + $this->installSchema('system', array('key_value_expire')); // Create several objects for testing. for ($i = 0; $i <= 3; $i++) { diff --git a/core/modules/user/src/Tests/Update/UserUpdateEmailToken.php b/core/modules/user/src/Tests/Update/UserUpdateEmailToken.php new file mode 100644 index 0000000..3e1140b --- /dev/null +++ b/core/modules/user/src/Tests/Update/UserUpdateEmailToken.php @@ -0,0 +1,40 @@ +databaseDumpFiles = [ + __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.bare.standard.php.gz', + __DIR__ . '/../../../tests/fixtures/update/drupal-8.user-email-token-2587275.php', + ]; + } + + /** + * Tests that email token in status_blocked of user.mail is updated. + */ + public function testEmailToken() { + $mail = \Drupal::config('user.mail')->get('status_blocked'); + $this->assertTrue(strpos($mail['body'], '[site:account-name]')); + $this->runUpdates(); + $mail = \Drupal::config('user.mail')->get('status_blocked'); + $this->assertFalse(strpos($mail['body'], '[site:account-name]')); + } + +} diff --git a/core/modules/user/src/Tests/UserAccountFormFieldsTest.php b/core/modules/user/src/Tests/UserAccountFormFieldsTest.php index 679eec0..843f036 100644 --- a/core/modules/user/src/Tests/UserAccountFormFieldsTest.php +++ b/core/modules/user/src/Tests/UserAccountFormFieldsTest.php @@ -79,7 +79,6 @@ function testUserEditForm() { $this->installConfig(array('user')); // Install the router table and then rebuild. - $this->installSchema('system', ['router']); \Drupal::service('router.builder')->rebuild(); $form = $this->buildAccountForm('default'); diff --git a/core/modules/user/src/Tests/UserAdminTest.php b/core/modules/user/src/Tests/UserAdminTest.php index cf942f9..595e0ae 100644 --- a/core/modules/user/src/Tests/UserAdminTest.php +++ b/core/modules/user/src/Tests/UserAdminTest.php @@ -28,6 +28,7 @@ class UserAdminTest extends WebTestBase { * Registers a user and deletes it. */ function testUserAdmin() { + $config = $this->config('user.settings'); $user_a = $this->drupalCreateUser(); $user_a->name = 'User A'; $user_a->mail = $this->randomMachineName() . '@example.com'; @@ -101,11 +102,16 @@ function testUserAdmin() { $edit = array(); $edit['action'] = 'user_block_user_action'; $edit['user_bulk_form[4]'] = TRUE; + $config + ->set('notify.status_blocked', TRUE) + ->save(); $this->drupalPostForm('admin/people', $edit, t('Apply'), array( // Sort the table by username so that we know reliably which user will be // targeted with the blocking action. 'query' => array('order' => 'name', 'sort' => 'asc') )); + $site_name = $this->config('system.site')->get('name'); + $this->assertMailString('body', 'Your account on ' . $site_name . ' has been blocked.', 1, 'Blocked message found in the mail sent to user C.'); $user_storage->resetCache(array($user_c->id())); $account = $user_storage->load($user_c->id()); $this->assertTrue($account->isBlocked(), 'User C blocked'); diff --git a/core/modules/user/src/Tests/UserCacheTagsTest.php b/core/modules/user/src/Tests/UserCacheTagsTest.php index 8bb2a6a..450f7bc 100644 --- a/core/modules/user/src/Tests/UserCacheTagsTest.php +++ b/core/modules/user/src/Tests/UserCacheTagsTest.php @@ -9,6 +9,7 @@ use Drupal\system\Tests\Entity\EntityWithUriCacheTagsTestBase; use Drupal\user\Entity\Role; +use Drupal\user\Entity\User; use Drupal\user\RoleInterface; /** @@ -41,10 +42,10 @@ protected function setUp() { */ protected function createEntity() { // Create a "Llama" user. - $user = entity_create('user', array( + $user = User::create([ 'name' => 'Llama', 'status' => TRUE, - )); + ]); $user->save(); return $user; diff --git a/core/modules/user/src/Tests/UserCancelTest.php b/core/modules/user/src/Tests/UserCancelTest.php index d3be1af..3da7489 100644 --- a/core/modules/user/src/Tests/UserCancelTest.php +++ b/core/modules/user/src/Tests/UserCancelTest.php @@ -249,7 +249,7 @@ function testUserBlockUnpublish() { // Add a comment to the page. $comment_subject = $this->randomMachineName(8); $comment_body = $this->randomMachineName(8); - $comment = entity_create('comment', array( + $comment = Comment::create(array( 'subject' => $comment_subject, 'comment_body' => $comment_body, 'entity_id' => $node->id(), @@ -316,7 +316,7 @@ function testUserAnonymize() { // Add a comment to the page. $comment_subject = $this->randomMachineName(8); $comment_body = $this->randomMachineName(8); - $comment = entity_create('comment', array( + $comment = Comment::create(array( 'subject' => $comment_subject, 'comment_body' => $comment_body, 'entity_id' => $node->id(), diff --git a/core/modules/user/src/Tests/UserCreateTest.php b/core/modules/user/src/Tests/UserCreateTest.php index 2674f09..481c267 100644 --- a/core/modules/user/src/Tests/UserCreateTest.php +++ b/core/modules/user/src/Tests/UserCreateTest.php @@ -7,7 +7,9 @@ namespace Drupal\user\Tests; +use Drupal\field\Entity\FieldConfig; use Drupal\simpletest\WebTestBase; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests the create user administration page. @@ -36,7 +38,7 @@ public function testUserAdd() { // Create a field. $field_name = 'test_field'; - entity_create('field_storage_config', array( + FieldStorageConfig::create(array( 'field_name' => $field_name, 'entity_type' => 'user', 'module' => 'image', @@ -49,7 +51,7 @@ public function testUserAdd() { ), ))->save(); - entity_create('field_config', array( + FieldConfig::create([ 'field_name' => $field_name, 'entity_type' => 'user', 'label' => 'Picture', @@ -65,7 +67,7 @@ public function testUserAdd() { 'max_resolution' => '85x85', 'min_resolution' => '', ), - ))->save(); + ])->save(); // Test user creation page for valid fields. $this->drupalGet('admin/people/create'); diff --git a/core/modules/user/src/Tests/UserEntityCallbacksTest.php b/core/modules/user/src/Tests/UserEntityCallbacksTest.php index ce7726a..2a2caaf 100644 --- a/core/modules/user/src/Tests/UserEntityCallbacksTest.php +++ b/core/modules/user/src/Tests/UserEntityCallbacksTest.php @@ -8,6 +8,7 @@ namespace Drupal\user\Tests; use Drupal\simpletest\WebTestBase; +use Drupal\user\Entity\User; /** * Tests specific parts of the user entity like the URI callback and the label @@ -42,7 +43,7 @@ protected function setUp() { parent::setUp(); $this->account = $this->drupalCreateUser(); - $this->anonymous = entity_create('user', array('uid' => 0)); + $this->anonymous = User::create(['uid' => 0]); } /** diff --git a/core/modules/user/src/Tests/UserEntityReferenceTest.php b/core/modules/user/src/Tests/UserEntityReferenceTest.php index 65b92e5..8e21255 100644 --- a/core/modules/user/src/Tests/UserEntityReferenceTest.php +++ b/core/modules/user/src/Tests/UserEntityReferenceTest.php @@ -10,6 +10,7 @@ use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait; use Drupal\field\Entity\FieldConfig; use Drupal\system\Tests\Entity\EntityUnitTestBase; +use Drupal\user\Entity\Role; /** * Tests the user reference field functionality. @@ -40,13 +41,13 @@ class UserEntityReferenceTest extends EntityUnitTestBase { protected function setUp() { parent::setUp(); - $this->role1 = entity_create('user_role', array( + $this->role1 = Role::create(array( 'id' => strtolower($this->randomMachineName(8)), 'label' => $this->randomMachineName(8), )); $this->role1->save(); - $this->role2 = entity_create('user_role', array( + $this->role2 = Role::create(array( 'id' => strtolower($this->randomMachineName(8)), 'label' => $this->randomMachineName(8), )); diff --git a/core/modules/user/src/Tests/UserPasswordResetTest.php b/core/modules/user/src/Tests/UserPasswordResetTest.php index 420b97a..1b415fa 100644 --- a/core/modules/user/src/Tests/UserPasswordResetTest.php +++ b/core/modules/user/src/Tests/UserPasswordResetTest.php @@ -140,6 +140,15 @@ function testUserPasswordReset() { $this->drupalPostForm(NULL, $edit, t('Submit')); $this->assertTrue( count($this->drupalGetMails(array('id' => 'user_password_reset'))) === $before + 1, 'Email sent when requesting password reset using email address.'); + // Visit the user edit page without pass-reset-token and make sure it does + // not cause an error. + $resetURL = $this->getResetURL(); + $this->drupalGet($resetURL); + $this->drupalPostForm(NULL, NULL, t('Log in')); + $this->drupalGet('user/' . $this->account->id() . '/edit'); + $this->assertNoText('Expected user_string to be a string, NULL given'); + $this->drupalLogout(); + // Create a password reset link as if the request time was 60 seconds older than the allowed limit. $timeout = $this->config('user.settings')->get('password_reset_timeout'); $bogus_timestamp = REQUEST_TIME - $timeout - 60; diff --git a/core/modules/user/src/Tests/UserRegistrationTest.php b/core/modules/user/src/Tests/UserRegistrationTest.php index 1e16bc7..bb6bbd7 100644 --- a/core/modules/user/src/Tests/UserRegistrationTest.php +++ b/core/modules/user/src/Tests/UserRegistrationTest.php @@ -288,19 +288,19 @@ public function testUniqueFields() { */ function testRegistrationWithUserFields() { // Create a field on 'user' entity type. - $field_storage = entity_create('field_storage_config', array( + $field_storage = FieldStorageConfig::create(array( 'field_name' => 'test_user_field', 'entity_type' => 'user', 'type' => 'test_field', 'cardinality' => 1, )); $field_storage->save(); - $field = entity_create('field_config', array( + $field = FieldConfig::create([ 'field_storage' => $field_storage, 'label' => 'Some user field', 'bundle' => 'user', 'required' => TRUE, - )); + ]); $field->save(); entity_get_form_display('user', 'user', 'default') ->setComponent('test_user_field', array('type' => 'test_field_widget')) diff --git a/core/modules/user/src/Tests/UserSaveTest.php b/core/modules/user/src/Tests/UserSaveTest.php index 2a4e7b1..b174341 100644 --- a/core/modules/user/src/Tests/UserSaveTest.php +++ b/core/modules/user/src/Tests/UserSaveTest.php @@ -32,13 +32,13 @@ function testUserImport() { $test_name = $this->randomMachineName(); // Create the base user, based on drupalCreateUser(). - $user = entity_create('user', array( + $user = User::create([ 'name' => $test_name, 'uid' => $test_uid, 'mail' => $test_name . '@example.com', 'pass' => user_password(), 'status' => 1, - )); + ]); $user->enforceIsNew(); $user->save(); diff --git a/core/modules/user/src/Tests/UserValidationTest.php b/core/modules/user/src/Tests/UserValidationTest.php index dfa8a21..54e0983 100644 --- a/core/modules/user/src/Tests/UserValidationTest.php +++ b/core/modules/user/src/Tests/UserValidationTest.php @@ -91,10 +91,10 @@ function testValidation() { $this->assertEqual($violations[0]->getMessage(), t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => 60))); // Create a second test user to provoke a name collision. - $user2 = entity_create('user', array( + $user2 = User::create([ 'name' => 'existing', 'mail' => 'existing@example.com', - )); + ]); $user2->save(); $user->set('name', 'existing'); $violations = $user->validate(); @@ -165,11 +165,11 @@ function testValidation() { Role::create(array('id' => 'role2'))->save(); // Test cardinality of user roles. - $user = entity_create('user', array( + $user = User::create([ 'name' => 'role_test', 'mail' => 'test@example.com', 'roles' => array('role1', 'role2'), - )); + ]); $violations = $user->validate(); $this->assertEqual(count($violations), 0); diff --git a/core/modules/user/src/Tests/Views/HandlerFieldPermissionTest.php b/core/modules/user/src/Tests/Views/HandlerFieldPermissionTest.php deleted file mode 100644 index 0756858..0000000 --- a/core/modules/user/src/Tests/Views/HandlerFieldPermissionTest.php +++ /dev/null @@ -1,58 +0,0 @@ -setupPermissionTestData(); - - $view = Views::getView('test_field_permission'); - $this->executeView($view); - $view->initStyle(); - $view->render(); - $style_plugin = $view->style_plugin; - - $expected_permissions = array(); - $expected_permissions[$this->users[0]->id()] = array(); - $expected_permissions[$this->users[1]->id()] = array(); - $expected_permissions[$this->users[2]->id()][] = t('Administer permissions'); - // View user profiles comes first, because we sort by the permission - // machine name. - $expected_permissions[$this->users[3]->id()][] = t('Administer permissions'); - $expected_permissions[$this->users[3]->id()][] = t('Administer users'); - $expected_permissions[$this->users[3]->id()][] = t('View user information'); - - foreach ($view->result as $index => $row) { - $uid = $view->field['uid']->getValue($row); - $rendered_permission = $style_plugin->getField($index, 'permission'); - - $expected_output = implode(', ', $expected_permissions[$uid]); - $this->assertEqual($rendered_permission, $expected_output, 'The right permissions are rendered.'); - } - } - -} diff --git a/core/modules/user/src/Tests/Views/HandlerFilterPermissionTest.php b/core/modules/user/src/Tests/Views/HandlerFilterPermissionTest.php deleted file mode 100644 index ac2644b..0000000 --- a/core/modules/user/src/Tests/Views/HandlerFilterPermissionTest.php +++ /dev/null @@ -1,120 +0,0 @@ -setupPermissionTestData(); - - $column_map = array('uid' => 'uid'); - $view = Views::getView('test_filter_permission'); - - // Filter by a non existing permission. - $view->initHandlers(); - $view->filter['permission']->value = array('non_existent_permission'); - $this->executeView($view); - $this->assertEqual(count($view->result), 4, 'A non existent permission is not filtered so everything is the result.'); - $expected[] = array('uid' => 1); - $expected[] = array('uid' => 2); - $expected[] = array('uid' => 3); - $expected[] = array('uid' => 4); - $this->assertIdenticalResultset($view, $expected, $column_map); - $view->destroy(); - - // Filter by a permission. - $view->initHandlers(); - $view->filter['permission']->value = array('administer permissions'); - $this->executeView($view); - $this->assertEqual(count($view->result), 2); - $expected = array(); - $expected[] = array('uid' => 3); - $expected[] = array('uid' => 4); - $this->assertIdenticalResultset($view, $expected, $column_map); - $view->destroy(); - - // Filter by not a permission. - $view->initHandlers(); - $view->filter['permission']->operator = 'not'; - $view->filter['permission']->value = array('administer users'); - $this->executeView($view); - $this->assertEqual(count($view->result), 3); - $expected = array(); - $expected[] = array('uid' => 1); - $expected[] = array('uid' => 2); - $expected[] = array('uid' => 3); - $this->assertIdenticalResultset($view, $expected, $column_map); - $view->destroy(); - - // Filter by not multiple permissions, that are present in multiple roles. - $view->initHandlers(); - $view->filter['permission']->operator = 'not'; - $view->filter['permission']->value = array('administer users', 'administer permissions'); - $this->executeView($view); - $this->assertEqual(count($view->result), 2); - $expected = array(); - $expected[] = array('uid' => 1); - $expected[] = array('uid' => 2); - $this->assertIdenticalResultset($view, $expected, $column_map); - $view->destroy(); - - // Filter by another permission of a role with multiple permissions. - $view->initHandlers(); - $view->filter['permission']->value = array('administer users'); - $this->executeView($view); - $this->assertEqual(count($view->result), 1); - $expected = array(); - $expected[] = array('uid' => 4); - $this->assertIdenticalResultset($view, $expected, $column_map); - $view->destroy(); - - $view->initDisplay(); - $view->initHandlers(); - - // Test the value options. - $value_options = $view->filter['permission']->getValueOptions(); - - $permission_by_module = []; - $permissions = \Drupal::service('user.permissions')->getPermissions(); - foreach ($permissions as $name => $permission) { - $permission_by_module[$permission['provider']][$name] = $permission; - } - foreach (array('system' => 'System', 'user' => 'User') as $module => $title) { - $expected = array_map(function ($permission) { - return Html::escape(strip_tags($permission['title'])); - }, $permission_by_module[$module]); - - $this->assertEqual($expected, $value_options[$title], 'Ensure the all permissions are available'); - } - } - -} diff --git a/core/modules/user/src/Tests/Views/HandlerFilterRolesTest.php b/core/modules/user/src/Tests/Views/HandlerFilterRolesTest.php deleted file mode 100644 index 6873a76..0000000 --- a/core/modules/user/src/Tests/Views/HandlerFilterRolesTest.php +++ /dev/null @@ -1,75 +0,0 @@ - 'test_user_role']); - $role->save(); - $view = View::load('test_user_name'); - $expected = [ - 'module' => ['user'], - ]; - $this->assertEqual($expected, $view->getDependencies()); - - $display = &$view->getDisplay('default'); - $display['display_options']['filters']['roles_target_id'] = [ - 'id' => 'roles_target_id', - 'table' => 'user__roles', - 'field' => 'roles_target_id', - 'value' => [ - 'test_user_role' => 'test_user_role', - ], - 'plugin_id' => 'user_roles', - ]; - $view->save(); - $expected['config'][] = 'user.role.test_user_role'; - $this->assertEqual($expected, $view->getDependencies()); - - $view = Views::getView('test_user_name'); - $view->initDisplay(); - $view->initHandlers(); - $this->assertEqual(array_keys($view->filter['roles_target_id']->getValueOptions()), ['test_user_role']); - - $view = View::load('test_user_name'); - $display = &$view->getDisplay('default'); - $display['display_options']['filters']['roles_target_id'] = [ - 'id' => 'roles_target_id', - 'table' => 'user__roles', - 'field' => 'roles_target_id', - 'value' => [], - 'plugin_id' => 'user_roles', - ]; - $view->save(); - unset($expected['config']); - $this->assertEqual($expected, $view->getDependencies()); - } - -} diff --git a/core/modules/user/src/Tests/Views/UserKernelTestBase.php b/core/modules/user/src/Tests/Views/UserKernelTestBase.php deleted file mode 100644 index fa26cf2..0000000 --- a/core/modules/user/src/Tests/Views/UserKernelTestBase.php +++ /dev/null @@ -1,95 +0,0 @@ -installEntitySchema('user'); - - $entity_manager = $this->container->get('entity.manager'); - $this->roleStorage = $entity_manager->getStorage('user_role'); - $this->userStorage = $entity_manager->getStorage('user'); - } - - /** - * Set some test data for permission related tests. - */ - protected function setupPermissionTestData() { - // Setup a role without any permission. - $this->roleStorage->create(array('id' => 'authenticated')) - ->save(); - $this->roleStorage->create(array('id' => 'no_permission')) - ->save(); - // Setup a role with just one permission. - $this->roleStorage->create(array('id' => 'one_permission')) - ->save(); - user_role_grant_permissions('one_permission', array('administer permissions')); - // Setup a role with multiple permissions. - $this->roleStorage->create(array('id' => 'multiple_permissions')) - ->save(); - user_role_grant_permissions('multiple_permissions', array('administer permissions', 'administer users', 'access user profiles')); - - // Setup a user without an extra role. - $this->users[] = $account = $this->userStorage->create(['name' => $this->randomString()]); - $account->save(); - // Setup a user with just the first role (so no permission beside the - // ones from the authenticated role). - $this->users[] = $account = $this->userStorage->create(array('name' => 'first_role')); - $account->addRole('no_permission'); - $account->save(); - // Setup a user with just the second role (so one additional permission). - $this->users[] = $account = $this->userStorage->create(array('name' => 'second_role')); - $account->addRole('one_permission'); - $account->save(); - // Setup a user with both the second and the third role. - $this->users[] = $account = $this->userStorage->create(array('name' => 'second_third_role')); - $account->addRole('one_permission'); - $account->addRole('multiple_permissions'); - $account->save(); - } - -} diff --git a/core/modules/user/src/Tests/Views/UserViewsFieldAccessTest.php b/core/modules/user/src/Tests/Views/UserViewsFieldAccessTest.php deleted file mode 100644 index f3e4dc7..0000000 --- a/core/modules/user/src/Tests/Views/UserViewsFieldAccessTest.php +++ /dev/null @@ -1,72 +0,0 @@ -installEntitySchema('user'); - } - - public function testUserFields() { - ConfigurableLanguage::create([ - 'id' => 'es', - 'name' => 'Spanish', - ])->save(); - ConfigurableLanguage::create([ - 'id' => 'fr', - 'name' => 'French', - ])->save(); - - $user = User::create([ - 'name' => 'test user', - 'mail' => 'druplicon@drop.org', - 'status' => 1, - 'preferred_langcode' => 'es', - 'preferred_admin_langcode' => 'fr', - 'timezone' => 'ut1', - 'created' => 123456, - ]); - - $user->save(); - - // @todo Expand the test coverage in https://www.drupal.org/node/2464635 - - $this->assertFieldAccess('user', 'uid', $user->id()); - $this->assertFieldAccess('user', 'uuid', $user->uuid()); - $this->assertFieldAccess('user', 'langcode', $user->language()->getName()); - $this->assertFieldAccess('user', 'preferred_langcode', 'Spanish'); - $this->assertFieldAccess('user', 'preferred_admin_langcode', 'French'); - $this->assertFieldAccess('user', 'name', 'test user'); - // $this->assertFieldAccess('user', 'mail', 'druplicon@drop.org'); - $this->assertFieldAccess('user', 'timezone', 'ut1'); - $this->assertFieldAccess('user', 'status', 'On'); - // $this->assertFieldAccess('user', 'created', \Drupal::service('date.formatter')->format(123456)); - // $this->assertFieldAccess('user', 'changed', \Drupal::service('date.formatter')->format(REQUEST_TIME)); - } - -} diff --git a/core/modules/user/src/UserAccessControlHandler.php b/core/modules/user/src/UserAccessControlHandler.php index ae52b2b..8fa13cf 100644 --- a/core/modules/user/src/UserAccessControlHandler.php +++ b/core/modules/user/src/UserAccessControlHandler.php @@ -22,11 +22,25 @@ class UserAccessControlHandler extends EntityAccessControlHandler { /** + * Allow access to user label. + * + * @var bool + */ + protected $viewLabelOperation = TRUE; + + /** * {@inheritdoc} */ protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { /** @var \Drupal\user\UserInterface $entity*/ + // We don't treat the user label as privileged information, so this check + // has to be the first one in order to allow labels for all users to be + // viewed, including the special anonymous user. + if ($operation === 'view label') { + return AccessResult::allowed(); + } + // The anonymous user's profile can neither be viewed, updated nor deleted. if ($entity->isAnonymous()) { return AccessResult::forbidden(); @@ -41,7 +55,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter case 'view': // Only allow view access if the account is active. if ($account->hasPermission('access user profiles') && $entity->isActive()) { - return AccessResult::allowed()->cachePerPermissions()->cacheUntilEntityChanges($entity); + return AccessResult::allowed()->cachePerPermissions()->addCacheableDependency($entity); } // Users can view own profiles at all times. elseif ($account->id() == $entity->id()) { diff --git a/core/modules/user/src/UserListBuilder.php b/core/modules/user/src/UserListBuilder.php index 5c4e988..f5abe38 100644 --- a/core/modules/user/src/UserListBuilder.php +++ b/core/modules/user/src/UserListBuilder.php @@ -7,6 +7,7 @@ namespace Drupal\user; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityListBuilder; @@ -151,8 +152,19 @@ public function buildRow(EntityInterface $entity) { '#theme' => 'item_list', '#items' => $users_roles, ); - $row['member_for'] = $this->dateFormatter->formatTimeDiffSince($entity->getCreatedTime()); - $row['access'] = $entity->getLastAccessedTime() ? $this->t('@time ago', array('@time' => $this->dateFormatter->formatTimeDiffSince($entity->getLastAccessedTime()))) : t('never'); + $options = [ + 'return_as_object' => TRUE, + ]; + $row['member_for']['data'] = $this->dateFormatter->formatTimeDiffSince($entity->getCreatedTime(), $options)->toRenderable(); + $last_access = $this->dateFormatter->formatTimeDiffSince($entity->getLastAccessedTime(), $options); + + if ($entity->getLastAccessedTime()) { + $row['access']['data']['#markup'] = $last_access->getString(); + CacheableMetadata::createFromObject($last_access)->applyTo($row['access']['data']); + } + else { + $row['access']['data']['#markup'] = t('never'); + } return $row + parent::buildRow($entity); } diff --git a/core/modules/user/src/UserStorage.php b/core/modules/user/src/UserStorage.php index d6a42f4..206329e 100644 --- a/core/modules/user/src/UserStorage.php +++ b/core/modules/user/src/UserStorage.php @@ -7,16 +7,9 @@ namespace Drupal\user; -use Drupal\Core\Database\Connection; -use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Entity\ContentEntityInterface; -use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\Sql\SqlContentEntityStorage; -use Drupal\Core\Password\PasswordInterface; use Drupal\Core\Session\AccountInterface; -use Drupal\Core\Language\LanguageManagerInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * Controller class for users. @@ -27,49 +20,6 @@ class UserStorage extends SqlContentEntityStorage implements UserStorageInterface { /** - * Provides the password hashing service object. - * - * @var \Drupal\Core\Password\PasswordInterface - */ - protected $password; - - /** - * Constructs a new UserStorage object. - * - * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type - * The entity type definition. - * @param \Drupal\Core\Database\Connection $database - * The database connection to be used. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. - * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend - * Cache backend instance to use. - * @param \Drupal\Core\Password\PasswordInterface $password - * The password hashing service. - * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager - * The language manager. - */ - public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, CacheBackendInterface $cache, PasswordInterface $password, LanguageManagerInterface $language_manager) { - parent::__construct($entity_type, $database, $entity_manager, $cache, $language_manager); - - $this->password = $password; - } - - /** - * {@inheritdoc} - */ - public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { - return new static( - $entity_type, - $container->get('database'), - $container->get('entity.manager'), - $container->get('cache.entity'), - $container->get('password'), - $container->get('language_manager') - ); - } - - /** * {@inheritdoc} */ protected function doSaveFieldItems(ContentEntityInterface $entity, array $names = []) { diff --git a/core/modules/user/src/UserViewsData.php b/core/modules/user/src/UserViewsData.php index c71b2ac..e31e5c5 100644 --- a/core/modules/user/src/UserViewsData.php +++ b/core/modules/user/src/UserViewsData.php @@ -20,7 +20,7 @@ class UserViewsData extends EntityViewsData { public function getViewsData() { $data = parent::getViewsData(); - $data['users_field_data']['table']['base']['help'] = t('Users who have created accounts on your site.'); + $data['users_field_data']['table']['base']['help'] = $this->t('Users who have created accounts on your site.'); $data['users_field_data']['table']['base']['access query tag'] = 'user_access'; $data['users_field_data']['table']['wizard_id'] = 'user'; @@ -32,31 +32,32 @@ public function getViewsData() { 'empty field name' => \Drupal::config('user.settings')->get('anonymous'), ); $data['users_field_data']['uid']['filter']['id'] = 'user_name'; - $data['users_field_data']['uid']['filter']['title'] = t('Name'); + $data['users_field_data']['uid']['filter']['title'] = $this->t('Name (autocomplete)'); + $data['users_field_data']['uid']['filter']['help'] = $this->t('The user or author name. Uses an autocomplete widget to find a user name, the actual filter uses the resulting user ID.'); $data['users_field_data']['uid']['relationship'] = array( - 'title' => t('Content authored'), - 'help' => t('Relate content to the user who created it. This relationship will create one record for each content item created by the user.'), + 'title' => $this->t('Content authored'), + 'help' => $this->t('Relate content to the user who created it. This relationship will create one record for each content item created by the user.'), 'id' => 'standard', 'base' => 'node_field_data', 'base field' => 'uid', 'field' => 'uid', - 'label' => t('nodes'), + 'label' => $this->t('nodes'), ); $data['users_field_data']['uid_raw'] = array( - 'help' => t('The raw numeric user ID.'), + 'help' => $this->t('The raw numeric user ID.'), 'real field' => 'uid', 'filter' => array( - 'title' => t('The user ID'), + 'title' => $this->t('The user ID'), 'id' => 'numeric', ), ); $data['users_field_data']['uid_representative'] = array( 'relationship' => array( - 'title' => t('Representative node'), - 'label' => t('Representative node'), - 'help' => t('Obtains a single representative node for each user, according to a chosen sort criterion.'), + 'title' => $this->t('Representative node'), + 'label' => $this->t('Representative node'), + 'help' => $this->t('Obtains a single representative node for each user, according to a chosen sort criterion.'), 'id' => 'groupwise_max', 'relationship field' => 'uid', 'outer field' => 'users_field_data.uid', @@ -70,33 +71,33 @@ public function getViewsData() { $data['users']['uid_current'] = array( 'real field' => 'uid', - 'title' => t('Current'), - 'help' => t('Filter the view to the currently logged in user.'), + 'title' => $this->t('Current'), + 'help' => $this->t('Filter the view to the currently logged in user.'), 'filter' => array( 'id' => 'user_current', 'type' => 'yes-no', ), ); - $data['users_field_data']['name']['help'] = t('The user or author name.'); + $data['users_field_data']['name']['help'] = $this->t('The user or author name.'); $data['users_field_data']['name']['field']['default_formatter'] = 'user_name'; - $data['users_field_data']['name']['filter']['title'] = t('Name (raw)'); - $data['users_field_data']['name']['filter']['help'] = t('The user or author name. This filter does not check if the user exists and allows partial matching. Does not use autocomplete.'); + $data['users_field_data']['name']['filter']['title'] = $this->t('Name (raw)'); + $data['users_field_data']['name']['filter']['help'] = $this->t('The user or author name. This filter does not check if the user exists and allows partial matching. Does not use autocomplete.'); // Note that this field implements field level access control. - $data['users_field_data']['mail']['help'] = t('Email address for a given user. This field is normally not shown to users, so be cautious when using it.'); + $data['users_field_data']['mail']['help'] = $this->t('Email address for a given user. This field is normally not shown to users, so be cautious when using it.'); - $data['users_field_data']['langcode']['help'] = t('Original language of the user information'); - $data['users_field_data']['langcode']['help'] = t('Language of the translation of user information'); + $data['users_field_data']['langcode']['help'] = $this->t('Original language of the user information'); + $data['users_field_data']['langcode']['help'] = $this->t('Language of the translation of user information'); - $data['users_field_data']['preferred_langcode']['title'] = t('Preferred language'); - $data['users_field_data']['preferred_langcode']['help'] = t('Preferred language of the user'); - $data['users_field_data']['preferred_admin_langcode']['title'] = t('Preferred admin language'); - $data['users_field_data']['preferred_admin_langcode']['help'] = t('Preferred administrative language of the user'); + $data['users_field_data']['preferred_langcode']['title'] = $this->t('Preferred language'); + $data['users_field_data']['preferred_langcode']['help'] = $this->t('Preferred language of the user'); + $data['users_field_data']['preferred_admin_langcode']['title'] = $this->t('Preferred admin language'); + $data['users_field_data']['preferred_admin_langcode']['help'] = $this->t('Preferred administrative language of the user'); $data['users_field_data']['created_fulldate'] = array( - 'title' => t('Created date'), - 'help' => t('Date in the form of CCYYMMDD.'), + 'title' => $this->t('Created date'), + 'help' => $this->t('Date in the form of CCYYMMDD.'), 'argument' => array( 'field' => 'created', 'id' => 'date_fulldate', @@ -104,8 +105,8 @@ public function getViewsData() { ); $data['users_field_data']['created_year_month'] = array( - 'title' => t('Created year + month'), - 'help' => t('Date in the form of YYYYMM.'), + 'title' => $this->t('Created year + month'), + 'help' => $this->t('Date in the form of YYYYMM.'), 'argument' => array( 'field' => 'created', 'id' => 'date_year_month', @@ -113,8 +114,8 @@ public function getViewsData() { ); $data['users_field_data']['created_year'] = array( - 'title' => t('Created year'), - 'help' => t('Date in the form of YYYY.'), + 'title' => $this->t('Created year'), + 'help' => $this->t('Date in the form of YYYY.'), 'argument' => array( 'field' => 'created', 'id' => 'date_year', @@ -122,8 +123,8 @@ public function getViewsData() { ); $data['users_field_data']['created_month'] = array( - 'title' => t('Created month'), - 'help' => t('Date in the form of MM (01 - 12).'), + 'title' => $this->t('Created month'), + 'help' => $this->t('Date in the form of MM (01 - 12).'), 'argument' => array( 'field' => 'created', 'id' => 'date_month', @@ -131,8 +132,8 @@ public function getViewsData() { ); $data['users_field_data']['created_day'] = array( - 'title' => t('Created day'), - 'help' => t('Date in the form of DD (01 - 31).'), + 'title' => $this->t('Created day'), + 'help' => $this->t('Date in the form of DD (01 - 31).'), 'argument' => array( 'field' => 'created', 'id' => 'date_day', @@ -140,22 +141,22 @@ public function getViewsData() { ); $data['users_field_data']['created_week'] = array( - 'title' => t('Created week'), - 'help' => t('Date in the form of WW (01 - 53).'), + 'title' => $this->t('Created week'), + 'help' => $this->t('Date in the form of WW (01 - 53).'), 'argument' => array( 'field' => 'created', 'id' => 'date_week', ), ); - $data['users_field_data']['status']['filter']['label'] = t('Active'); + $data['users_field_data']['status']['filter']['label'] = $this->t('Active'); $data['users_field_data']['status']['filter']['type'] = 'yes-no'; - $data['users_field_data']['changed']['title'] = t('Updated date'); + $data['users_field_data']['changed']['title'] = $this->t('Updated date'); $data['users_field_data']['changed_fulldate'] = array( - 'title' => t('Updated date'), - 'help' => t('Date in the form of CCYYMMDD.'), + 'title' => $this->t('Updated date'), + 'help' => $this->t('Date in the form of CCYYMMDD.'), 'argument' => array( 'field' => 'changed', 'id' => 'date_fulldate', @@ -163,8 +164,8 @@ public function getViewsData() { ); $data['users_field_data']['changed_year_month'] = array( - 'title' => t('Updated year + month'), - 'help' => t('Date in the form of YYYYMM.'), + 'title' => $this->t('Updated year + month'), + 'help' => $this->t('Date in the form of YYYYMM.'), 'argument' => array( 'field' => 'changed', 'id' => 'date_year_month', @@ -172,8 +173,8 @@ public function getViewsData() { ); $data['users_field_data']['changed_year'] = array( - 'title' => t('Updated year'), - 'help' => t('Date in the form of YYYY.'), + 'title' => $this->t('Updated year'), + 'help' => $this->t('Date in the form of YYYY.'), 'argument' => array( 'field' => 'changed', 'id' => 'date_year', @@ -181,8 +182,8 @@ public function getViewsData() { ); $data['users_field_data']['changed_month'] = array( - 'title' => t('Updated month'), - 'help' => t('Date in the form of MM (01 - 12).'), + 'title' => $this->t('Updated month'), + 'help' => $this->t('Date in the form of MM (01 - 12).'), 'argument' => array( 'field' => 'changed', 'id' => 'date_month', @@ -190,8 +191,8 @@ public function getViewsData() { ); $data['users_field_data']['changed_day'] = array( - 'title' => t('Updated day'), - 'help' => t('Date in the form of DD (01 - 31).'), + 'title' => $this->t('Updated day'), + 'help' => $this->t('Date in the form of DD (01 - 31).'), 'argument' => array( 'field' => 'changed', 'id' => 'date_day', @@ -199,8 +200,8 @@ public function getViewsData() { ); $data['users_field_data']['changed_week'] = array( - 'title' => t('Updated week'), - 'help' => t('Date in the form of WW (01 - 53).'), + 'title' => $this->t('Updated week'), + 'help' => $this->t('Date in the form of WW (01 - 53).'), 'argument' => array( 'field' => 'changed', 'id' => 'date_week', @@ -208,8 +209,8 @@ public function getViewsData() { ); $data['users']['data'] = array( - 'title' => t('Data'), - 'help' => t('Provides access to the user data service.'), + 'title' => $this->t('Data'), + 'help' => $this->t('Provides access to the user data service.'), 'real field' => 'uid', 'field' => array( 'id' => 'user_data', @@ -217,14 +218,14 @@ public function getViewsData() { ); $data['users']['user_bulk_form'] = array( - 'title' => t('Bulk update'), - 'help' => t('Add a form element that lets you run operations on multiple users.'), + 'title' => $this->t('Bulk update'), + 'help' => $this->t('Add a form element that lets you run operations on multiple users.'), 'field' => array( 'id' => 'user_bulk_form', ), ); - $data['user__roles']['table']['group'] = t('User'); + $data['user__roles']['table']['group'] = $this->t('User'); $data['user__roles']['table']['join'] = array( 'users_field_data' => array( @@ -234,8 +235,8 @@ public function getViewsData() { ); $data['user__roles']['roles_target_id'] = array( - 'title' => t('Roles'), - 'help' => t('Roles that a user belongs to.'), + 'title' => $this->t('Roles'), + 'help' => $this->t('Roles that a user belongs to.'), 'field' => array( 'id' => 'user_roles', 'no group by' => TRUE, @@ -248,15 +249,15 @@ public function getViewsData() { 'id' => 'user__roles_rid', 'name table' => 'role', 'name field' => 'name', - 'empty field name' => t('No role'), + 'empty field name' => $this->t('No role'), 'zero is null' => TRUE, 'numeric' => TRUE, ), ); $data['user__roles']['permission'] = array( - 'title' => t('Permission'), - 'help' => t('The user permissions.'), + 'title' => $this->t('Permission'), + 'help' => $this->t('The user permissions.'), 'field' => array( 'id' => 'user_permissions', 'no group by' => TRUE, diff --git a/core/modules/user/tests/fixtures/update/drupal-8.user-email-token-2587275.php b/core/modules/user/tests/fixtures/update/drupal-8.user-email-token-2587275.php new file mode 100644 index 0000000..28dab8b --- /dev/null +++ b/core/modules/user/tests/fixtures/update/drupal-8.user-email-token-2587275.php @@ -0,0 +1,22 @@ +delete('config')->condition('name', 'user.mail')->execute(); +$connection->insert('config') +->fields(array('collection', 'name', 'data')) +->values(array( + 'collection' => '', + 'name' => 'user.mail', + 'data' => "a:10:{s:14:\"cancel_confirm\";a:2:{s:4:\"body\";s:369:\"[user:name],\n\nA request to cancel your account has been made at [site:name].\n\nYou may now cancel your account on [site:url-brief] by clicking this link or copying and pasting it into your browser:\n\n[user:cancel-url]\n\nNOTE: The cancellation of your account is not reversible.\n\nThis link expires in one day and nothing will happen if it is not used.\n\n-- [site:name] team\";s:7:\"subject\";s:59:\"Account cancellation request for [user:name] at [site:name]\";}s:14:\"password_reset\";a:2:{s:4:\"body\";s:397:\"[user:name],\n\nA request to reset the password for your account has been made at [site:name].\n\nYou may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password. It expires after one day and nothing will happen if it's not used.\n\n-- [site:name] team\";s:7:\"subject\";s:60:\"Replacement login information for [user:name] at [site:name]\";}s:22:\"register_admin_created\";a:2:{s:4:\"body\";s:463:\"[user:name],\n\nA site administrator at [site:name] has created an account for you. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:58:\"An administrator created an account for you at [site:name]\";}s:29:\"register_no_approval_required\";a:2:{s:4:\"body\";s:437:\"[user:name],\n\nThank you for registering at [site:name]. You may now log in by clicking this link or copying and pasting it to your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:46:\"Account details for [user:name] at [site:name]\";}s:25:\"register_pending_approval\";a:2:{s:4:\"body\";s:281:\"[user:name],\n\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\n\n\n-- [site:name] team\";s:7:\"subject\";s:71:\"Account details for [user:name] at [site:name] (pending admin approval)\";}s:31:\"register_pending_approval_admin\";a:2:{s:4:\"body\";s:56:\"[user:name] has applied for an account.\n\n[user:edit-url]\";s:7:\"subject\";s:71:\"Account details for [user:name] at [site:name] (pending admin approval)\";}s:16:\"status_activated\";a:2:{s:4:\"body\";s:446:\"[user:name],\n\nYour account at [site:name] has been activated.\n\nYou may now log in by clicking this link or copying and pasting it into your browser:\n\n[user:one-time-login-url]\n\nThis link can only be used once to log in and will lead you to a page where you can set your password.\n\nAfter setting your password, you will be able to log in at [site:login-url] in the future using:\n\nusername: [user:name]\npassword: Your password\n\n-- [site:name] team\";s:7:\"subject\";s:57:\"Account details for [user:name] at [site:name] (approved)\";}s:14:\"status_blocked\";a:2:{s:4:\"body\";s:89:\"[user:name],\n\nYour account on [site:account-name] has been blocked.\n\n-- [site:name] team\";s:7:\"subject\";s:56:\"Account details for [user:name] at [site:name] (blocked)\";}s:15:\"status_canceled\";a:2:{s:4:\"body\";s:82:\"[user:name],\n\nYour account on [site:name] has been canceled.\n\n-- [site:name] team\";s:7:\"subject\";s:57:\"Account details for [user:name] at [site:name] (canceled)\";}s:8:\"langcode\";s:2:\"en\";}" +))->execute(); diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserAdminPassTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserAdminPassTest.php new file mode 100644 index 0000000..6f0918d --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserAdminPassTest.php @@ -0,0 +1,114 @@ +container->get('module_handler')->loadInclude('user', 'install'); + $this->installEntitySchema('user'); + user_install(); + /** @var \Drupal\user\Entity\User $admin_account */ + $admin_account = User::load(1); + $admin_account->setPassword('original'); + $admin_account->save(); + $this->originalPasswords[1] = $admin_account->getPassword(); + + /** @var \Drupal\user\Entity\User $user_account */ + $user_account = User::create([ + 'uid' => 2, + 'name' => 'original_username', + 'mail' => 'original_email@example.com', + 'pass' => 'original_password', + ]); + $user_account->save(); + $this->originalPasswords[2] = $user_account->getPassword(); + } + + /** + * Tests preserving the admin user's password. + */ + public function testAdminPasswordPreserved() { + $user_data_rows = [ + [ + 'id' => '1', + 'username' => 'site_admin', + 'password' => 'new_password', + 'email' => 'site_admin@example.com', + ], + [ + 'id' => '2', + 'username' => 'random_user', + 'password' => 'random_password', + 'email' => 'random_user@example.com', + ], + ]; + $ids = ['id' => ['type' => 'integer']]; + $definition = [ + 'id' => 'users', + 'migration_tags' => ['Admin password test'], + 'source' => [ + 'plugin' => 'embedded_data', + 'data_rows' => $user_data_rows, + 'ids' => $ids, + ], + 'process' => [ + 'uid' => 'id', + 'name' => 'username', + 'mail' => 'email', + 'pass' => 'password', + ], + 'destination' => ['plugin' => 'entity:user'], + ]; + $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($definition); + $this->executeMigration($migration); + + // Verify that admin username and email were changed, but password was not. + /** @var \Drupal\user\Entity\User $admin_account */ + $admin_account = User::load(1); + $this->assertIdentical($admin_account->getUsername(), 'site_admin'); + $this->assertIdentical($admin_account->getEmail(), 'site_admin@example.com'); + $this->assertIdentical($admin_account->getPassword(), $this->originalPasswords[1]); + + // Verify that everything changed for the regular user. + /** @var \Drupal\user\Entity\User $user_account */ + $user_account = User::load(2); + $this->assertIdentical($user_account->getUsername(), 'random_user'); + $this->assertIdentical($user_account->getEmail(), 'random_user@example.com'); + $this->assertNotIdentical($user_account->getPassword(), $this->originalPasswords[2]); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserPictureEntityDisplayTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserPictureEntityDisplayTest.php new file mode 100644 index 0000000..14c7c49 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserPictureEntityDisplayTest.php @@ -0,0 +1,48 @@ +installEntitySchema('file'); + $this->executeMigrations([ + 'user_picture_field', + 'user_picture_field_instance', + 'user_picture_entity_display', + ]); + } + + /** + * Tests the Drupal 7 user picture to Drupal 8 entity display migration. + */ + public function testUserPictureEntityDisplay() { + $component = EntityViewDisplay::load('user.user.default')->getComponent('user_picture'); + $this->assertIdentical('image', $component['type']); + $this->assertIdentical('', $component['settings']['image_style']); + $this->assertIdentical('content', $component['settings']['image_link']); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserPictureEntityFormDisplayTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserPictureEntityFormDisplayTest.php new file mode 100644 index 0000000..bc28eae --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserPictureEntityFormDisplayTest.php @@ -0,0 +1,44 @@ +executeMigrations([ + 'user_picture_field', + 'user_picture_field_instance', + 'user_picture_entity_form_display', + ]); + } + + /** + * Tests the field's entity form display settings. + */ + public function testEntityFormDisplaySettings() { + $component = EntityFormDisplay::load('user.user.default')->getComponent('user_picture'); + $this->assertIdentical('image_image', $component['type']); + $this->assertIdentical('throbber', $component['settings']['progress_indicator']); + $this->assertIdentical('thumbnail', $component['settings']['preview_image_style']); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserPictureFieldInstanceTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserPictureFieldInstanceTest.php new file mode 100644 index 0000000..6e87113 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserPictureFieldInstanceTest.php @@ -0,0 +1,46 @@ +executeMigrations([ + 'user_picture_field', + 'user_picture_field_instance', + ]); + } + + /** + * Test the user picture field migration. + */ + public function testUserPictureField() { + /** @var \Drupal\field\FieldConfigInterface $field */ + $field = FieldConfig::load('user.user.user_picture'); + $this->assertTrue($field instanceof FieldConfigInterface); + $this->assertIdentical('user', $field->getTargetEntityTypeId()); + $this->assertIdentical('user', $field->getTargetBundle()); + $this->assertIdentical('user_picture', $field->getName()); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserPictureFieldTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserPictureFieldTest.php new file mode 100644 index 0000000..3644493 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserPictureFieldTest.php @@ -0,0 +1,43 @@ +executeMigration('user_picture_field'); + } + + /** + * Test the user picture field migration. + */ + public function testUserPictureField() { + /** @var \Drupal\field\FieldStorageConfigInterface $field_storage */ + $field_storage = FieldStorageConfig::load('user.user_picture'); + $this->assertTrue($field_storage instanceof FieldStorageConfigInterface); + $this->assertIdentical('user.user_picture', $field_storage->id()); + $this->assertIdentical('image', $field_storage->getType()); + $this->assertIdentical('user', $field_storage->getTargetEntityTypeId()); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityDisplayTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityDisplayTest.php new file mode 100644 index 0000000..11c40d5 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityDisplayTest.php @@ -0,0 +1,57 @@ +executeMigrations([ + 'user_profile_field', + 'user_profile_field_instance', + 'user_profile_entity_display', + ]); + } + + /** + * Tests migration of user profile fields. + */ + public function testUserProfileFields() { + $display = EntityViewDisplay::load('user.user.default'); + + // Test a text field. + $component = $display->getComponent('profile_color'); + $this->assertIdentical('text_default', $component['type']); + + // Test a list field. + $component = $display->getComponent('profile_bands'); + $this->assertIdentical('text_default', $component['type']); + + // Test a date field. + $component = $display->getComponent('profile_birthdate'); + $this->assertIdentical('datetime_default', $component['type']); + + // Test PROFILE_PRIVATE field is hidden. + $this->assertNull($display->getComponent('profile_sell_address')); + + // Test PROFILE_HIDDEN field is hidden. + $this->assertNull($display->getComponent('profile_sold_to')); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityFormDisplayTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityFormDisplayTest.php new file mode 100644 index 0000000..d212607 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileEntityFormDisplayTest.php @@ -0,0 +1,62 @@ +executeMigrations([ + 'user_profile_field', + 'user_profile_field_instance', + 'user_profile_entity_form_display', + ]); + } + + /** + * Tests migration of user profile fields. + */ + public function testUserProfileEntityFormDisplay() { + $display = EntityFormDisplay::load('user.user.default'); + + // Test a text field. + $component = $display->getComponent('profile_color'); + $this->assertIdentical('text_textfield', $component['type']); + + // Test a list field. + $component = $display->getComponent('profile_bands'); + $this->assertIdentical('text_textfield', $component['type']); + + // Test a date field. + $component = $display->getComponent('profile_birthdate'); + $this->assertIdentical('datetime_default', $component['type']); + + // Test PROFILE_PRIVATE field is hidden. + $this->assertNull($display->getComponent('profile_sell_address')); + + // Test PROFILE_HIDDEN field is hidden. + $this->assertNull($display->getComponent('profile_sold_to')); + + // Test that a checkbox field has the proper display label setting. + $component = $display->getComponent('profile_love_migrations'); + $this->assertIdentical('boolean_checkbox', $component['type']); + $this->assertIdentical(true, $component['settings']['display_label']); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileFieldInstanceTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileFieldInstanceTest.php new file mode 100644 index 0000000..d2d2e9d --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileFieldInstanceTest.php @@ -0,0 +1,81 @@ +executeMigrations([ + 'user_profile_field', + 'user_profile_field_instance', + ]); + } + + /** + * Tests migration of user profile fields. + */ + public function testUserProfileFields() { + // Migrated a text field. + $field = FieldConfig::load('user.user.profile_color'); + $this->assertIdentical('Favorite color', $field->label()); + $this->assertIdentical('List your favorite color', $field->getDescription()); + + // Migrated a textarea. + $field = FieldConfig::load('user.user.profile_biography'); + $this->assertIdentical('Biography', $field->label()); + $this->assertIdentical('Tell people a little bit about yourself', $field->getDescription()); + + // Migrated checkbox field. + $field = FieldConfig::load('user.user.profile_sell_address'); + $this->assertIdentical('Sell your email address?', $field->label()); + $this->assertIdentical("If you check this box, we'll sell your address to spammers to help line the pockets of our shareholders. Thanks!", $field->getDescription()); + + // Migrated selection field. + $field = FieldConfig::load('user.user.profile_sold_to'); + $this->assertIdentical('Sales Category', $field->label()); + $this->assertIdentical("Select the sales categories to which this user's address was sold.", $field->getDescription()); + + // Migrated list field. + $field = FieldConfig::load('user.user.profile_bands'); + $this->assertIdentical('Favorite bands', $field->label()); + $this->assertIdentical("Enter your favorite bands. When you've saved your profile, you'll be able to find other people with the same favorites.", $field->getDescription()); + + // Migrated URL field. + $field = FieldConfig::load('user.user.profile_blog'); + $this->assertIdentical('Blog', $field->label()); + $this->assertIdentical("Paste the full URL, including http://, of your personal blog.", $field->getDescription()); + + // Migrated date field. + $field = FieldConfig::load('user.user.profile_birthdate'); + $this->assertIdentical('Birthdate', $field->label()); + $this->assertIdentical("Enter your birth date and we'll send you a coupon.", $field->getDescription()); + + // Another migrated checkbox field, with a different source visibility setting. + $field = FieldConfig::load('user.user.profile_love_migrations'); + $this->assertIdentical('I love migrations', $field->label()); + $this->assertIdentical("If you check this box, you love migrations.", $field->getDescription()); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileFieldTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileFieldTest.php new file mode 100644 index 0000000..bb2edf45 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserProfileFieldTest.php @@ -0,0 +1,75 @@ +executeMigration('user_profile_field'); + } + + /** + * Tests migration of user profile fields. + */ + public function testUserProfileFields() { + // Migrated a text field. + $field_storage = FieldStorageConfig::load('user.profile_color'); + $this->assertIdentical('text', $field_storage->getType(), 'Field type is text.'); + $this->assertIdentical(1, $field_storage->getCardinality(), 'Text field has correct cardinality'); + + // Migrated a textarea. + $field_storage = FieldStorageConfig::load('user.profile_biography'); + $this->assertIdentical('text_long', $field_storage->getType(), 'Field type is text_long.'); + + // Migrated checkbox field. + $field_storage = FieldStorageConfig::load('user.profile_sell_address'); + $this->assertIdentical('boolean', $field_storage->getType(), 'Field type is boolean.'); + + // Migrated selection field. + $field_storage = FieldStorageConfig::load('user.profile_sold_to'); + $this->assertIdentical('list_string', $field_storage->getType(), 'Field type is list_string.'); + $settings = $field_storage->getSettings(); + $this->assertEqual($settings['allowed_values'], array( + 'Pill spammers' => 'Pill spammers', + 'Fitness spammers' => 'Fitness spammers', + 'Back\slash' => 'Back\slash', + 'Forward/slash' => 'Forward/slash', + 'Dot.in.the.middle' => 'Dot.in.the.middle', + 'Faithful servant' => 'Faithful servant', + 'Anonymous donor' => 'Anonymous donor', + )); + $this->assertIdentical('list_string', $field_storage->getType(), 'Field type is list_string.'); + + // Migrated list field. + $field_storage = FieldStorageConfig::load('user.profile_bands'); + $this->assertIdentical('text', $field_storage->getType(), 'Field type is text.'); + $this->assertIdentical(-1, $field_storage->getCardinality(), 'List field has correct cardinality'); + + // Migrated URL field. + $field_storage = FieldStorageConfig::load('user.profile_blog'); + $this->assertIdentical('link', $field_storage->getType(), 'Field type is link.'); + + // Migrated date field. + $field_storage = FieldStorageConfig::load('user.profile_birthdate'); + $this->assertIdentical('datetime', $field_storage->getType(), 'Field type is datetime.'); + $this->assertIdentical('date', $field_storage->getSettings()['datetime_type']); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/MigrateUserStubTest.php b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserStubTest.php new file mode 100644 index 0000000..8e7e6ef --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/MigrateUserStubTest.php @@ -0,0 +1,43 @@ +installEntitySchema('user'); + $this->installSchema('system', ['sequences']); + } + + /** + * Tests creation of user stubs. + */ + public function testStub() { + $this->performStubTest('user'); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserConfigsTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserConfigsTest.php new file mode 100644 index 0000000..cd51021 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserConfigsTest.php @@ -0,0 +1,94 @@ +container->get('router.builder')->rebuild(); + $this->executeMigrations(['d6_user_mail', 'd6_user_settings']); + } + + /** + * Tests migration of user variables to user.mail.yml. + */ + public function testUserMail() { + $config = $this->config('user.mail'); + + $this->assertIdentical('Account details for [user:name] at [site:name] (approved)', $config->get('status_activated.subject')); + $this->assertIdentical("[user:name],\n\nYour account at [site:name] has been activated.\n\nYou may now log in by clicking on this link or copying and pasting it in your browser:\n\n[user:one-time-login-url]\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\n\nOnce you have set your own password, you will be able to log in to [site:login-url] in the future using:\n\nusername: [user:name]\n", $config->get('status_activated.body')); + $this->assertIdentical('Replacement login information for [user:name] at [site:name]', $config->get('password_reset.subject')); + $this->assertIdentical("[user:name],\n\nA request to reset the password for your account has been made at [site:name].\n\nYou may now log in to [site:url-brief] by clicking on this link or copying and pasting it in your browser:\n\n[user:one-time-login-url]\n\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\n\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password." , $config->get('password_reset.body')); + $this->assertIdentical('Account details for [user:name] at [site:name] (deleted)', $config->get('cancel_confirm.subject')); + $this->assertIdentical("[user:name],\n\nYour account on [site:name] has been deleted.", $config->get('cancel_confirm.body')); + $this->assertIdentical('An administrator created an account for you at [site:name]', $config->get('register_admin_created.subject')); + $this->assertIdentical("[user:name],\n\nA site administrator at [site:name] has created an account for you. You may now log in to [site:login-url] using the following username and password:\n\nusername: [user:name]\npassword: \n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n[user:one-time-login-url]\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\n\n\n-- [site:name] team", $config->get('register_admin_created.body')); + $this->assertIdentical('Account details for [user:name] at [site:name]', $config->get('register_no_approval_required.subject')); + $this->assertIdentical("[user:name],\n\nThank you for registering at [site:name]. You may now log in to [site:login-url] using the following username and password:\n\nusername: [user:name]\npassword: \n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n[user:one-time-login-url]\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to [user:edit-url] so you can change your password.\n\n\n-- [site:name] team", $config->get('register_no_approval_required.body')); + $this->assertIdentical('Account details for [user:name] at [site:name] (pending admin approval)', $config->get('register_pending_approval.subject')); + $this->assertIdentical("[user:name],\n\nThank you for registering at [site:name]. Your application for an account is currently pending approval. Once it has been approved, you will receive another email containing information about how to log in, set your password, and other details.\n\n\n-- [site:name] team", $config->get('register_pending_approval.body')); + $this->assertIdentical('Account details for [user:name] at [site:name] (blocked)', $config->get('status_blocked.subject')); + $this->assertIdentical("[user:name],\n\nYour account on [site:name] has been blocked.", $config->get('status_blocked.body')); + $this->assertConfigSchema(\Drupal::service('config.typed'), 'user.mail', $config->get()); + } + + /** + * Tests migration of user variables to user.settings.yml. + */ + public function testUserSettings() { + $config = $this->config('user.settings'); + $this->assertIdentical(TRUE, $config->get('notify.status_blocked')); + $this->assertIdentical(FALSE, $config->get('notify.status_activated')); + $this->assertIdentical(FALSE, $config->get('verify_mail')); + $this->assertIdentical('admin_only', $config->get('register')); + $this->assertIdentical('Guest', $config->get('anonymous')); + + // Tests migration of user_register using the AccountSettingsForm. + + // Map D6 value to D8 value + $user_register_map = [ + [0, USER_REGISTER_ADMINISTRATORS_ONLY], + [1, USER_REGISTER_VISITORS], + [2, USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL], + ]; + + foreach ($user_register_map as $map) { + // Tests migration of user_register = 1 + Database::getConnection('default', 'migrate') + ->update('variable') + ->fields(['value' => serialize($map[0])]) + ->condition('name', 'user_register') + ->execute(); + + /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ + $migration = $this->getMigration('d6_user_settings'); + // Indicate we're rerunning a migration that's already run. + $migration->getIdMap()->prepareUpdate(); + $this->executeMigration($migration); + $form = $this->container->get('form_builder')->getForm(AccountSettingsForm::create($this->container)); + $this->assertIdentical($map[1], $form['registration_cancellation']['user_register']['#value']); + } + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserContactSettingsTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserContactSettingsTest.php new file mode 100644 index 0000000..11bbb1a --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserContactSettingsTest.php @@ -0,0 +1,53 @@ +migrateUsers(FALSE); + $this->installSchema('user', ['users_data']); + $this->executeMigration('d6_user_contact_settings'); + } + + /** + * Tests the Drupal6 user contact settings migration. + */ + public function testUserContactSettings() { + $user_data = \Drupal::service('user.data'); + $module = $key = 'contact'; + $uid = 2; + $setting = $user_data->get($module, $uid, $key); + $this->assertIdentical('1', $setting); + + $uid = 8; + $setting = $user_data->get($module, $uid, $key); + $this->assertIdentical('0', $setting); + + $uid = 15; + $setting = $user_data->get($module, $uid, $key); + $this->assertIdentical(NULL, $setting); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserPictureFileTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserPictureFileTest.php new file mode 100644 index 0000000..ff51635 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserPictureFileTest.php @@ -0,0 +1,60 @@ +installEntitySchema('file'); + + /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ + $migration = $this->getMigration('d6_user_picture_file'); + $source = $migration->getSourceConfiguration(); + $source['site_path'] = 'core/modules/simpletest'; + $migration->set('source', $source); + $this->executeMigration($migration); + } + + /** + * Tests the Drupal 6 user pictures to Drupal 8 migration. + */ + public function testUserPictures() { + $file_ids = array(); + foreach ($this->migration->getIdMap() as $destination_ids) { + $file_ids[] = reset($destination_ids); + } + $files = File::loadMultiple($file_ids); + /** @var \Drupal\file\FileInterface $file */ + $file = array_shift($files); + $this->assertIdentical('image-test.jpg', $file->getFilename()); + $this->assertIdentical('public://image-test.jpg', $file->getFileUri()); + $this->assertIdentical('2', $file->getOwnerId()); + $this->assertIdentical('1901', $file->getSize()); + $this->assertIdentical('image/jpeg', $file->getMimeType()); + + $file = array_shift($files); + $this->assertIdentical('image-test.png', $file->getFilename()); + $this->assertIdentical('public://image-test.png', $file->getFileUri()); + $this->assertIdentical('8', $file->getOwnerId()); + $this->assertFalse($files); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php new file mode 100644 index 0000000..8d273fa --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserProfileValuesTest.php @@ -0,0 +1,70 @@ +executeMigrations([ + 'user_profile_field', + 'user_profile_field_instance', + 'user_profile_entity_display', + 'user_profile_entity_form_display', + ]); + $this->migrateUsers(FALSE); + $this->executeMigration('d6_profile_values'); + } + + /** + * Tests Drupal 6 profile values to Drupal 8 migration. + */ + public function testUserProfileValues() { + $user = User::load(2); + $this->assertFalse(is_null($user)); + $this->assertIdentical('red', $user->profile_color->value); + $expected = <<assertIdentical($expected, $user->profile_biography->value); + $this->assertIdentical('1', $user->profile_sell_address->value); + $this->assertIdentical('Back\slash', $user->profile_sold_to->value); + $this->assertIdentical('AC/DC', $user->profile_bands[0]->value); + $this->assertIdentical('Eagles', $user->profile_bands[1]->value); + $this->assertIdentical('Elton John', $user->profile_bands[2]->value); + $this->assertIdentical('Lemonheads', $user->profile_bands[3]->value); + $this->assertIdentical('Rolling Stones', $user->profile_bands[4]->value); + $this->assertIdentical('Queen', $user->profile_bands[5]->value); + $this->assertIdentical('The White Stripes', $user->profile_bands[6]->value); + $this->assertIdentical('1974-06-02', $user->profile_birthdate->value); + $this->assertIdentical('http://example.com/blog', $user->profile_blog->uri); + $this->assertNull($user->profile_blog->title); + $this->assertIdentical([], $user->profile_blog->options); + $this->assertIdentical('http://example.com/blog', $user->profile_blog->uri); + $this->assertNull($user->profile_love_migrations->value); + + $user = User::load(8); + $this->assertIdentical('Forward/slash', $user->profile_sold_to->value); + + $user = User::load(15); + $this->assertIdentical('Dot.in.the.middle', $user->profile_sold_to->value); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserRoleTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserRoleTest.php new file mode 100644 index 0000000..aeab36f --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserRoleTest.php @@ -0,0 +1,77 @@ +executeMigrations(['d6_filter_format', 'd6_user_role']); + } + + /** + * Tests user role migration. + */ + public function testUserRole() { + /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ + $id_map = $this->getMigration('d6_user_role')->getIdMap(); + $rid = 'anonymous'; + $anonymous = Role::load($rid); + $this->assertIdentical($rid, $anonymous->id()); + $this->assertIdentical(array('migrate test anonymous permission', 'use text format filtered_html'), $anonymous->getPermissions()); + $this->assertIdentical(array($rid), $id_map->lookupDestinationId(array(1))); + $rid = 'authenticated'; + $authenticated = Role::load($rid); + $this->assertIdentical($rid, $authenticated->id()); + $this->assertIdentical(array('migrate test authenticated permission', 'use text format filtered_html'), $authenticated->getPermissions()); + $this->assertIdentical(array($rid), $id_map->lookupDestinationId(array(2))); + $rid = 'migrate_test_role_1'; + $migrate_test_role_1 = Role::load($rid); + $this->assertIdentical($rid, $migrate_test_role_1->id()); + $this->assertIdentical(array('migrate test role 1 test permission', 'use text format full_html', 'use text format php_code'), $migrate_test_role_1->getPermissions()); + $this->assertIdentical(array($rid), $id_map->lookupDestinationId(array(3))); + $rid = 'migrate_test_role_2'; + $migrate_test_role_2 = Role::load($rid); + $this->assertIdentical(array( + 'migrate test role 2 test permission', + 'use PHP for settings', + 'administer contact forms', + 'skip comment approval', + 'edit own blog content', + 'edit any blog content', + 'delete own blog content', + 'delete any blog content', + 'create forum content', + 'delete any forum content', + 'delete own forum content', + 'edit any forum content', + 'edit own forum content', + 'administer nodes', + 'access content overview', + 'use text format php_code', + ), $migrate_test_role_2->getPermissions()); + $this->assertIdentical($rid, $migrate_test_role_2->id()); + $this->assertIdentical(array($rid), $id_map->lookupDestinationId(array(4))); + $rid = 'migrate_test_role_3_that_is_long'; + $migrate_test_role_3 = Role::load($rid); + $this->assertIdentical($rid, $migrate_test_role_3->id()); + $this->assertIdentical(array($rid), $id_map->lookupDestinationId(array(5))); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php new file mode 100644 index 0000000..a7e8f4d --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/d6/MigrateUserTest.php @@ -0,0 +1,150 @@ +installEntitySchema('file'); + $this->installSchema('file', ['file_usage']); + $this->installEntitySchema('node'); + $this->installSchema('user', ['users_data']); + // Make sure uid 1 is created. + user_install(); + + $file = File::create(array( + 'fid' => 2, + 'uid' => 2, + 'filename' => 'image-test.jpg', + 'uri' => "public://image-test.jpg", + 'filemime' => 'image/jpeg', + 'created' => 1, + 'changed' => 1, + 'status' => FILE_STATUS_PERMANENT, + )); + $file->enforceIsNew(); + file_put_contents($file->getFileUri(), file_get_contents('core/modules/simpletest/files/image-1.png')); + $file->save(); + + $file = File::create(array( + 'fid' => 8, + 'uid' => 8, + 'filename' => 'image-test.png', + 'uri' => "public://image-test.png", + 'filemime' => 'image/png', + 'created' => 1, + 'changed' => 1, + 'status' => FILE_STATUS_PERMANENT, + )); + $file->enforceIsNew(); + file_put_contents($file->getFileUri(), file_get_contents('core/modules/simpletest/files/image-2.jpg')); + $file->save(); + + $this->migrateUsers(); + } + + /** + * Tests the Drupal6 user to Drupal 8 migration. + */ + public function testUser() { + $users = Database::getConnection('default', 'migrate') + ->select('users', 'u') + ->fields('u') + ->condition('uid', 0, '>') + ->execute() + ->fetchAll(); + + foreach ($users as $source) { + // Get roles directly from the source. + $rids = Database::getConnection('default', 'migrate') + ->select('users_roles', 'ur') + ->fields('ur', array('rid')) + ->condition('ur.uid', $source->uid) + ->execute() + ->fetchCol(); + $roles = array(RoleInterface::AUTHENTICATED_ID); + $id_map = $this->getMigration('d6_user_role')->getIdMap(); + foreach ($rids as $rid) { + $role = $id_map->lookupDestinationId(array($rid)); + $roles[] = reset($role); + } + + /** @var \Drupal\user\UserInterface $user */ + $user = User::load($source->uid); + $this->assertIdentical($source->uid, $user->id()); + $this->assertIdentical($source->name, $user->label()); + $this->assertIdentical($source->mail, $user->getEmail()); + $this->assertIdentical($source->created, $user->getCreatedTime()); + $this->assertIdentical($source->access, $user->getLastAccessedTime()); + $this->assertIdentical($source->login, $user->getLastLoginTime()); + $is_blocked = $source->status == 0; + $this->assertIdentical($is_blocked, $user->isBlocked()); + // $user->getPreferredLangcode() might fallback to default language if the + // user preferred language is not configured on the site. We just want to + // test if the value was imported correctly. + $this->assertIdentical($source->language, $user->preferred_langcode->value); + $expected_timezone_name = $source->timezone_name ?: $this->config('system.date')->get('timezone.default'); + $this->assertIdentical($expected_timezone_name, $user->getTimeZone()); + $this->assertIdentical($source->init, $user->getInitialEmail()); + $this->assertIdentical($roles, $user->getRoles()); + + // We have one empty picture in the data so don't try load that. + if (!empty($source->picture)) { + // Test the user picture. + $file = File::load($user->user_picture->target_id); + $this->assertIdentical(basename($source->picture), $file->getFilename()); + } + else { + // Ensure the user does not have a picture. + $this->assertFalse($user->user_picture->target_id, sprintf('User %s does not have a picture', $user->id())); + } + + // Use the API to check if the password has been salted and re-hashed to + // conform to Drupal >= 7 for non-admin users. + if ($user->id() != 1) { + $this->assertTrue(\Drupal::service('password') + ->check($source->pass_plain, $user->getPassword())); + } + } + // Rollback the migration and make sure everything is deleted but uid 1. + (new MigrateExecutable($this->migration, $this))->rollback(); + $users = Database::getConnection('default', 'migrate') + ->select('users', 'u') + ->fields('u', ['uid']) + ->condition('uid', 0, '>') + ->execute() + ->fetchCol(); + foreach ($users as $uid) { + $account = User::load($uid); + if ($uid == 1) { + $this->assertNotNull($account, 'User 1 was preserved after rollback'); + } + else { + $this->assertNull($account); + } + } + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserFloodTest.php b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserFloodTest.php new file mode 100644 index 0000000..0351006 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserFloodTest.php @@ -0,0 +1,45 @@ +installConfig(['user']); + $this->executeMigration('d7_user_flood'); + } + + /** + * Tests the migration. + */ + public function testMigration() { + $expected = [ + 'uid_only' => TRUE, + 'ip_limit' => 30, + 'ip_window' => 7200, + 'user_limit' => 22, + 'user_window' => 86400, + '_core' => [ + 'default_config_hash' => 'UYfMzeP1S8jKm9PSvxf7nQNe8DsNS-3bc2WSNNXBQWs', + ], + ]; + $this->assertIdentical($expected, $this->config('user.flood')->get()); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserMailTest.php b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserMailTest.php new file mode 100644 index 0000000..a9645de --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserMailTest.php @@ -0,0 +1,49 @@ +installConfig(['user']); + $this->executeMigration('d7_user_mail'); + } + + /** + * Tests the migration. + */ + public function testMigration() { + $config = $this->config('user.mail'); + $this->assertIdentical('Your account is approved!', $config->get('status_activated.subject')); + $this->assertIdentical('Your account was activated, and there was much rejoicing.', $config->get('status_activated.body')); + $this->assertIdentical('Fix your password', $config->get('password_reset.subject')); + $this->assertIdentical("Nope! You're locked out forever.", $config->get('password_reset.body')); + $this->assertIdentical('So long, bub', $config->get('cancel_confirm.subject')); + $this->assertIdentical('The gates of Drupal are closed to you. Now you will work in the salt mines.', $config->get('cancel_confirm.body')); + $this->assertIdentical('Gawd made you an account', $config->get('register_admin_created.subject')); + $this->assertIdentical('...and she could take it away.', $config->get('register_admin_created.body')); + $this->assertIdentical('Welcome!', $config->get('register_no_approval_required.subject')); + $this->assertIdentical('You can now log in if you can figure out how to use Drupal!', $config->get('register_no_approval_required.body')); + $this->assertIdentical('Soon...', $config->get('register_pending_approval.subject')); + $this->assertIdentical('...you will join our Circle. Let the Drupal flow through you.', $config->get('register_pending_approval.body')); + $this->assertIdentical('BEGONE!', $config->get('status_blocked.subject')); + $this->assertIdentical('You no longer please the robot overlords. Go to your room and chill out.', $config->get('status_blocked.body')); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserRoleTest.php b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserRoleTest.php new file mode 100644 index 0000000..46e7834 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserRoleTest.php @@ -0,0 +1,66 @@ +executeMigration('d7_user_role'); + } + + /** + * Asserts aspects of a user role config entity. + * + * @param string $id + * The role ID. + * @param string $label + * The role's expected label. + * @param int|NULL $original_rid + * The original (integer) ID of the role, to check permissions. + */ + protected function assertEntity($id, $label, $original_rid) { + /** @var \Drupal\user\RoleInterface $entity */ + $entity = Role::load($id); + $this->assertTrue($entity instanceof RoleInterface); + $this->assertIdentical($label, $entity->label()); + + if (isset($original_rid)) { + $permissions = Database::getConnection('default', 'migrate') + ->select('role_permission', 'rp') + ->fields('rp', ['permission']) + ->condition('rid', $original_rid) + ->execute() + ->fetchCol(); + $this->assertIdentical($permissions, $entity->getPermissions()); + } + } + + /** + * Tests user role migration. + */ + public function testUserRole() { + $this->assertEntity('anonymous', 'anonymous user', 1); + $this->assertEntity('authenticated', 'authenticated user', 2); + $this->assertEntity('administrator', 'administrator', 3); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php new file mode 100644 index 0000000..5ba23eb --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/d7/MigrateUserTest.php @@ -0,0 +1,94 @@ +installEntitySchema('file'); + $this->executeMigrations([ + 'user_picture_field', + 'user_picture_field_instance', + 'd7_user_role', + 'd7_user', + ]); + } + + /** + * Asserts various aspects of a user account. + * + * @param string $id + * The user ID. + * @param string $label + * The username. + * @param string $mail + * The user's email address. + * @param int $access + * The last access time. + * @param int $login + * The last login time. + * @param bool $blocked + * Whether or not the account is blocked. + * @param string $langcode + * The user account's language code. + * @param string $init + * The user's initial email address. + * @param string[] $roles + * Role IDs the user account is expected to have. + * @param bool $has_picture + * Whether the user is expected to have a picture attached. + */ + protected function assertEntity($id, $label, $mail, $access, $login, $blocked, $langcode, $init, array $roles = [RoleInterface::AUTHENTICATED_ID], $has_picture = FALSE) { + /** @var \Drupal\user\UserInterface $user */ + $user = User::load($id); + $this->assertTrue($user instanceof UserInterface); + $this->assertIdentical($label, $user->label()); + $this->assertIdentical($mail, $user->getEmail()); + $this->assertIdentical($access, $user->getLastAccessedTime()); + $this->assertIdentical($login, $user->getLastLoginTime()); + $this->assertIdentical($blocked, $user->isBlocked()); + // $user->getPreferredLangcode() might fallback to default language if the + // user preferred language is not configured on the site. We just want to + // test if the value was imported correctly. + $this->assertIdentical($langcode, $user->langcode->value); + $this->assertIdentical($langcode, $user->preferred_langcode->value); + $this->assertIdentical($langcode, $user->preferred_admin_langcode->value); + $this->assertIdentical($init, $user->getInitialEmail()); + $this->assertIdentical($roles, $user->getRoles()); + $this->assertIdentical($has_picture, !$user->user_picture->isEmpty()); + } + + /** + * Tests the Drupal 7 user to Drupal 8 migration. + */ + public function testUser() { + $this->assertEntity(2, 'Odo', 'odo@local.host', '0', '0', FALSE, '', 'odo@local.host'); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Migrate/d7/UserMigrationClassTest.php b/core/modules/user/tests/src/Kernel/Migrate/d7/UserMigrationClassTest.php new file mode 100644 index 0000000..edd58a1 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Migrate/d7/UserMigrationClassTest.php @@ -0,0 +1,33 @@ +getMigration('d7_user'); + /** @var \Drupal\migrate\Plugin\MigrationInterface[] $migrations */ + $this->assertIdentical('d7_user', $migration->id()); + $process = $migration->getProcess(); + $this->assertIdentical('field_file', $process['field_file'][0]['source']); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Views/HandlerFieldPermissionTest.php b/core/modules/user/tests/src/Kernel/Views/HandlerFieldPermissionTest.php new file mode 100644 index 0000000..77a9d24 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Views/HandlerFieldPermissionTest.php @@ -0,0 +1,59 @@ +setupPermissionTestData(); + + $view = Views::getView('test_field_permission'); + $this->executeView($view); + $view->initStyle(); + $view->render(); + $style_plugin = $view->style_plugin; + + $expected_permissions = array(); + $expected_permissions[$this->users[0]->id()] = array(); + $expected_permissions[$this->users[1]->id()] = array(); + $expected_permissions[$this->users[2]->id()][] = t('Administer permissions'); + // View user profiles comes first, because we sort by the permission + // machine name. + $expected_permissions[$this->users[3]->id()][] = t('Administer permissions'); + $expected_permissions[$this->users[3]->id()][] = t('Administer users'); + $expected_permissions[$this->users[3]->id()][] = t('View user information'); + + foreach ($view->result as $index => $row) { + $uid = $view->field['uid']->getValue($row); + $rendered_permission = $style_plugin->getField($index, 'permission'); + + $expected_output = implode(', ', $expected_permissions[$uid]); + $this->assertEqual($rendered_permission, $expected_output, 'The right permissions are rendered.'); + } + } + +} diff --git a/core/modules/user/tests/src/Kernel/Views/HandlerFilterPermissionTest.php b/core/modules/user/tests/src/Kernel/Views/HandlerFilterPermissionTest.php new file mode 100644 index 0000000..294d000 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Views/HandlerFilterPermissionTest.php @@ -0,0 +1,120 @@ +setupPermissionTestData(); + + $column_map = array('uid' => 'uid'); + $view = Views::getView('test_filter_permission'); + + // Filter by a non existing permission. + $view->initHandlers(); + $view->filter['permission']->value = array('non_existent_permission'); + $this->executeView($view); + $this->assertEqual(count($view->result), 4, 'A non existent permission is not filtered so everything is the result.'); + $expected[] = array('uid' => 1); + $expected[] = array('uid' => 2); + $expected[] = array('uid' => 3); + $expected[] = array('uid' => 4); + $this->assertIdenticalResultset($view, $expected, $column_map); + $view->destroy(); + + // Filter by a permission. + $view->initHandlers(); + $view->filter['permission']->value = array('administer permissions'); + $this->executeView($view); + $this->assertEqual(count($view->result), 2); + $expected = array(); + $expected[] = array('uid' => 3); + $expected[] = array('uid' => 4); + $this->assertIdenticalResultset($view, $expected, $column_map); + $view->destroy(); + + // Filter by not a permission. + $view->initHandlers(); + $view->filter['permission']->operator = 'not'; + $view->filter['permission']->value = array('administer users'); + $this->executeView($view); + $this->assertEqual(count($view->result), 3); + $expected = array(); + $expected[] = array('uid' => 1); + $expected[] = array('uid' => 2); + $expected[] = array('uid' => 3); + $this->assertIdenticalResultset($view, $expected, $column_map); + $view->destroy(); + + // Filter by not multiple permissions, that are present in multiple roles. + $view->initHandlers(); + $view->filter['permission']->operator = 'not'; + $view->filter['permission']->value = array('administer users', 'administer permissions'); + $this->executeView($view); + $this->assertEqual(count($view->result), 2); + $expected = array(); + $expected[] = array('uid' => 1); + $expected[] = array('uid' => 2); + $this->assertIdenticalResultset($view, $expected, $column_map); + $view->destroy(); + + // Filter by another permission of a role with multiple permissions. + $view->initHandlers(); + $view->filter['permission']->value = array('administer users'); + $this->executeView($view); + $this->assertEqual(count($view->result), 1); + $expected = array(); + $expected[] = array('uid' => 4); + $this->assertIdenticalResultset($view, $expected, $column_map); + $view->destroy(); + + $view->initDisplay(); + $view->initHandlers(); + + // Test the value options. + $value_options = $view->filter['permission']->getValueOptions(); + + $permission_by_module = []; + $permissions = \Drupal::service('user.permissions')->getPermissions(); + foreach ($permissions as $name => $permission) { + $permission_by_module[$permission['provider']][$name] = $permission; + } + foreach (array('system' => 'System', 'user' => 'User') as $module => $title) { + $expected = array_map(function ($permission) { + return Html::escape(strip_tags($permission['title'])); + }, $permission_by_module[$module]); + + $this->assertEqual($expected, $value_options[$title], 'Ensure the all permissions are available'); + } + } + +} diff --git a/core/modules/user/tests/src/Kernel/Views/HandlerFilterRolesTest.php b/core/modules/user/tests/src/Kernel/Views/HandlerFilterRolesTest.php new file mode 100644 index 0000000..6001f47 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Views/HandlerFilterRolesTest.php @@ -0,0 +1,76 @@ + 'test_user_role']); + $role->save(); + $view = View::load('test_user_name'); + $expected = [ + 'module' => ['user'], + ]; + $this->assertEqual($expected, $view->getDependencies()); + + $display = &$view->getDisplay('default'); + $display['display_options']['filters']['roles_target_id'] = [ + 'id' => 'roles_target_id', + 'table' => 'user__roles', + 'field' => 'roles_target_id', + 'value' => [ + 'test_user_role' => 'test_user_role', + ], + 'plugin_id' => 'user_roles', + ]; + $view->save(); + $expected['config'][] = 'user.role.test_user_role'; + $this->assertEqual($expected, $view->getDependencies()); + + $view = Views::getView('test_user_name'); + $view->initDisplay(); + $view->initHandlers(); + $this->assertEqual(array_keys($view->filter['roles_target_id']->getValueOptions()), ['test_user_role']); + + $view = View::load('test_user_name'); + $display = &$view->getDisplay('default'); + $display['display_options']['filters']['roles_target_id'] = [ + 'id' => 'roles_target_id', + 'table' => 'user__roles', + 'field' => 'roles_target_id', + 'value' => [], + 'plugin_id' => 'user_roles', + ]; + $view->save(); + unset($expected['config']); + $this->assertEqual($expected, $view->getDependencies()); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Views/UserKernelTestBase.php b/core/modules/user/tests/src/Kernel/Views/UserKernelTestBase.php new file mode 100644 index 0000000..6676007 --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Views/UserKernelTestBase.php @@ -0,0 +1,95 @@ +installEntitySchema('user'); + + $entity_manager = $this->container->get('entity.manager'); + $this->roleStorage = $entity_manager->getStorage('user_role'); + $this->userStorage = $entity_manager->getStorage('user'); + } + + /** + * Set some test data for permission related tests. + */ + protected function setupPermissionTestData() { + // Setup a role without any permission. + $this->roleStorage->create(array('id' => 'authenticated')) + ->save(); + $this->roleStorage->create(array('id' => 'no_permission')) + ->save(); + // Setup a role with just one permission. + $this->roleStorage->create(array('id' => 'one_permission')) + ->save(); + user_role_grant_permissions('one_permission', array('administer permissions')); + // Setup a role with multiple permissions. + $this->roleStorage->create(array('id' => 'multiple_permissions')) + ->save(); + user_role_grant_permissions('multiple_permissions', array('administer permissions', 'administer users', 'access user profiles')); + + // Setup a user without an extra role. + $this->users[] = $account = $this->userStorage->create(['name' => $this->randomString()]); + $account->save(); + // Setup a user with just the first role (so no permission beside the + // ones from the authenticated role). + $this->users[] = $account = $this->userStorage->create(array('name' => 'first_role')); + $account->addRole('no_permission'); + $account->save(); + // Setup a user with just the second role (so one additional permission). + $this->users[] = $account = $this->userStorage->create(array('name' => 'second_role')); + $account->addRole('one_permission'); + $account->save(); + // Setup a user with both the second and the third role. + $this->users[] = $account = $this->userStorage->create(array('name' => 'second_third_role')); + $account->addRole('one_permission'); + $account->addRole('multiple_permissions'); + $account->save(); + } + +} diff --git a/core/modules/user/tests/src/Kernel/Views/UserViewsFieldAccessTest.php b/core/modules/user/tests/src/Kernel/Views/UserViewsFieldAccessTest.php new file mode 100644 index 0000000..7783d7c --- /dev/null +++ b/core/modules/user/tests/src/Kernel/Views/UserViewsFieldAccessTest.php @@ -0,0 +1,72 @@ +installEntitySchema('user'); + } + + public function testUserFields() { + ConfigurableLanguage::create([ + 'id' => 'es', + 'name' => 'Spanish', + ])->save(); + ConfigurableLanguage::create([ + 'id' => 'fr', + 'name' => 'French', + ])->save(); + + $user = User::create([ + 'name' => 'test user', + 'mail' => 'druplicon@drop.org', + 'status' => 1, + 'preferred_langcode' => 'es', + 'preferred_admin_langcode' => 'fr', + 'timezone' => 'ut1', + 'created' => 123456, + ]); + + $user->save(); + + // @todo Expand the test coverage in https://www.drupal.org/node/2464635 + + $this->assertFieldAccess('user', 'uid', $user->id()); + $this->assertFieldAccess('user', 'uuid', $user->uuid()); + $this->assertFieldAccess('user', 'langcode', $user->language()->getName()); + $this->assertFieldAccess('user', 'preferred_langcode', 'Spanish'); + $this->assertFieldAccess('user', 'preferred_admin_langcode', 'French'); + $this->assertFieldAccess('user', 'name', 'test user'); + // $this->assertFieldAccess('user', 'mail', 'druplicon@drop.org'); + $this->assertFieldAccess('user', 'timezone', 'ut1'); + $this->assertFieldAccess('user', 'status', 'On'); + // $this->assertFieldAccess('user', 'created', \Drupal::service('date.formatter')->format(123456)); + // $this->assertFieldAccess('user', 'changed', \Drupal::service('date.formatter')->format(REQUEST_TIME)); + } + +} diff --git a/core/modules/user/tests/src/Unit/PermissionHandlerTest.php b/core/modules/user/tests/src/Unit/PermissionHandlerTest.php index e252b49..6a2b26f 100644 --- a/core/modules/user/tests/src/Unit/PermissionHandlerTest.php +++ b/core/modules/user/tests/src/Unit/PermissionHandlerTest.php @@ -113,6 +113,9 @@ public function testBuildPermissionsYaml() { "'access module b': title: 'Access B' description: 'bla bla' +'access module a via module b': + title: 'Access A via B' + provider: 'module_a' "); mkdir($url . '/module_c'); file_put_contents($url . '/module_c/module_c.permissions.yml', @@ -239,6 +242,7 @@ public function testBuildPermissionsYamlCallback() { file_put_contents($url . '/module_b/module_b.permissions.yml', "permission_callbacks: - 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescription' + - 'Drupal\\user\\Tests\\TestPermissionCallbacks::titleProvider' "); mkdir($url . '/module_c'); file_put_contents($url . '/module_c/module_c.permissions.yml', @@ -272,6 +276,10 @@ public function testBuildPermissionsYamlCallback() { ->willReturn(array(new TestPermissionCallbacks(), 'titleDescription')); $this->controllerResolver->expects($this->at(2)) ->method('getControllerFromDefinition') + ->with('Drupal\\user\\Tests\\TestPermissionCallbacks::titleProvider') + ->willReturn(array(new TestPermissionCallbacks(), 'titleProvider')); + $this->controllerResolver->expects($this->at(3)) + ->method('getControllerFromDefinition') ->with('Drupal\\user\\Tests\\TestPermissionCallbacks::titleDescriptionRestrictAccess') ->willReturn(array(new TestPermissionCallbacks(), 'titleDescriptionRestrictAccess')); @@ -351,7 +359,7 @@ public function testPermissionsYamlStaticAndCallback() { * The actual permissions */ protected function assertPermissions(array $actual_permissions) { - $this->assertCount(3, $actual_permissions); + $this->assertCount(4, $actual_permissions); $this->assertEquals($actual_permissions['access_module_a']['title'], 'single_description'); $this->assertEquals($actual_permissions['access_module_a']['provider'], 'module_a'); $this->assertEquals($actual_permissions['access module b']['title'], 'Access B'); @@ -359,6 +367,7 @@ protected function assertPermissions(array $actual_permissions) { $this->assertEquals($actual_permissions['access_module_c']['title'], 'Access C'); $this->assertEquals($actual_permissions['access_module_c']['provider'], 'module_c'); $this->assertEquals($actual_permissions['access_module_c']['restrict access'], TRUE); + $this->assertEquals($actual_permissions['access module a via module b']['provider'], 'module_a'); } } @@ -409,6 +418,14 @@ public function titleDescriptionRestrictAccess() { ); } + public function titleProvider() { + return array( + 'access module a via module b' => array( + 'title' => 'Access A via B', + 'provider' => 'module_a', + ), + ); + } } /** diff --git a/core/modules/user/user.install b/core/modules/user/user.install index 91a908e..7cc46ef 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -85,3 +85,25 @@ function user_install() { )) ->save(); } + +/** + * @addtogroup updates-8.1.0-beta + * @{ + */ + +/** + * Fix invalid token in the status_blocked email body. + */ +function user_update_8100() { + $config_factory = \Drupal::configFactory(); + $config = $config_factory->getEditable('user.mail'); + $mail = $config->get('status_blocked'); + if (strpos($mail['body'], '[site:account-name]') !== FALSE) { + $mail['body'] = str_replace('[site:account-name]', '[site:name]', $mail['body']); + $config->set('status_blocked', $mail)->save(TRUE); + } +} + +/** + * @} End of "addtogroup updates-8.1.0-beta". + */ diff --git a/core/modules/user/user.module b/core/modules/user/user.module index 3d76074..0e9cedb 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -1,5 +1,10 @@ id(); if (!entity_load('action', $add_id)) { - $action = entity_create('action', array( + $action = Action::create(array( 'id' => $add_id, 'type' => 'user', 'label' => t('Add the @label role to the selected users', array('@label' => $role->label())), @@ -998,7 +999,7 @@ function user_user_role_insert(RoleInterface $role) { } $remove_id = 'user_remove_role_action.' . $role->id(); if (!entity_load('action', $remove_id)) { - $action = entity_create('action', array( + $action = Action::create(array( 'id' => $remove_id, 'type' => 'user', 'label' => t('Remove the @label role from the selected users', array('@label' => $role->label())), diff --git a/core/modules/user/user.permissions.js b/core/modules/user/user.permissions.js index ec90b38..c3dc24f 100644 --- a/core/modules/user/user.permissions.js +++ b/core/modules/user/user.permissions.js @@ -3,7 +3,7 @@ * User permission page behaviors. */ -(function ($) { +(function ($, Drupal) { 'use strict'; @@ -85,4 +85,4 @@ } }; -})(jQuery); +})(jQuery, Drupal); diff --git a/core/modules/views/config/schema/views.field.schema.yml b/core/modules/views/config/schema/views.field.schema.yml index 58d4c0d..562e8ca 100644 --- a/core/modules/views/config/schema/views.field.schema.yml +++ b/core/modules/views/config/schema/views.field.schema.yml @@ -176,6 +176,13 @@ views.field.language: type: boolean label: 'Display in native language' +views.field.rendered_entity: + type: views_field + label: 'Rendered entity' + mapping: + view_mode: + type: string + label: 'View mode' views.field.entity_link: type: views_field diff --git a/core/modules/views/js/ajax_view.js b/core/modules/views/js/ajax_view.js index 57bbb45..ad61000 100644 --- a/core/modules/views/js/ajax_view.js +++ b/core/modules/views/js/ajax_view.js @@ -85,25 +85,25 @@ // Add the ajax to exposed forms. this.$exposed_form = $('form#views-exposed-form-' + settings.view_name.replace(/_/g, '-') + '-' + settings.view_display_id.replace(/_/g, '-')); - this.$exposed_form.once('exposed-form').each(jQuery.proxy(this.attachExposedFormAjax, this)); + this.$exposed_form.once('exposed-form').each($.proxy(this.attachExposedFormAjax, this)); // Add the ajax to pagers. this.$view // Don't attach to nested views. Doing so would attach multiple behaviors // to a given element. - .filter(jQuery.proxy(this.filterNestedViews, this)) - .once('ajax-pager').each(jQuery.proxy(this.attachPagerAjax, this)); + .filter($.proxy(this.filterNestedViews, this)) + .once('ajax-pager').each($.proxy(this.attachPagerAjax, this)); // Add a trigger to update this view specifically. In order to trigger a // refresh use the following code. // // @code - // jQuery('.view-name').trigger('RefreshView'); + // $('.view-name').trigger('RefreshView'); // @endcode var self_settings = $.extend({}, this.element_settings, { event: 'RefreshView', base: this.selector, - element: this.$view + element: this.$view.get(0) }); this.refreshViewAjax = Drupal.ajax(self_settings); }; @@ -140,7 +140,7 @@ */ Drupal.views.ajaxView.prototype.attachPagerAjax = function () { this.$view.find('ul.js-pager__items > li > a, th.views-field a, .attachment .views-summary a') - .each(jQuery.proxy(this.attachPagerLinkAjax, this)); + .each($.proxy(this.attachPagerLinkAjax, this)); }; /** @@ -168,7 +168,7 @@ var self_settings = $.extend({}, this.element_settings, { submit: viewData, base: false, - element: $link + element: link }); this.pagerAjax = Drupal.ajax(self_settings); }; diff --git a/core/modules/views/src/Annotation/ViewsPluginAnnotationBase.php b/core/modules/views/src/Annotation/ViewsPluginAnnotationBase.php index 869cc69..5c949a5 100644 --- a/core/modules/views/src/Annotation/ViewsPluginAnnotationBase.php +++ b/core/modules/views/src/Annotation/ViewsPluginAnnotationBase.php @@ -16,15 +16,6 @@ abstract class ViewsPluginAnnotationBase extends Plugin implements AnnotationInterface { /** - * A class to make the plugin derivative aware. - * - * @var string - * - * @see \Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator - */ - public $derivative; - - /** * Whether or not to register a theme function automatically. * * @var bool (optional) diff --git a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php index f2a3778..e26accf 100644 --- a/core/modules/views/src/Entity/Render/EntityFieldRenderer.php +++ b/core/modules/views/src/Entity/Render/EntityFieldRenderer.php @@ -7,6 +7,7 @@ namespace Drupal\views\Entity\Render; +use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\Entity\Entity\EntityViewDisplay; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; @@ -25,6 +26,7 @@ */ class EntityFieldRenderer extends RendererBase { use EntityTranslationRenderTrait; + use DependencySerializationTrait; /** * The relationship being handled. diff --git a/core/modules/views/src/EntityViewsData.php b/core/modules/views/src/EntityViewsData.php index 00c7039..3b57d53 100644 --- a/core/modules/views/src/EntityViewsData.php +++ b/core/modules/views/src/EntityViewsData.php @@ -182,6 +182,16 @@ public function getViewsData() { ); } + if ($this->entityType->hasViewBuilderClass()) { + $data[$base_table]['rendered_entity'] = [ + 'field' => [ + 'title' => $this->t('Rendered entity'), + 'help' => $this->t('Renders an entity in a view mode.'), + 'id' => 'rendered_entity', + ], + ]; + } + // Setup relations to the revisions/property data. if ($data_table) { $data[$base_table]['table']['join'][$data_table] = [ diff --git a/core/modules/views/src/Form/ViewsForm.php b/core/modules/views/src/Form/ViewsForm.php index 39ff499..fe27c8c 100644 --- a/core/modules/views/src/Form/ViewsForm.php +++ b/core/modules/views/src/Form/ViewsForm.php @@ -66,6 +66,13 @@ class ViewsForm implements FormInterface, ContainerInjectionInterface { protected $viewDisplayId; /** + * The arguments passed to the active view. + * + * @var string[] + */ + protected $viewArguments; + + /** * Constructs a ViewsForm object. * * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver @@ -78,37 +85,60 @@ class ViewsForm implements FormInterface, ContainerInjectionInterface { * The ID of the view. * @param string $view_display_id * The ID of the active view's display. + * @param string[] $view_args + * The arguments passed to the active view. */ - public function __construct(ClassResolverInterface $class_resolver, UrlGeneratorInterface $url_generator, RequestStack $requestStack, $view_id, $view_display_id) { + public function __construct(ClassResolverInterface $class_resolver, UrlGeneratorInterface $url_generator, RequestStack $requestStack, $view_id, $view_display_id, array $view_args) { $this->classResolver = $class_resolver; $this->urlGenerator = $url_generator; $this->requestStack = $requestStack; $this->viewId = $view_id; $this->viewDisplayId = $view_display_id; + $this->viewArguments = $view_args; } /** * {@inheritdoc} */ - public static function create(ContainerInterface $container, $view_id = NULL, $view_display_id = NULL) { + public static function create(ContainerInterface $container, $view_id = NULL, $view_display_id = NULL, array $view_args = NULL) { return new static( $container->get('class_resolver'), $container->get('url_generator'), $container->get('request_stack'), $view_id, - $view_display_id + $view_display_id, + $view_args ); } /** - * {@inheritdoc} + * Returns a string for the form's base ID. + * + * @return string + * The string identifying the form's base ID. */ - public function getFormId() { - $parts = array( + public function getBaseFormId() { + $parts = [ 'views_form', $this->viewId, $this->viewDisplayId, - ); + ]; + + return implode('_', $parts); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + $parts = [ + $this->getBaseFormId(), + ]; + + if (!empty($this->viewArguments)) { + // Append the passed arguments to ensure form uniqueness. + $parts = array_merge($parts, $this->viewArguments); + } return implode('_', $parts); } @@ -123,6 +153,9 @@ public function buildForm(array $form, FormStateInterface $form_state, ViewExecu } $form_state->set(['step_controller', 'views_form_views_form'], 'Drupal\views\Form\ViewsFormMainForm'); + // Add the base form ID. + $form_state->addBuildInfo('base_form_id', $this->getBaseFormId()); + $form = array(); $query = $this->requestStack->getCurrentRequest()->query->all(); diff --git a/core/modules/views/src/Plugin/views/HandlerBase.php b/core/modules/views/src/Plugin/views/HandlerBase.php index b8fa6d2..af1db69 100644 --- a/core/modules/views/src/Plugin/views/HandlerBase.php +++ b/core/modules/views/src/Plugin/views/HandlerBase.php @@ -50,13 +50,6 @@ public $tableAlias; /** - * When a table has been moved this property is set. - * - * @var string - */ - public $actualTable; - - /** * The actual field in the database table, maybe different * on other kind of query plugins/special handlers. * @@ -72,13 +65,6 @@ public $field; /** - * When a field has been moved this property is set. - * - * @var string - */ - public $actualField; - - /** * The relationship used for this field. * * @var string @@ -124,15 +110,6 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o // we have to do a lookup because the type is singular but the // option is stored as the plural. - // If the 'moved to' keyword moved our handler, let's fix that now. - if (isset($this->actualTable)) { - $options['table'] = $this->actualTable; - } - - if (isset($this->actualField)) { - $options['field'] = $this->actualField; - } - $this->unpackOptions($this->options, $options); // This exist on most handlers, but not all. So they are still optional. diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php index 462d53f..3821230 100644 --- a/core/modules/views/src/Plugin/views/PluginBase.php +++ b/core/modules/views/src/Plugin/views/PluginBase.php @@ -369,7 +369,7 @@ protected function viewsTokenReplace($text, $tokens) { if (strpos($token, '.') === FALSE) { // We need to validate tokens are valid Twig variables. Twig uses the // same variable naming rules as PHP. - // @see http://php.net/manual/en/language.variables.basics.php + // @see http://php.net/manual/language.variables.basics.php assert('preg_match(\'/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/\', $token) === 1', 'Tokens need to be valid Twig variables.'); $twig_tokens[$token] = $replacement; } diff --git a/core/modules/views/src/Plugin/views/area/Result.php b/core/modules/views/src/Plugin/views/area/Result.php index 3079f0d..52b4e33 100644 --- a/core/modules/views/src/Plugin/views/area/Result.php +++ b/core/modules/views/src/Plugin/views/area/Result.php @@ -104,11 +104,15 @@ public function render($empty = FALSE) { } $current_record_count = ($end - $start) + 1; // Get the search information. - $items = array('start', 'end', 'total', 'label', 'per_page', 'current_page', 'current_record_count', 'page_count'); - $replacements = array(); - foreach ($items as $item) { - $replacements["@$item"] = ${$item}; - } + $replacements = []; + $replacements['@start'] = $start; + $replacements['@end'] = $end; + $replacements['@total'] = $total; + $replacements['@label'] = $label; + $replacements['@per_page'] = $per_page; + $replacements['@current_page'] = $current_page; + $replacements['@current_record_count'] = $current_record_count; + $replacements['@page_count'] = $page_count; // Send the output. if (!empty($total)) { $output .= Xss::filterAdmin(str_replace(array_keys($replacements), array_values($replacements), $format)); diff --git a/core/modules/views/src/Plugin/views/argument/StringArgument.php b/core/modules/views/src/Plugin/views/argument/StringArgument.php index 3302a52..3987a37 100644 --- a/core/modules/views/src/Plugin/views/argument/StringArgument.php +++ b/core/modules/views/src/Plugin/views/argument/StringArgument.php @@ -99,7 +99,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { $form['path_case'] = array( '#type' => 'select', '#title' => $this->t('Case in path'), - '#description' => $this->t('When printing url paths, how to transform the case of the filter value. Do not use this unless with Postgres as it uses case sensitive comparisons.'), + '#description' => $this->t('When printing URL paths, how to transform the case of the filter value. Do not use this unless with Postgres as it uses case sensitive comparisons.'), '#options' => array( 'none' => $this->t('No transform'), 'upper' => $this->t('Upper case'), diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php index e49b553..2348408 100644 --- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php +++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php @@ -951,7 +951,7 @@ protected function getAllPlugins($only_overrides = FALSE) { * {@inheritdoc} */ public function calculateDependencies() { - $this->addDependencies(parent::calculateDependencies()); + $this->dependencies = parent::calculateDependencies(); // Collect all the dependencies of handlers and plugins. Only calculate // their dependencies if they are configured by this display. $plugins = array_merge($this->getAllHandlers(TRUE), $this->getAllPlugins(TRUE)); @@ -1331,7 +1331,7 @@ public function optionsSummary(&$categories, &$options) { 'category' => 'pager', 'title' => $this->t('Link display'), 'value' => $link_display, - 'desc' => $this->t('Specify which display or custom url this display will link to.'), + 'desc' => $this->t('Specify which display or custom URL this display will link to.'), ); } @@ -1485,7 +1485,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { $form['use_more'] = array( '#type' => 'checkbox', '#title' => $this->t('Create more link'), - '#description' => $this->t("This will add a more link to the bottom of this view, which will link to the page view. If you have more than one page view, the link will point to the display specified in 'Link display' section under pager. You can override the url at the link display setting."), + '#description' => $this->t("This will add a more link to the bottom of this view, which will link to the page view. If you have more than one page view, the link will point to the display specified in 'Link display' section under pager. You can override the URL at the link display setting."), '#default_value' => $this->getOption('use_more'), ); $form['use_more_always'] = array( @@ -2195,7 +2195,7 @@ public function elementPreRender(array $element) { $output = $element['#empty']; } - $form_object = ViewsForm::create(\Drupal::getContainer(), $view->storage->id(), $view->current_display); + $form_object = ViewsForm::create(\Drupal::getContainer(), $view->storage->id(), $view->current_display, $view->args); $form = \Drupal::formBuilder()->getForm($form_object, $view, $output); // The form is requesting that all non-essential views elements be hidden, // usually because the rendered step is not a view result. diff --git a/core/modules/views/src/Plugin/views/field/Field.php b/core/modules/views/src/Plugin/views/field/Field.php index af3dad1..91f5f6b 100644 --- a/core/modules/views/src/Plugin/views/field/Field.php +++ b/core/modules/views/src/Plugin/views/field/Field.php @@ -7,6 +7,7 @@ namespace Drupal\views\Plugin\views\field; +use Drupal\Component\Plugin\DependentPluginInterface; use Drupal\Component\Utility\Xss; use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheableDependencyInterface; @@ -18,6 +19,7 @@ use Drupal\Core\Form\FormHelper; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageManagerInterface; +use Drupal\Core\Plugin\PluginDependencyTrait; use Drupal\Core\Render\BubbleableMetadata; use Drupal\Core\Render\Element; use Drupal\Core\Render\RendererInterface; @@ -40,7 +42,9 @@ * @ViewsField("field") */ class Field extends FieldPluginBase implements CacheableDependencyInterface, MultiItemsFieldHandlerInterface { + use FieldAPIHandlerTrait; + use PluginDependencyTrait; /** * An array to store field renderable arrays for use by renderItems(). @@ -471,25 +475,9 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { $form['field_api_classes']['#description'] .= ' ' . $this->t('Checking this option will cause the group Display Type and Separator values to be ignored.'); } - // Get the currently selected formatter. - $format = $this->options['type']; - - $settings = $this->options['settings'] + $this->formatterPluginManager->getDefaultSettings($format); - - $options = array( - 'field_definition' => $field, - 'configuration' => array( - 'type' => $format, - 'settings' => $settings, - 'label' => '', - 'weight' => 0, - ), - 'view_mode' => '_custom', - ); - // Get the settings form. $settings_form = array('#value' => array()); - if ($formatter = $this->formatterPluginManager->getInstance($options)) { + if ($formatter = $this->getFormatterInstance()) { $settings_form = $formatter->settingsForm($form, $form_state); // Convert field UI selector states to work in the Views field form. FormHelper::rewriteStatesSelector($settings_form, "fields[{$field->getName()}][settings_edit_form]", 'options'); @@ -815,7 +803,7 @@ public function getItems(ResultRow $values) { // For grouped results we need to retrieve a massaged entity having // grouped field values to ensure that "grouped by" values, especially // those with multiple cardinality work properly. See - // \Drupal\views\Tests\QueryGroupByTest::testGroupByFieldWithCardinality. + // \Drupal\Tests\views\Kernel\QueryGroupByTest::testGroupByFieldWithCardinality. $display = [ 'type' => $this->options['type'], 'settings' => $this->options['settings'], @@ -949,21 +937,49 @@ protected function addSelfTokens(&$tokens, $item) { } /** + * Returns the field formatter instance. + * + * @return \Drupal\Core\Field\FormatterInterface|null + * The field formatter instance. + */ + protected function getFormatterInstance() { + $settings = $this->options['settings'] + $this->formatterPluginManager->getDefaultSettings($this->options['type']); + + $options = [ + 'field_definition' => $this->getFieldDefinition(), + 'configuration' => [ + 'type' => $this->options['type'], + 'settings' => $settings, + 'label' => '', + 'weight' => 0, + ], + 'view_mode' => '_custom', + ]; + + return $this->formatterPluginManager->getInstance($options); + } + + /** * {@inheritdoc} */ public function calculateDependencies() { - $dependencies = parent::calculateDependencies(); + $this->dependencies = parent::calculateDependencies(); // Add the module providing the configured field storage as a dependency. if (($field_storage_definition = $this->getFieldStorageDefinition()) && $field_storage_definition instanceof EntityInterface) { - $dependencies['config'][] = $field_storage_definition->getConfigDependencyName(); + $this->dependencies['config'][] = $field_storage_definition->getConfigDependencyName(); } - // Add the module providing the formatter. if (!empty($this->options['type'])) { - $dependencies['module'][] = $this->formatterPluginManager->getDefinition($this->options['type'])['provider']; + // Add the module providing the formatter. + $this->dependencies['module'][] = $this->formatterPluginManager->getDefinition($this->options['type'])['provider']; + + // Add the formatter's dependencies. + if (($formatter = $this->getFormatterInstance()) && $formatter instanceof DependentPluginInterface) { + $this->calculatePluginDependencies($formatter); + } } - return $dependencies; + return $this->dependencies; } /** diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php index 9208e10..8b54632 100644 --- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php +++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php @@ -8,13 +8,11 @@ namespace Drupal\views\Plugin\views\field; use Drupal\Component\Utility\Html; -use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Render\MarkupInterface; use Drupal\Component\Utility\Unicode; use Drupal\Component\Utility\UrlHelper; use Drupal\Component\Utility\Xss; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\Renderer; use Drupal\Core\Url as CoreUrl; use Drupal\views\Plugin\views\HandlerBase; use Drupal\views\Plugin\views\display\DisplayPluginBase; @@ -86,7 +84,7 @@ public $original_value = NULL; /** - * Stores additional fields that get added to the query. + * Stores additional fields which get added to the query. * * The generated aliases are stored in $aliases. * @@ -775,7 +773,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { $form['alter']['path_case'] = array( '#type' => 'select', '#title' => $this->t('Transform the case'), - '#description' => $this->t('When printing url paths, how to transform the case of the filter value.'), + '#description' => $this->t('When printing URL paths, how to transform the case of the filter value.'), '#states' => array( 'visible' => array( ':input[name="options[alter][make_link]"]' => array('checked' => TRUE), @@ -1221,7 +1219,7 @@ public function renderText($alter) { // alterations made by this method. Any alterations or replacements made // within this method need to ensure that at the minimum the result is // XSS admin filtered. See self::renderAltered() as an example that does. - $value_is_safe = SafeMarkup::isSafe($this->last_render); + $value_is_safe = $this->last_render instanceof MarkupInterface; // Cast to a string so that empty checks and string functions work as // expected. $value = (string) $this->last_render; @@ -1299,9 +1297,10 @@ public function renderText($alter) { } // Preserve whether or not the string is safe. Since $more_link comes from - // \Drupal::l(), it is safe to append. Use SafeMarkup::isSafe() here because - // renderAsLink() can return both safe and unsafe values. - if (SafeMarkup::isSafe($value)) { + // \Drupal::l(), it is safe to append. Check if the value is an instance of + // \Drupal\Component\Render\MarkupInterface here because renderAsLink() + // can return both safe and unsafe values. + if ($value instanceof MarkupInterface) { return ViewsRenderPipelineMarkup::create($value . $more_link); } else { @@ -1681,8 +1680,7 @@ protected function getTokenValuesRecursive(array $array, array $parent_keys = ar * fields as a list. For example, the field that displays all terms * on a node might have tokens for the tid and the term. * - * By convention, tokens should follow the format of {{ token - * subtoken }} + * By convention, tokens should follow the format of {{ token__subtoken }} * where token is the field ID and subtoken is the field. If the * field ID is terms, then the tokens might be {{ terms__tid }} and * {{ terms__name }}. diff --git a/core/modules/views/src/Plugin/views/field/RenderedEntity.php b/core/modules/views/src/Plugin/views/field/RenderedEntity.php new file mode 100644 index 0000000..21b8ae6 --- /dev/null +++ b/core/modules/views/src/Plugin/views/field/RenderedEntity.php @@ -0,0 +1,213 @@ +entityManager = $entity_manager; + $this->languageManager = $language_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity.manager'), + $container->get('language_manager') + ); + } + + /** + * {@inheritdoc} + */ + public function usesGroupBy() { + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function defineOptions() { + $options = parent::defineOptions(); + $options['view_mode'] = ['default' => 'default']; + + return $options; + } + + /** + * {@inheritdoc} + */ + public function buildOptionsForm(&$form, FormStateInterface $form_state) { + parent::buildOptionsForm($form, $form_state); + + $form['view_mode'] = [ + '#type' => 'select', + '#options' => $this->entityManager->getViewModeOptions($this->getEntityTypeId()), + '#title' => $this->t('View mode'), + '#default_value' => $this->options['view_mode'], + ]; + } + + /** + * {@inheritdoc} + */ + public function render(ResultRow $values) { + $entity = $this->getEntityTranslation($this->getEntity($values), $values); + $build = []; + if (isset($entity)) { + $access = $entity->access('view', NULL, TRUE); + $build['#access'] = $access; + if ($access->isAllowed()) { + $view_builder = $this->entityManager->getViewBuilder($this->getEntityTypeId()); + $build += $view_builder->view($entity, $this->options['view_mode']); + } + } + return $build; + } + + /** + * {@inheritdoc} + */ + public function getCacheContexts() { + return []; + } + + /** + * {@inheritdoc} + */ + public function getCacheTags() { + $view_display_storage = $this->entityManager->getStorage('entity_view_display'); + $view_displays = $view_display_storage->loadMultiple($view_display_storage + ->getQuery() + ->condition('targetEntityType', $this->getEntityTypeId()) + ->execute()); + + $tags = []; + foreach ($view_displays as $view_display) { + $tags = array_merge($tags, $view_display->getCacheTags()); + } + return $tags; + } + + /** + * {@inheritdoc} + */ + public function getCacheMaxAge() { + return Cache::PERMANENT; + } + + /** + * {@inheritdoc} + */ + public function query() { + // We purposefully do not call parent::query() because we do not want the + // default query behavior for Views fields. Instead, let the entity + // translation renderer provide the correct query behavior. + if ($this->languageManager->isMultilingual()) { + $this->getEntityTranslationRenderer()->query($this->query, $this->relationship); + } + } + + /** + * {@inheritdoc} + */ + public function getEntityTypeId() { + return $this->getEntityType(); + } + + /** + * {@inheritdoc} + */ + protected function getEntityManager() { + return $this->entityManager; + } + + /** + * {@inheritdoc} + */ + protected function getLanguageManager() { + return $this->languageManager; + } + + /** + * {@inheritdoc} + */ + protected function getView() { + return $this->view; + } + + /** + * {@inheritdoc} + */ + public function calculateDependencies() { + $dependencies = parent::calculateDependencies(); + + $view_mode = $this->entityManager + ->getStorage('entity_view_mode') + ->load($this->getEntityTypeId() . '.' . $this->options['view_mode']); + if ($view_mode) { + $dependencies[$view_mode->getConfigDependencyKey()][] = $view_mode->getConfigDependencyName(); + } + + return $dependencies; + } + +} diff --git a/core/modules/views/src/Plugin/views/filter/BooleanOperator.php b/core/modules/views/src/Plugin/views/filter/BooleanOperator.php index e37ed31..a4cded4 100644 --- a/core/modules/views/src/Plugin/views/filter/BooleanOperator.php +++ b/core/modules/views/src/Plugin/views/filter/BooleanOperator.php @@ -76,14 +76,14 @@ protected function operators() { return array( '=' => array( 'title' => $this->t('Is equal to'), - 'method' => '_queryOperatorBoolean', + 'method' => 'queryOpBoolean', 'short' => $this->t('='), 'values' => 1, 'query_operator' => static::EQUAL, ), '!=' => array( 'title' => $this->t('Is not equal to'), - 'method' => '_queryOperatorBoolean', + 'method' => 'queryOpBoolean', 'short' => $this->t('!='), 'values' => 1, 'query_operator' => static::NOT_EQUAL, @@ -226,29 +226,15 @@ public function query() { } /** - * Adds a where condition to the query for a boolean value. This function - * remains to prevent breaks in public-facing API's. - * - * @param string $field - * The field name to add the where condition for. - */ - protected function queryOpBoolean($field) { - $this->_queryOperatorBoolean($field, static::EQUAL); - } - - /** * Adds a where condition to the query for a boolean value. * * @param string $field * The field name to add the where condition for. * @param string $query_operator - * Either static::EQUAL or static::NOT_EQUAL. - * - * @internal - * This method will be removed in 8.1.0 and is here to maintain backwards- - * compatibility in 8.0.x releases. + * (optional) Either static::EQUAL or static::NOT_EQUAL. Defaults to + * static::EQUAL. */ - protected function _queryOperatorBoolean($field, $query_operator) { + protected function queryOpBoolean($field, $query_operator = EQUAL) { if (empty($this->value)) { if ($this->accept_null) { if ($query_operator == static::EQUAL) { diff --git a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php index c0cd5d0..75b5297 100644 --- a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php +++ b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php @@ -610,7 +610,7 @@ public function buildExposeForm(&$form, FormStateInterface $form_state) { '#default_value' => $this->options['expose']['identifier'], '#title' => $this->t('Filter identifier'), '#size' => 40, - '#description' => $this->t('This will appear in the URL after the ? to identify this filter. Cannot be blank.'), + '#description' => $this->t('This will appear in the URL after the ? to identify this filter. Cannot be blank. Only letters, digits and the dot ("."), hyphen ("-"), underscore ("_"), and tilde ("~") characters are allowed.'), ); } @@ -619,16 +619,7 @@ public function buildExposeForm(&$form, FormStateInterface $form_state) { */ public function validateExposeForm($form, FormStateInterface $form_state) { $identifier = $form_state->getValue(array('options', 'expose', 'identifier')); - if (empty($identifier)) { - $form_state->setError($form['expose']['identifier'], $this->t('The identifier is required if the filter is exposed.')); - } - elseif ($identifier == 'value') { - $form_state->setError($form['expose']['identifier'], $this->t('This identifier is not allowed.')); - } - - if (!$this->view->display_handler->isIdentifierUnique($form_state->get('id'), $identifier)) { - $form_state->setError($form['expose']['identifier'], $this->t('This identifier is used by another handler.')); - } + $this->validateIdentifier($identifier, $form_state, $form['expose']['identifier']); } /** @@ -637,17 +628,7 @@ public function validateExposeForm($form, FormStateInterface $form_state) { protected function buildGroupValidate($form, FormStateInterface $form_state) { if (!$form_state->isValueEmpty(array('options', 'group_info'))) { $identifier = $form_state->getValue(array('options', 'group_info', 'identifier')); - if (empty($identifier)) { - $form_state->setError($form['group_info']['identifier'], $this->t('The identifier is required if the filter is exposed.')); - } - - elseif ($identifier == 'value') { - $form_state->setError($form['group_info']['identifier'], $this->t('This identifier is not allowed.')); - } - - if (!$this->view->display_handler->isIdentifierUnique($form_state->get('id'), $identifier)) { - $form_state->setError($form['group_info']['identifier'], $this->t('This identifier is used by another handler.')); - } + $this->validateIdentifier($identifier, $form_state, $form['group_info']['identifier']); } if ($group_items = $form_state->getValue(array('options', 'group_info', 'group_items'))) { @@ -677,6 +658,42 @@ protected function buildGroupValidate($form, FormStateInterface $form_state) { } /** + * Validates a filter identifier. + * + * Sets the form error if $form_state is passed or a error string if + * $form_state is not passed. + * + * @param string $identifier + * The identifier to check. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * @param array $form_group + * The form element to set any errors on. + * + * @return string + */ + protected function validateIdentifier($identifier, FormStateInterface $form_state = NULL, &$form_group = array()) { + $error = ''; + if (empty($identifier)) { + $error = $this->t('The identifier is required if the filter is exposed.'); + } + elseif ($identifier == 'value') { + $error = $this->t('This identifier is not allowed.'); + } + elseif (preg_match('/[^a-zA-z0-9_~\.\-]/', $identifier)) { + $error = $this->t('This identifier has illegal characters.'); + } + + if ($form_state && !$this->view->display_handler->isIdentifierUnique($form_state->get('id'), $identifier)) { + $error = $this->t('This identifier is used by another handler.'); + } + + if (!empty($form_state) && !empty($error)) { + $form_state->setError($form_group, $error); + } + return $error; + } + + /** * Save new group items, re-enumerates and remove groups marked to delete. */ protected function buildGroupSubmit($form, FormStateInterface $form_state) { @@ -864,7 +881,7 @@ protected function buildExposedFiltersGroupForm(&$form, FormStateInterface $form '#default_value' => $identifier, '#title' => $this->t('Filter identifier'), '#size' => 40, - '#description' => $this->t('This will appear in the URL after the ? to identify this filter. Cannot be blank.'), + '#description' => $this->t('This will appear in the URL after the ? to identify this filter. Cannot be blank. Only letters, digits and the dot ("."), hyphen ("-"), underscore ("_"), and tilde ("~") characters are allowed.'), ); $form['group_info']['label'] = array( '#type' => 'textfield', @@ -918,7 +935,7 @@ protected function buildExposedFiltersGroupForm(&$form, FormStateInterface $form '#default_value' => $identifier, '#title' => $this->t('Filter identifier'), '#size' => 40, - '#description' => $this->t('This will appear in the URL after the ? to identify this filter. Cannot be blank.'), + '#description' => $this->t('This will appear in the URL after the ? to identify this filter. Cannot be blank. Only letters, digits and the dot ("."), hyphen ("-"), underscore ("_"), and tilde ("~") characters are allowed.'), ); $form['group_info']['label'] = array( '#type' => 'textfield', @@ -1485,6 +1502,15 @@ public function getCacheTags() { return []; } + /** + * {@inheritdoc} + */ + public function validate() { + if (!empty($this->options['exposed']) && $error = $this->validateIdentifier($this->options['expose']['identifier'])) { + return [$error]; + } + } + } /** diff --git a/core/modules/views/src/Plugin/views/filter/InOperator.php b/core/modules/views/src/Plugin/views/filter/InOperator.php index 042dca5..cfcde0e 100644 --- a/core/modules/views/src/Plugin/views/filter/InOperator.php +++ b/core/modules/views/src/Plugin/views/filter/InOperator.php @@ -415,7 +415,7 @@ protected function opEmpty() { public function validate() { $this->getValueOptions(); - $errors = array(); + $errors = parent::validate(); // If the operator is an operator which doesn't require a value, there is // no need for additional validation. diff --git a/core/modules/views/src/Plugin/views/query/Sql.php b/core/modules/views/src/Plugin/views/query/Sql.php index 85356fc..25374c0 100644 --- a/core/modules/views/src/Plugin/views/query/Sql.php +++ b/core/modules/views/src/Plugin/views/query/Sql.php @@ -1838,7 +1838,7 @@ public function getDateFormat($field, $format, $string_date = FALSE) { // SQLite does not have a ISO week substitution string, so it needs // special handling. - // @see http://en.wikipedia.org/wiki/ISO_week_date#Calculation + // @see http://wikipedia.org/wiki/ISO_week_date#Calculation // @see http://stackoverflow.com/a/15511864/1499564 if ($format === '%W') { $expression = "((strftime('%j', date(strftime('%Y-%m-%d', $field" . $unixepoch . "), '-3 days', 'weekday 4')) - 1) / 7 + 1)"; diff --git a/core/modules/views/src/Plugin/views/relationship/GroupwiseMax.php b/core/modules/views/src/Plugin/views/relationship/GroupwiseMax.php index 52b1ff7..0aae903 100644 --- a/core/modules/views/src/Plugin/views/relationship/GroupwiseMax.php +++ b/core/modules/views/src/Plugin/views/relationship/GroupwiseMax.php @@ -10,6 +10,7 @@ use Drupal\Core\Database\Query\AlterableInterface; use Drupal\Core\Form\FormStateInterface; use Drupal\views\Views; +use Drupal\views\Entity\View; /** * Relationship handler that allows a groupwise maximum of the linked in table. @@ -156,7 +157,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { * We use this to obtain our subquery SQL. */ protected function getTemporaryView() { - $view = entity_create('view', array('base_table' => $this->definition['base'])); + $view = View::create(array('base_table' => $this->definition['base'])); $view->addDisplay('default'); return $view->getExecutable(); } diff --git a/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php b/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php index 7ca2cc90..5269074 100644 --- a/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php +++ b/core/modules/views/src/Plugin/views/relationship/RelationshipPluginBase.php @@ -11,7 +11,6 @@ use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\views\Plugin\views\HandlerBase; -use Drupal\views\Join; use Drupal\views\Views; /** diff --git a/core/modules/views/src/Plugin/views/row/OpmlFields.php b/core/modules/views/src/Plugin/views/row/OpmlFields.php index a2ddf43..91c3f04 100644 --- a/core/modules/views/src/Plugin/views/row/OpmlFields.php +++ b/core/modules/views/src/Plugin/views/row/OpmlFields.php @@ -134,7 +134,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { $form['url_field'] = array( '#type' => 'select', '#title' => $this->t('URL attribute'), - '#description' => $this->t('The field that is going to be used as the OPML url attribute for each row.'), + '#description' => $this->t('The field that is going to be used as the OPML URL attribute for each row.'), '#options' => $view_fields_labels, '#default_value' => $this->options['url_field'], '#states' => array( diff --git a/core/modules/views/src/Plugin/views/style/StylePluginBase.php b/core/modules/views/src/Plugin/views/style/StylePluginBase.php index 148e053..65916ba 100644 --- a/core/modules/views/src/Plugin/views/style/StylePluginBase.php +++ b/core/modules/views/src/Plugin/views/style/StylePluginBase.php @@ -10,7 +10,6 @@ use Drupal\Component\Utility\Html; use Drupal\Component\Utility\Xss; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\Element; use Drupal\views\Plugin\views\display\DisplayPluginBase; use Drupal\views\Plugin\views\PluginBase; use Drupal\views\Plugin\views\wizard\WizardInterface; diff --git a/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php b/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php index 998447c..d8f90c3 100644 --- a/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php +++ b/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php @@ -660,7 +660,7 @@ protected function instantiateView($form, FormStateInterface $form_state) { 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(), ); - $view = entity_create('view', $values); + $view = View::create($values); // Build all display options for this view. $display_options = $this->buildDisplayOptions($form, $form_state); @@ -800,7 +800,7 @@ protected function defaultDisplayOptions() { $display_options['cache']['type'] = 'tag'; $display_options['query']['type'] = 'views_query'; $display_options['exposed_form']['type'] = 'basic'; - $display_options['pager']['type'] = 'full'; + $display_options['pager']['type'] = 'mini'; $display_options['style']['type'] = 'default'; $display_options['row']['type'] = 'fields'; @@ -1038,9 +1038,9 @@ protected function pageDisplayOptions(array $form, FormStateInterface $form_stat if (empty($page['items_per_page'])) { $display_options['pager']['type'] = 'none'; } - // If the user checked the pager checkbox use a full pager. + // If the user checked the pager checkbox use a mini pager. elseif (!empty($page['pager'])) { - $display_options['pager']['type'] = 'full'; + $display_options['pager']['type'] = 'mini'; } // If the user doesn't have checked the checkbox use the pager which just // displays a certain amount of items. diff --git a/core/modules/views/src/Tests/BasicTest.php b/core/modules/views/src/Tests/BasicTest.php deleted file mode 100644 index 69aacf0..0000000 --- a/core/modules/views/src/Tests/BasicTest.php +++ /dev/null @@ -1,142 +0,0 @@ -setDisplay(); - - // Execute the view. - $this->executeView($view); - - // Verify the result. - $this->assertEqual(5, count($view->result), 'The number of returned rows match.'); - $this->assertIdenticalResultset($view, $this->dataSet(), array( - 'views_test_data_name' => 'name', - 'views_test_data_age' => 'age', - )); - } - - /** - * Tests filtering of the result set. - */ - public function testSimpleFiltering() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Add a filter. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'age' => array( - 'operator' => '<', - 'value' => array( - 'value' => '28', - 'min' => '', - 'max' => '', - ), - 'group' => '0', - 'exposed' => FALSE, - 'expose' => array( - 'operator' => FALSE, - 'label' => '', - ), - 'id' => 'age', - 'table' => 'views_test_data', - 'field' => 'age', - 'relationship' => 'none', - ), - )); - - // Execute the view. - $this->executeView($view); - - // Build the expected result. - $dataset = array( - array( - 'id' => 1, - 'name' => 'John', - 'age' => 25, - ), - array( - 'id' => 2, - 'name' => 'George', - 'age' => 27, - ), - array( - 'id' => 4, - 'name' => 'Paul', - 'age' => 26, - ), - ); - - // Verify the result. - $this->assertEqual(3, count($view->result), 'The number of returned rows match.'); - $this->assertIdenticalResultSet($view, $dataset, array( - 'views_test_data_name' => 'name', - 'views_test_data_age' => 'age', - )); - } - - /** - * Tests simple argument. - */ - public function testSimpleArgument() { - // Execute with a view - $view = Views::getView('test_simple_argument'); - $view->setArguments(array(27)); - $this->executeView($view); - - // Build the expected result. - $dataset = array( - array( - 'id' => 2, - 'name' => 'George', - 'age' => 27, - ), - ); - - // Verify the result. - $this->assertEqual(1, count($view->result), 'The number of returned rows match.'); - $this->assertIdenticalResultSet($view, $dataset, array( - 'views_test_data_name' => 'name', - 'views_test_data_age' => 'age', - )); - - // Test "show all" if no argument is present. - $view = Views::getView('test_simple_argument'); - $this->executeView($view); - - // Build the expected result. - $dataset = $this->dataSet(); - - $this->assertEqual(5, count($view->result), 'The number of returned rows match.'); - $this->assertIdenticalResultSet($view, $dataset, array( - 'views_test_data_name' => 'name', - 'views_test_data_age' => 'age', - )); - } - -} diff --git a/core/modules/views/src/Tests/DefaultViewsTest.php b/core/modules/views/src/Tests/DefaultViewsTest.php index 197d25e..1276378 100644 --- a/core/modules/views/src/Tests/DefaultViewsTest.php +++ b/core/modules/views/src/Tests/DefaultViewsTest.php @@ -15,6 +15,9 @@ use Drupal\Core\Url; use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait; use Drupal\views\Views; +use Drupal\comment\Entity\Comment; +use Drupal\taxonomy\Entity\Vocabulary; +use Drupal\taxonomy\Entity\Term; /** * Tests the default views provided by views. @@ -52,7 +55,7 @@ protected function setUp() { // Create Basic page node type. $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page')); - $vocabulary = entity_create('taxonomy_vocabulary', array( + $vocabulary = Vocabulary::create([ 'name' => $this->randomMachineName(), 'description' => $this->randomMachineName(), 'vid' => Unicode::strtolower($this->randomMachineName()), @@ -60,7 +63,7 @@ protected function setUp() { 'help' => '', 'nodes' => array('page' => 'page'), 'weight' => mt_rand(0, 10), - )); + ]); $vocabulary->save(); // Create a field. @@ -101,7 +104,7 @@ protected function setUp() { 'entity_type' => 'node', 'field_name' => 'comment' ); - entity_create('comment', $comment)->save(); + Comment::create($comment)->save(); } // Some views, such as the "Who's Online" view, only return results if at @@ -148,14 +151,14 @@ public function testDefaultViews() { function createTerm($vocabulary) { $filter_formats = filter_formats(); $format = array_pop($filter_formats); - $term = entity_create('taxonomy_term', array( + $term = Term::create([ 'name' => $this->randomMachineName(), 'description' => $this->randomMachineName(), // Use the first available text format. 'format' => $format->id(), 'vid' => $vocabulary->id(), 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - )); + ]); $term->save(); return $term; } diff --git a/core/modules/views/src/Tests/Entity/FieldEntityTest.php b/core/modules/views/src/Tests/Entity/FieldEntityTest.php index 925ed10..6f34dda 100644 --- a/core/modules/views/src/Tests/Entity/FieldEntityTest.php +++ b/core/modules/views/src/Tests/Entity/FieldEntityTest.php @@ -8,9 +8,12 @@ namespace Drupal\views\Tests\Entity; use Drupal\comment\Tests\CommentTestTrait; +use Drupal\node\Entity\Node; +use Drupal\user\Entity\User; use Drupal\views\Tests\ViewTestBase; use Drupal\views\Tests\ViewTestData; use Drupal\views\Views; +use Drupal\comment\Entity\Comment; /** * Tests the field plugin base integration with the entity system. @@ -54,12 +57,16 @@ public function testGetEntity() { // The view is a view of comments, their nodes and their authors, so there // are three layers of entities. - $account = entity_create('user', array('name' => $this->randomMachineName(), 'bundle' => 'user')); + $account = User::create(['name' => $this->randomMachineName(), 'bundle' => 'user']); $account->save(); - $node = entity_create('node', array('uid' => $account->id(), 'type' => 'page', 'title' => $this->randomString())); + $node = Node::create([ + 'uid' => $account->id(), + 'type' => 'page', + 'title' => $this->randomString(), + ]); $node->save(); - $comment = entity_create('comment', array( + $comment = Comment::create(array( 'uid' => $account->id(), 'entity_id' => $node->id(), 'entity_type' => 'node', diff --git a/core/modules/views/src/Tests/Entity/FieldEntityTranslationTest.php b/core/modules/views/src/Tests/Entity/FieldEntityTranslationTest.php index 8e4515a..540ff04 100644 --- a/core/modules/views/src/Tests/Entity/FieldEntityTranslationTest.php +++ b/core/modules/views/src/Tests/Entity/FieldEntityTranslationTest.php @@ -46,9 +46,7 @@ protected function setUp($import_test_views = TRUE) { /** @var \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager */ $content_translation_manager = \Drupal::service('content_translation.manager'); - $content_translation_manager->setEnabled('node', 'article', 'title'); - $content_translation_manager->setEnabled('node', 'article', 'sticky'); - $content_translation_manager->setEnabled('node', 'article', 'published'); + $content_translation_manager->setEnabled('node', 'article', TRUE); $language = ConfigurableLanguage::create([ 'id' => 'es', diff --git a/core/modules/views/src/Tests/Entity/FilterEntityBundleTest.php b/core/modules/views/src/Tests/Entity/FilterEntityBundleTest.php index 709dd37..99a7193 100644 --- a/core/modules/views/src/Tests/Entity/FilterEntityBundleTest.php +++ b/core/modules/views/src/Tests/Entity/FilterEntityBundleTest.php @@ -7,6 +7,7 @@ namespace Drupal\views\Tests\Entity; +use Drupal\node\Entity\Node; use Drupal\views\Tests\ViewTestBase; use Drupal\views\Tests\ViewTestData; use Drupal\views\Views; @@ -60,7 +61,11 @@ protected function setUp() { foreach ($this->entityBundles as $key => $info) { for ($i = 0; $i < 5; $i++) { - $entity = entity_create('node', array('title' => $this->randomString(), 'uid' => 1, 'type' => $key)); + $entity = Node::create([ + 'title' => $this->randomString(), + 'uid' => 1, + 'type' => $key, + ]); $entity->save(); $this->entities[$key][$entity->id()] = $entity; $this->entities['count']++; diff --git a/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php b/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php deleted file mode 100644 index f69444a..0000000 --- a/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php +++ /dev/null @@ -1,238 +0,0 @@ -installEntitySchema('node'); - $this->installEntitySchema('user'); - $this->installSchema('node', array('node_access')); - $this->installConfig(array('node', 'language')); - - // The entity.node.canonical route must exist when nodes are rendered. - $this->container->get('router.builder')->rebuild(); - - $this->langcodes = array(\Drupal::languageManager()->getDefaultLanguage()->getId()); - for ($i = 0; $i < 2; $i++) { - $langcode = 'l' . $i; - $this->langcodes[] = $langcode; - ConfigurableLanguage::createFromLangcode($langcode)->save(); - } - - // Make sure we do not try to render non-existing user data. - $node_type = NodeType::create(array('type' => 'test')); - $node_type->setDisplaySubmitted(FALSE); - $node_type->save(); - - $this->values = array(); - $controller = \Drupal::entityManager()->getStorage('node'); - $langcode_index = 0; - - for ($i = 0; $i < count($this->langcodes); $i++) { - // Create a node with a different default language each time. - $default_langcode = $this->langcodes[$langcode_index++]; - $node = $controller->create(array('type' => 'test', 'uid' => 0, 'langcode' => $default_langcode)); - // Ensure the default language is processed first. - $langcodes = array_merge(array($default_langcode), array_diff($this->langcodes, array($default_langcode))); - - foreach ($langcodes as $langcode) { - // Ensure we have a predictable result order. - $this->values[$i][$langcode] = $i . '-' . $langcode . '-' . $this->randomMachineName(); - - if ($langcode != $default_langcode) { - $node->addTranslation($langcode, array('title' => $this->values[$i][$langcode])); - } - else { - $node->setTitle($this->values[$i][$langcode]); - } - - $node->save(); - } - } - } - - /** - * Tests the entity row renderers. - */ - public function testEntityRenderers() { - $this->checkLanguageRenderers('page_1', $this->values); - } - - /** - * Tests the field row renderers. - */ - public function testFieldRenderers() { - $this->checkLanguageRenderers('page_2', $this->values); - } - - /** - * Checks that the language renderer configurations work as expected. - * - * @param string $display - * Name of display to test with. - * @param array $values - * An array of node information which are each an array of node titles - * associated with language keys appropriate for the translation of that - * node. - */ - protected function checkLanguageRenderers($display, $values) { - $expected = array( - $values[0]['en'], - $values[0]['en'], - $values[0]['en'], - $values[1]['en'], - $values[1]['en'], - $values[1]['en'], - $values[2]['en'], - $values[2]['en'], - $values[2]['en'], - ); - $this->assertTranslations($display, '***LANGUAGE_language_content***', $expected, 'The current language renderer behaves as expected.'); - - $expected = array( - $values[0]['en'], - $values[0]['en'], - $values[0]['en'], - $values[1]['l0'], - $values[1]['l0'], - $values[1]['l0'], - $values[2]['l1'], - $values[2]['l1'], - $values[2]['l1'], - ); - $this->assertTranslations($display, '***LANGUAGE_entity_default***', $expected, 'The default language renderer behaves as expected.'); - - $expected = array( - $values[0]['en'], - $values[0]['l0'], - $values[0]['l1'], - $values[1]['en'], - $values[1]['l0'], - $values[1]['l1'], - $values[2]['en'], - $values[2]['l0'], - $values[2]['l1'], - ); - $this->assertTranslations($display, '***LANGUAGE_entity_translation***', $expected, 'The translation language renderer behaves as expected.'); - - $expected = array( - $values[0][$this->langcodes[0]], - $values[0][$this->langcodes[0]], - $values[0][$this->langcodes[0]], - $values[1][$this->langcodes[0]], - $values[1][$this->langcodes[0]], - $values[1][$this->langcodes[0]], - $values[2][$this->langcodes[0]], - $values[2][$this->langcodes[0]], - $values[2][$this->langcodes[0]], - ); - $this->assertTranslations($display, '***LANGUAGE_site_default***', $expected, 'The site default language renderer behaves as expected.'); - - $expected = array( - $values[0]['l0'], - $values[0]['l0'], - $values[0]['l0'], - $values[1]['l0'], - $values[1]['l0'], - $values[1]['l0'], - $values[2]['l0'], - $values[2]['l0'], - $values[2]['l0'], - ); - $this->assertTranslations($display, 'l0', $expected, 'The language specific renderer behaves as expected.'); - } - - /** - * Checks that the view results match the expected values. - * - * @param string $display - * Name of display to test with. - * @param string $renderer_id - * The id of the renderer to be tested. - * @param array $expected - * An array of expected title translation values, one for each result row. - * @param string $message - * (optional) A message to display with the assertion. - * @param string $group - * (optional) The group this message is in. - * - * @return bool - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertTranslations($display, $renderer_id, array $expected, $message = '', $group = 'Other') { - $view = Views::getView('test_entity_row_renderers'); - $view->storage->invalidateCaches(); - $view->setDisplay($display); - $view->getDisplay()->setOption('rendering_language', $renderer_id); - $view->preview(); - - $result = FALSE; - foreach ($expected as $index => $expected_output) { - if (!empty($view->result[$index])) { - $build = $view->rowPlugin->render($view->result[$index]); - $output = \Drupal::service('renderer')->renderRoot($build); - $result = strpos($output, $expected_output) !== FALSE; - if (!$result) { - break; - } - } - else { - $result = FALSE; - break; - } - } - - return $this->assertTrue($result, $message, $group); - } - -} diff --git a/core/modules/views/src/Tests/Entity/ViewEntityDependenciesTest.php b/core/modules/views/src/Tests/Entity/ViewEntityDependenciesTest.php deleted file mode 100644 index 632be6a..0000000 --- a/core/modules/views/src/Tests/Entity/ViewEntityDependenciesTest.php +++ /dev/null @@ -1,180 +0,0 @@ -installEntitySchema('node'); - $this->installConfig(array('field', 'node')); - - $comment_type = entity_create('comment_type', array( - 'id' => 'comment', - 'label' => 'Comment settings', - 'description' => 'Comment settings', - 'target_entity_type_id' => 'node', - )); - $comment_type->save(); - - $content_type = entity_create('node_type', array( - 'type' => $this->randomMachineName(), - 'name' => $this->randomString(), - )); - $content_type->save(); - $field_storage = entity_create('field_storage_config', array( - 'field_name' => Unicode::strtolower($this->randomMachineName()), - 'entity_type' => 'node', - 'type' => 'comment', - )); - $field_storage->save(); - entity_create('field_config', array( - 'field_storage' => $field_storage, - 'bundle' => $content_type->id(), - 'label' => $this->randomMachineName() . '_label', - 'description' => $this->randomMachineName() . '_description', - 'settings' => array( - 'comment_type' => $comment_type->id(), - ), - ))->save(); - entity_create('field_config', array( - 'field_storage' => FieldStorageConfig::loadByName('node', 'body'), - 'bundle' => $content_type->id(), - 'label' => $this->randomMachineName() . '_body', - 'settings' => array('display_summary' => TRUE), - ))->save(); - - ViewTestData::createTestViews(get_class($this), array('views_test_config')); - } - - /** - * Tests the getDependencies method. - */ - public function testGetDependencies() { - $expected = []; - $expected['test_field_get_entity'] = [ - 'module' => [ - 'comment', - 'node', - 'user', - ] - ]; - // Tests dependencies of relationships. - $expected['test_relationship_dependency'] = [ - 'module' => [ - 'comment', - 'node', - 'user', - ] - ]; - $expected['test_plugin_dependencies'] = [ - 'module' => [ - 'comment', - 'views_test_data', - ], - 'content' => [ - 'RowTest', - 'StaticTest', - 'StyleTest', - ] - ]; - - $expected['test_argument_dependency'] = [ - 'config' => [ - 'core.entity_view_mode.node.teaser', - 'field.storage.node.body' - ], - 'content' => [ - 'ArgumentDefaultTest', - 'ArgumentValidatorTest' - ], - 'module' => [ - 'node', - // The argument handler is provided by the search module. - 'search', - 'text', - 'user' - ], - ]; - - foreach ($this::$testViews as $view_id) { - $view = Views::getView($view_id); - - $dependencies = $view->getDependencies(); - $this->assertEqual($expected[$view_id], $dependencies); - $config = $this->config('views.view.' . $view_id); - \Drupal::service('config.storage.sync')->write($view_id, $config->get()); - } - - // Ensure that dependencies are calculated on the display level. - $expected_display['default'] = [ - 'config' => [ - 'core.entity_view_mode.node.teaser', - ], - 'content' => [ - 'ArgumentDefaultTest', - 'ArgumentValidatorTest' - ], - 'module' => [ - 'core', - 'node', - 'search', - 'user', - 'views' - ], - ]; - $expected_display['page'] = [ - 'config' => [ - 'field.storage.node.body' - ], - 'module' => [ - 'core', - 'text', - 'views' - ], - ]; - - $view = Views::getView('test_argument_dependency'); - $view->initDisplay(); - foreach ($view->displayHandlers as $display) { - // Calculate the dependencies each display has. - $this->assertEqual($expected_display[$display->getPluginId()], $display->calculateDependencies()); - } - } - -} diff --git a/core/modules/views/src/Tests/EventSubscriber/ViewsEntitySchemaSubscriberIntegrationTest.php b/core/modules/views/src/Tests/EventSubscriber/ViewsEntitySchemaSubscriberIntegrationTest.php deleted file mode 100644 index 1f8491a..0000000 --- a/core/modules/views/src/Tests/EventSubscriber/ViewsEntitySchemaSubscriberIntegrationTest.php +++ /dev/null @@ -1,466 +0,0 @@ -eventDispatcher = $this->container->get('event_dispatcher'); - $this->eventSubscriber = $this->container->get('views.entity_schema_subscriber'); - $this->entityDefinitionUpdateManager = $this->container->get('entity.definition_update_manager'); - $this->entityManager = $this->container->get('entity.manager'); - $this->state = $this->container->get('state'); - - $this->database = $this->container->get('database'); - - // Install every entity type's schema that wasn't installed in the parent - // method. - foreach (array_diff_key($this->entityManager->getDefinitions(), array_flip(array('user', 'entity_test'))) as $entity_type_id => $entity_type) { - $this->installEntitySchema($entity_type_id); - } - - $this->installSchema('system', 'key_value_expire'); - } - - /** - * Tests that views are disabled when an entity type is deleted. - */ - public function testDeleteEntityType() { - $entity_storage = $this->entityManager->getStorage('view'); - - $views = $entity_storage->loadMultiple(); - - // Ensure that all test views exists. - $this->assertTrue(isset($views['test_view_entity_test'])); - $this->assertTrue(isset($views['test_view_entity_test_revision'])); - $this->assertTrue(isset($views['test_view_entity_test_data'])); - $this->assertTrue(isset($views['test_view_entity_test_additional_base_field'])); - - $event = new EntityTypeEvent($this->entityManager->getDefinition('entity_test_update')); - $this->eventDispatcher->dispatch(EntityTypeEvents::DELETE, $event); - - // We expect that views which use 'entity_test_update' as base tables are - // disabled. - $views = $entity_storage->loadMultiple(); - - // Ensure that all test views still exists after the deletion of the - // entity type. - $this->assertTrue(isset($views['test_view_entity_test'])); - $this->assertTrue(isset($views['test_view_entity_test_revision'])); - $this->assertTrue(isset($views['test_view_entity_test_data'])); - $this->assertTrue(isset($views['test_view_entity_test_additional_base_field'])); - - // Ensure that they are all disabled. - $this->assertFalse($views['test_view_entity_test']->status()); - $this->assertFalse($views['test_view_entity_test_revision']->status()); - $this->assertFalse($views['test_view_entity_test_data']->status()); - $this->assertFalse($views['test_view_entity_test_additional_base_field']->status()); - } - - /** - * Tests that renaming base tables adapts the views. - */ - public function testBaseTableRename() { - $this->renameBaseTable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - /** @var \Drupal\views\Entity\View $view */ - $entity_storage = $this->entityManager->getStorage('view'); - $view = $entity_storage->load('test_view_entity_test'); - - // Ensure the base table got renamed, so also the views fields. - $this->assertEqual('entity_test_update_new', $view->get('base_table')); - $display = $view->getDisplay('default'); - $this->assertEqual('entity_test_update_new', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_new', $display['display_options']['fields']['name']['table']); - } - - /** - * Tests that renaming data tables adapts the views. - */ - public function testDataTableRename() { - $this->updateEntityTypeToTranslatable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - $entity_storage = $this->entityManager->getStorage('view'); - $view = $entity_storage->load('test_view_entity_test_data'); - $this->assertEqual('entity_test_update', $view->get('base_table')); - $display = $view->getDisplay('default'); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - // Ensure that the data table is used. - $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); - - $this->renameDataTable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - /** @var \Drupal\views\Entity\View $view */ - $entity_storage = $this->entityManager->getStorage('view'); - $view = $entity_storage->load('test_view_entity_test_data'); - - // Ensure the data table got renamed, so also the views fields. - $this->assertEqual('entity_test_update', $view->get('base_table')); - $display = $view->getDisplay('default'); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_data_new', $display['display_options']['fields']['name']['table']); - } - - /** - * Tests that renaming revision tables adapts the views. - */ - public function testRevisionBaseTableRename() { - $this->updateEntityTypeToRevisionable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - /** @var \Drupal\views\Entity\View $view */ - $entity_storage = $this->entityManager->getStorage('view'); - $view = $entity_storage->load('test_view_entity_test_revision'); - $this->assertEqual('entity_test_update_revision', $view->get('base_table')); - $display = $view->getDisplay('default'); - $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['name']['table']); - - $this->renameRevisionBaseTable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - /** @var \Drupal\views\Entity\View $view */ - $entity_storage = $this->entityManager->getStorage('view'); - $view = $entity_storage->load('test_view_entity_test_revision'); - - // Ensure the base table got renamed, so also the views fields. - $this->assertEqual('entity_test_update_revision_new', $view->get('base_table')); - $display = $view->getDisplay('default'); - $this->assertEqual('entity_test_update_revision_new', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_revision_new', $display['display_options']['fields']['name']['table']); - } - - /** - * Tests that renaming revision tables adapts the views. - */ - public function testRevisionDataTableRename() { - $this->updateEntityTypeToRevisionable(); - // Multiple changes, so we have to invalidate the caches, otherwise - // the second update will revert the first. - $this->entityManager->clearCachedDefinitions(); - $this->updateEntityTypeToTranslatable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - /** @var \Drupal\views\Entity\View $view */ - $entity_storage = $this->entityManager->getStorage('view'); - $view = $entity_storage->load('test_view_entity_test_revision'); - $this->assertEqual('entity_test_update_revision', $view->get('base_table')); - $display = $view->getDisplay('default'); - $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_revision_data', $display['display_options']['fields']['name']['table']); - - $this->renameRevisionDataTable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - /** @var \Drupal\views\Entity\View $view */ - $entity_storage = $this->entityManager->getStorage('view'); - $view = $entity_storage->load('test_view_entity_test_revision'); - - // Ensure the base table got renamed, so also the views fields. - $this->assertEqual('entity_test_update_revision', $view->get('base_table')); - $display = $view->getDisplay('default'); - $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_revision_data_new', $display['display_options']['fields']['name']['table']); - } - - /** - * Tests that adding data tables adapts the views. - */ - public function testDataTableAddition() { - $this->updateEntityTypeToTranslatable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - /** @var \Drupal\views\Entity\View $view */ - $entity_storage = $this->entityManager->getStorage('view'); - $view = $entity_storage->load('test_view_entity_test'); - - // Ensure the data table got renamed, so also the views fields. - $this->assertEqual('entity_test_update', $view->get('base_table')); - $display = $view->getDisplay('default'); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); - } - - /** - * Tests that enabling revisions doesn't do anything. - */ - public function testRevisionEnabling() { - $this->updateEntityTypeToRevisionable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - /** @var \Drupal\views\Entity\View $view */ - $entity_storage = $this->entityManager->getStorage('view'); - $view = $entity_storage->load('test_view_entity_test'); - - // Ensure that nothing happens. - $this->assertEqual('entity_test_update', $view->get('base_table')); - $display = $view->getDisplay('default'); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); - } - - /** - * Tests that removing revision support disables the view. - */ - public function testRevisionDisabling() { - $this->updateEntityTypeToRevisionable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - $this->updateEntityTypeToNotRevisionable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - - /** @var \Drupal\views\Entity\View $view */ - $entity_storage = $this->entityManager->getStorage('view'); - $view = $entity_storage->load('test_view_entity_test_revision'); - - $this->assertFalse($view->status()); - } - - /** - * Tests a bunch possible entity definition table updates. - */ - public function testVariousTableUpdates() { - // We want to test the following permutations of entity definition updates: - // base <-> base + translation - // base + translation <-> base + translation + revision - // base + revision <-> base + translation + revision - // base <-> base + revision - // base <-> base + translation + revision - - // base <-> base + translation - $this->updateEntityTypeToTranslatable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(); - - $this->assertEqual('entity_test_update', $view->get('base_table')); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); - - $this->updateEntityTypeToNotTranslatable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(); - - $this->assertEqual('entity_test_update', $view->get('base_table')); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); - - $this->resetEntityType(); - - // base + translation <-> base + translation + revision - $this->updateEntityTypeToTranslatable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(); - - $this->assertEqual('entity_test_update', $view->get('base_table')); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); - - $this->updateEntityTypeToRevisionable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(); - - $this->assertEqual('entity_test_update', $view->get('base_table')); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); - - $this->updateEntityTypeToNotRevisionable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(); - - $this->assertEqual('entity_test_update', $view->get('base_table')); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); - - $this->resetEntityType(); - - // base + revision <-> base + translation + revision - $this->updateEntityTypeToRevisionable(); - list($view, $display) = $this->getUpdatedViewAndDisplay(); - - $this->assertEqual('entity_test_update', $view->get('base_table')); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); - - $this->updateEntityTypeToTranslatable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(); - - $this->assertEqual('entity_test_update', $view->get('base_table')); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); - - $this->updateEntityTypeToNotTranslatable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(); - - $this->assertEqual('entity_test_update', $view->get('base_table')); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); - - $this->resetEntityType(); - - // base <-> base + revision - $this->updateEntityTypeToRevisionable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(); - - $this->assertEqual('entity_test_update', $view->get('base_table')); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); - - $this->updateEntityTypeToNotRevisionable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(); - - $this->assertEqual('entity_test_update', $view->get('base_table')); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); - - $this->resetEntityType(); - - // base <-> base + translation + revision - $this->updateEntityTypeToRevisionable(); - $this->updateEntityTypeToTranslatable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(); - - $this->assertEqual('entity_test_update', $view->get('base_table')); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); - - $this->updateEntityTypeToNotRevisionable(); - $this->updateEntityTypeToNotTranslatable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(); - - $this->assertEqual('entity_test_update', $view->get('base_table')); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); - } - - /** - * Tests some possible entity table updates for a revision view. - */ - public function testVariousTableUpdatesForRevisionView() { - // base + revision <-> base + translation + revision - $this->updateEntityTypeToRevisionable(); - // Multiple changes, so we have to invalidate the caches, otherwise - // the second update will revert the first. - $this->entityManager->clearCachedDefinitions(); - - list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE); - - $this->assertEqual('entity_test_update_revision', $view->get('base_table')); - $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['name']['table']); - - $this->updateEntityTypeToTranslatable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE); - - $this->assertEqual('entity_test_update_revision', $view->get('base_table')); - $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_revision_data', $display['display_options']['fields']['name']['table']); - - $this->updateEntityTypeToNotTranslatable(); - $this->entityDefinitionUpdateManager->applyUpdates(); - list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE); - - $this->assertEqual('entity_test_update_revision', $view->get('base_table')); - $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); - $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['name']['table']); - - $this->resetEntityType(); - } - - /** - * Gets a view and its display. - * - * @param bool $revision - * (optional) TRUE if we want to get a revision view. - * - * @return array - * An array with the view as first item, and the display as second. - */ - protected function getUpdatedViewAndDisplay($revision = FALSE) { - $entity_storage = $this->entityManager->getStorage('view'); - /** @var \Drupal\views\Entity\View $view */ - $view = $entity_storage->load($revision ? 'test_view_entity_test_revision' : 'test_view_entity_test'); - $display = $view->getDisplay('default'); - - return [$view, $display]; - } - -} diff --git a/core/modules/views/src/Tests/FieldApiDataTest.php b/core/modules/views/src/Tests/FieldApiDataTest.php index 72ee0be..68e1250 100644 --- a/core/modules/views/src/Tests/FieldApiDataTest.php +++ b/core/modules/views/src/Tests/FieldApiDataTest.php @@ -30,7 +30,7 @@ protected function setUp() { 'bundle' => 'page', 'label' => 'GiraffeA" label' ); - entity_create('field_config', $field)->save(); + FieldConfig::create($field)->save(); // Attach the same field to a different bundle with a different label. $this->drupalCreateContentType(['type' => 'article']); diff --git a/core/modules/views/src/Tests/Handler/AreaEntityTest.php b/core/modules/views/src/Tests/Handler/AreaEntityTest.php deleted file mode 100644 index 434463b..0000000 --- a/core/modules/views/src/Tests/Handler/AreaEntityTest.php +++ /dev/null @@ -1,202 +0,0 @@ -installEntitySchema('user'); - $this->installEntitySchema('entity_test'); - $this->installConfig(['entity_test']); - - Block::create([ - 'id' => 'test_block', - 'plugin' => 'system_main_block', - ])->save(); - - parent::setUpFixtures(); - } - - /** - * Tests views data for entity area handlers. - */ - public function testEntityAreaData() { - $data = $this->container->get('views.views_data')->get('views'); - $entity_types = $this->container->get('entity.manager')->getDefinitions(); - - $expected_entities = array_filter($entity_types, function (EntityTypeInterface $entity_type) { - return $entity_type->hasViewBuilderClass(); - }); - - // Test that all expected entity types have data. - foreach (array_keys($expected_entities) as $entity) { - $this->assertTrue(!empty($data['entity_' . $entity]), format_string('Views entity area data found for @entity', array('@entity' => $entity))); - // Test that entity_type is set correctly in the area data. - $this->assertEqual($entity, $data['entity_' . $entity]['area']['entity_type'], format_string('Correct entity_type set for @entity', array('@entity' => $entity))); - } - - $expected_entities = array_filter($entity_types, function (EntityTypeInterface $type) { - return !$type->hasViewBuilderClass(); - }); - - // Test that no configuration entity types have data. - foreach (array_keys($expected_entities) as $entity) { - $this->assertTrue(empty($data['entity_' . $entity]), format_string('Views config entity area data not found for @entity', array('@entity' => $entity))); - } - } - - /** - * Tests the area handler. - */ - public function testEntityArea() { - /** @var \Drupal\Core\Entity\EntityInterface[] $entities */ - $entities = array(); - for ($i = 0; $i < 3; $i++) { - $random_label = $this->randomMachineName(); - $data = array('bundle' => 'entity_test', 'name' => $random_label); - $entity_test = $this->container->get('entity.manager') - ->getStorage('entity_test') - ->create($data); - - $uuid_map[0] = 'aa0c61cb-b7bb-4795-972a-493dabcf529c'; - $uuid_map[1] = '62cef0ff-6f30-4f7a-b9d6-a8ed5a3a6bf3'; - $uuid_map[2] = '3161d6e9-3326-4719-b513-8fa68a731ba2'; - $entity_test->uuid->value = $uuid_map[$i]; - - $entity_test->save(); - $entities[] = $entity_test; - \Drupal::state() - ->set('entity_test_entity_access.view.' . $entity_test->id(), $i != 2); - } - - $this->doTestCalculateDependencies(); - $this->doTestRender($entities); - } - - /** - * Tests rendering the entity area handler. - * - * @param \Drupal\Core\Entity\EntityInterface[] $entities - * The entities. - */ - public function doTestRender($entities) { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = $this->container->get('renderer'); - $view = Views::getView('test_entity_area'); - $preview = $view->preview('default', [$entities[1]->id()]); - $this->setRawContent(\Drupal::service('renderer')->renderRoot($preview)); - $view_class = 'js-view-dom-id-' . $view->dom_id; - $header_xpath = '//div[@class = "' . $view_class . '"]/header[1]'; - $footer_xpath = '//div[@class = "' . $view_class . '"]/footer[1]'; - - $result = $this->xpath($header_xpath); - $this->assertTrue(strpos(trim((string) $result[0]), $entities[0]->label()) !== FALSE, 'The rendered entity appears in the header of the view.'); - $this->assertTrue(strpos(trim((string) $result[0]), 'full') !== FALSE, 'The rendered entity appeared in the right view mode.'); - - $result = $this->xpath($footer_xpath); - $this->assertTrue(strpos(trim((string) $result[0]), $entities[1]->label()) !== FALSE, 'The rendered entity appears in the footer of the view.'); - $this->assertTrue(strpos(trim((string) $result[0]), 'full') !== FALSE, 'The rendered entity appeared in the right view mode.'); - - $preview = $view->preview('default', array($entities[1]->id())); - $this->setRawContent($renderer->renderRoot($preview)); - - $result = $this->xpath($header_xpath); - $this->assertTrue(strpos(trim((string) $result[0]), $entities[0]->label()) !== FALSE, 'The rendered entity appears in the header of the view.'); - $this->assertTrue(strpos(trim((string) $result[0]), 'full') !== FALSE, 'The rendered entity appeared in the right view mode.'); - - $result = $this->xpath($footer_xpath); - $this->assertTrue(strpos(trim((string) $result[0]), $entities[1]->label()) !== FALSE, 'The rendered entity appears in the footer of the view.'); - $this->assertTrue(strpos(trim((string) $result[0]), 'full') !== FALSE, 'The rendered entity appeared in the right view mode.'); - - // Mark entity_test test view_mode as customizable. - $entity_view_mode = \Drupal::entityManager()->getStorage('entity_view_mode')->load('entity_test.test'); - $entity_view_mode->enable(); - $entity_view_mode->save(); - - // Change the view mode of the area handler. - $view = Views::getView('test_entity_area'); - $item = $view->getHandler('default', 'header', 'entity_entity_test'); - $item['view_mode'] = 'test'; - $view->setHandler('default', 'header', 'entity_entity_test', $item); - - $preview = $view->preview('default', array($entities[1]->id())); - $this->setRawContent($renderer->renderRoot($preview)); - $view_class = 'js-view-dom-id-' . $view->dom_id; - $result = $this->xpath('//div[@class = "' . $view_class . '"]/header[1]'); - $this->assertTrue(strpos(trim((string) $result[0]), $entities[0]->label()) !== FALSE, 'The rendered entity appears in the header of the view.'); - $this->assertTrue(strpos(trim((string) $result[0]), 'test') !== FALSE, 'The rendered entity appeared in the right view mode.'); - - // Test entity access. - $view = Views::getView('test_entity_area'); - $preview = $view->preview('default', array($entities[2]->id())); - $this->setRawContent($renderer->renderRoot($preview)); - $view_class = 'js-view-dom-id-' . $view->dom_id; - $result = $this->xpath('//div[@class = "' . $view_class . '"]/footer[1]'); - $this->assertTrue(strpos($result[0], $entities[2]->label()) === FALSE, 'The rendered entity does not appear in the footer of the view.'); - - // Test the available view mode options. - $form = array(); - $form_state = (new FormState()) - ->set('type', 'header'); - $view->display_handler->getHandler('header', 'entity_entity_test')->buildOptionsForm($form, $form_state); - $this->assertTrue(isset($form['view_mode']['#options']['test']), 'Ensure that the test view mode is available.'); - $this->assertTrue(isset($form['view_mode']['#options']['default']), 'Ensure that the default view mode is available.'); - } - - /** - * Tests the calculation of the rendered dependencies. - */ - public function doTestCalculateDependencies() { - $view = View::load('test_entity_area'); - - $dependencies = $view->calculateDependencies()->getDependencies(); - // Ensure that both config and content entity dependencies are calculated. - $this->assertEqual([ - 'config' => ['block.block.test_block'], - 'content' => ['entity_test:entity_test:aa0c61cb-b7bb-4795-972a-493dabcf529c'], - ], $dependencies); - } - -} diff --git a/core/modules/views/src/Tests/Handler/AreaMessagesTest.php b/core/modules/views/src/Tests/Handler/AreaMessagesTest.php deleted file mode 100644 index c17deb1..0000000 --- a/core/modules/views/src/Tests/Handler/AreaMessagesTest.php +++ /dev/null @@ -1,44 +0,0 @@ -setDisplay('default'); - $this->executeView($view); - $output = $view->render(); - $output = \Drupal::service('renderer')->renderRoot($output); - $this->setRawContent($output); - $this->assertText('My drupal set message.'); - } - -} diff --git a/core/modules/views/src/Tests/Handler/AreaTextTest.php b/core/modules/views/src/Tests/Handler/AreaTextTest.php deleted file mode 100644 index b0723d1..0000000 --- a/core/modules/views/src/Tests/Handler/AreaTextTest.php +++ /dev/null @@ -1,75 +0,0 @@ -installConfig(array('system', 'filter')); - $this->installEntitySchema('user'); - } - - public function testAreaText() { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = $this->container->get('renderer'); - $view = Views::getView('test_view'); - $view->setDisplay(); - - // add a text header - $string = $this->randomMachineName(); - $view->displayHandlers->get('default')->overrideOption('header', array( - 'area' => array( - 'id' => 'area', - 'table' => 'views', - 'field' => 'area', - 'content' => array( - 'value' => $string, - ), - ), - )); - - // Execute the view. - $this->executeView($view); - - $view->display_handler->handlers['header']['area']->options['content']['format'] = $this->randomString(); - $build = $view->display_handler->handlers['header']['area']->render(); - $this->assertEqual('', $renderer->renderRoot($build), 'Nonexistent format should return empty markup.'); - - $view->display_handler->handlers['header']['area']->options['content']['format'] = filter_default_format(); - $build = $view->display_handler->handlers['header']['area']->render(); - $this->assertEqual(check_markup($string), $renderer->renderRoot($build), 'Existent format should return something'); - - // Empty results, and it shouldn't be displayed . - $this->assertEqual(array(), $view->display_handler->handlers['header']['area']->render(TRUE), 'No result should lead to no header'); - // Empty results, and it should be displayed. - $view->display_handler->handlers['header']['area']->options['empty'] = TRUE; - $build = $view->display_handler->handlers['header']['area']->render(TRUE); - $this->assertEqual(check_markup($string), $renderer->renderRoot($build), 'No result, but empty enabled lead to a full header'); - } - -} diff --git a/core/modules/views/src/Tests/Handler/AreaTitleTest.php b/core/modules/views/src/Tests/Handler/AreaTitleTest.php deleted file mode 100644 index 06641a9..0000000 --- a/core/modules/views/src/Tests/Handler/AreaTitleTest.php +++ /dev/null @@ -1,61 +0,0 @@ -setDisplay('default'); - $this->executeView($view); - $view->render(); - $this->assertFalse($view->getTitle(), 'The title area does not override the title if the view is not empty.'); - $view->destroy(); - - $view->setDisplay('default'); - $this->executeView($view); - $view->result = array(); - $view->render(); - $this->assertEqual($view->getTitle(), 'test_title_empty', 'The title area should override the title if the result is empty.'); - $view->destroy(); - - $view->setDisplay('page_1'); - $this->executeView($view); - $view->render(); - $this->assertEqual($view->getTitle(), 'test_title_header', 'The title area on the header should override the title if the result is not empty.'); - $view->destroy(); - - $view->setDisplay('page_1'); - $this->executeView($view); - $view->result = array(); - $view->render(); - $this->assertEqual($view->getTitle(), 'test_title_empty', 'The title area should override the title if the result is empty.'); - $view->destroy(); - } - -} diff --git a/core/modules/views/src/Tests/Handler/AreaViewTest.php b/core/modules/views/src/Tests/Handler/AreaViewTest.php deleted file mode 100644 index 779b795..0000000 --- a/core/modules/views/src/Tests/Handler/AreaViewTest.php +++ /dev/null @@ -1,61 +0,0 @@ -container->get('renderer'); - $view = Views::getView('test_area_view'); - - // Tests \Drupal\views\Plugin\views\area\View::calculateDependencies(). - $this->assertIdentical(['config' => ['views.view.test_simple_argument']], $view->getDependencies()); - - $this->executeView($view); - $output = $view->render(); - $output = $renderer->renderRoot($output); - $this->assertTrue(strpos($output, 'js-view-dom-id-' . $view->dom_id) !== FALSE, 'The test view is correctly embedded.'); - $view->destroy(); - - $view->setArguments(array(27)); - $this->executeView($view); - $output = $view->render(); - $output = $renderer->renderRoot($output); - $this->assertTrue(strpos($output, 'John') === FALSE, 'The test view is correctly embedded with inherited arguments.'); - $this->assertTrue(strpos($output, 'George') !== FALSE, 'The test view is correctly embedded with inherited arguments.'); - $view->destroy(); - } - -} diff --git a/core/modules/views/src/Tests/Handler/ArgumentDateTest.php b/core/modules/views/src/Tests/Handler/ArgumentDateTest.php deleted file mode 100644 index d0939b1..0000000 --- a/core/modules/views/src/Tests/Handler/ArgumentDateTest.php +++ /dev/null @@ -1,323 +0,0 @@ - 'id', - ); - - /** - * {@inheritdoc} - */ - public function viewsData() { - $data = parent::viewsData(); - - $date_plugins = array( - 'date_fulldate', - 'date_day', - 'date_month', - 'date_week', - 'date_year', - 'date_year_month', - ); - foreach ($date_plugins as $plugin_id) { - $data['views_test_data'][$plugin_id] = $data['views_test_data']['created']; - $data['views_test_data'][$plugin_id]['real field'] = 'created'; - $data['views_test_data'][$plugin_id]['argument']['id'] = $plugin_id; - } - return $data; - } - - /** - * Tests the CreatedFullDate handler. - * - * @see \Drupal\node\Plugin\views\argument\CreatedFullDate - */ - public function testCreatedFullDateHandler() { - $view = Views::getView('test_argument_date'); - $view->setDisplay('default'); - $this->executeView($view, array('20000102')); - $expected = array(); - $expected[] = array('id' => 2); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('default'); - $this->executeView($view, array('20000101')); - $expected = array(); - $expected[] = array('id' => 1); - $expected[] = array('id' => 3); - $expected[] = array('id' => 4); - $expected[] = array('id' => 5); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('default'); - $this->executeView($view, array('20001023')); - $expected = array(); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - } - - /** - * Tests the Day handler. - * - * @see \Drupal\node\Plugin\views\argument\CreatedDay - */ - public function testDayHandler() { - $view = Views::getView('test_argument_date'); - $view->setDisplay('embed_1'); - $this->executeView($view, array('02')); - $expected = array(); - $expected[] = array('id' => 2); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_1'); - $this->executeView($view, array('01')); - $expected = array(); - $expected[] = array('id' => 1); - $expected[] = array('id' => 3); - $expected[] = array('id' => 4); - $expected[] = array('id' => 5); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_1'); - $this->executeView($view, array('23')); - $expected = array(); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - } - - /** - * Tests the Month handler. - * - * @see \Drupal\node\Plugin\views\argument\CreatedMonth - */ - public function testMonthHandler() { - $view = Views::getView('test_argument_date'); - $view->setDisplay('embed_2'); - $this->executeView($view, array('01')); - $expected = array(); - $expected[] = array('id' => 1); - $expected[] = array('id' => 2); - $expected[] = array('id' => 3); - $expected[] = array('id' => 4); - $expected[] = array('id' => 5); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_2'); - $this->executeView($view, array('12')); - $expected = array(); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - } - - /** - * Tests the Week handler. - * - * @see \Drupal\node\Plugin\views\argument\CreatedWeek - */ - public function testWeekHandler() { - $this->container->get('database')->update('views_test_data') - ->fields(array('created' => gmmktime(0, 0, 0, 9, 26, 2008))) - ->condition('id', 1) - ->execute(); - - $this->container->get('database')->update('views_test_data') - ->fields(array('created' => gmmktime(0, 0, 0, 2, 29, 2004))) - ->condition('id', 2) - ->execute(); - - $this->container->get('database')->update('views_test_data') - ->fields(array('created' => gmmktime(0, 0, 0, 1, 1, 2000))) - ->condition('id', 3) - ->execute(); - - $this->container->get('database')->update('views_test_data') - ->fields(array('created' => gmmktime(0, 0, 0, 1, 10, 2000))) - ->condition('id', 4) - ->execute(); - - $this->container->get('database')->update('views_test_data') - ->fields(array('created' => gmmktime(0, 0, 0, 2, 1, 2000))) - ->condition('id', 5) - ->execute(); - - $view = Views::getView('test_argument_date'); - $view->setDisplay('embed_3'); - // Check the week calculation for a leap year. - // @see http://en.wikipedia.org/wiki/ISO_week_date#Calculation - $this->executeView($view, array('39')); - $expected = array(); - $expected[] = array('id' => 1); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_3'); - // Check the week calculation for the 29th of February in a leap year. - // @see http://en.wikipedia.org/wiki/ISO_week_date#Calculation - $this->executeView($view, array('09')); - $expected = array(); - $expected[] = array('id' => 2); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_3'); - // The first jan 2000 was still in the last week of the previous year. - $this->executeView($view, array('52')); - $expected = array(); - $expected[] = array('id' => 3); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_3'); - $this->executeView($view, array('02')); - $expected = array(); - $expected[] = array('id' => 4); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_3'); - $this->executeView($view, array('05')); - $expected = array(); - $expected[] = array('id' => 5); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_3'); - $this->executeView($view, array('23')); - $expected = array(); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - } - - /** - * Tests the Year handler. - * - * @see \Drupal\node\Plugin\views\argument\CreatedYear - */ - public function testYearHandler() { - $this->container->get('database')->update('views_test_data') - ->fields(array('created' => gmmktime(0, 0, 0, 1, 1, 2001))) - ->condition('id', 3) - ->execute(); - - $this->container->get('database')->update('views_test_data') - ->fields(array('created' => gmmktime(0, 0, 0, 1, 1, 2002))) - ->condition('id', 4) - ->execute(); - - $this->container->get('database')->update('views_test_data') - ->fields(array('created' => gmmktime(0, 0, 0, 1, 1, 2002))) - ->condition('id', 5) - ->execute(); - - $view = Views::getView('test_argument_date'); - $view->setDisplay('embed_4'); - $this->executeView($view, array('2000')); - $expected = array(); - $expected[] = array('id' => 1); - $expected[] = array('id' => 2); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_4'); - $this->executeView($view, array('2001')); - $expected = array(); - $expected[] = array('id' => 3); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_4'); - $this->executeView($view, array('2002')); - $expected = array(); - $expected[] = array('id' => 4); - $expected[] = array('id' => 5); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_4'); - $this->executeView($view, array('23')); - $expected = array(); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - } - - /** - * Tests the YearMonth handler. - * - * @see \Drupal\node\Plugin\views\argument\CreatedYearMonth - */ - public function testYearMonthHandler() { - $this->container->get('database')->update('views_test_data') - ->fields(array('created' => gmmktime(0, 0, 0, 1, 1, 2001))) - ->condition('id', 3) - ->execute(); - - $this->container->get('database')->update('views_test_data') - ->fields(array('created' => gmmktime(0, 0, 0, 4, 1, 2001))) - ->condition('id', 4) - ->execute(); - - $this->container->get('database')->update('views_test_data') - ->fields(array('created' => gmmktime(0, 0, 0, 4, 1, 2001))) - ->condition('id', 5) - ->execute(); - - $view = Views::getView('test_argument_date'); - $view->setDisplay('embed_5'); - $this->executeView($view, array('200001')); - $expected = array(); - $expected[] = array('id' => 1); - $expected[] = array('id' => 2); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_5'); - $this->executeView($view, array('200101')); - $expected = array(); - $expected[] = array('id' => 3); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_5'); - $this->executeView($view, array('200104')); - $expected = array(); - $expected[] = array('id' => 4); - $expected[] = array('id' => 5); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - $view->destroy(); - - $view->setDisplay('embed_5'); - $this->executeView($view, array('201301')); - $expected = array(); - $this->assertIdenticalResultset($view, $expected, $this->columnMap); - } -} diff --git a/core/modules/views/src/Tests/Handler/ArgumentNullTest.php b/core/modules/views/src/Tests/Handler/ArgumentNullTest.php deleted file mode 100644 index 6f8be53..0000000 --- a/core/modules/views/src/Tests/Handler/ArgumentNullTest.php +++ /dev/null @@ -1,79 +0,0 @@ -setDisplay(); - - // Add a null argument. - $view->displayHandlers->get('default')->overrideOption('arguments', array( - 'null' => array( - 'id' => 'null', - 'table' => 'views', - 'field' => 'null', - ), - )); - - $this->executeView($view); - - // Make sure that the argument is not validated yet. - unset($view->argument['null']->argument_validated); - $this->assertTrue($view->argument['null']->validateArgument(26)); - // test must_not_be option. - unset($view->argument['null']->argument_validated); - $view->argument['null']->options['must_not_be'] = TRUE; - $this->assertFalse($view->argument['null']->validateArgument(26), 'must_not_be returns FALSE, if there is an argument'); - unset($view->argument['null']->argument_validated); - $this->assertTrue($view->argument['null']->validateArgument(NULL), 'must_not_be returns TRUE, if there is no argument'); - - // Test execution. - $view->destroy(); - $view->setDisplay(); - - // Add a argument, which has null as handler. - $view->displayHandlers->get('default')->overrideOption('arguments', array( - 'id' => array( - 'id' => 'id', - 'table' => 'views_test_data', - 'field' => 'id', - ), - )); - - $this->executeView($view, array(26)); - - // The argument should be ignored, so every result should return. - $this->assertEqual(5, count($view->result)); - } - -} diff --git a/core/modules/views/src/Tests/Handler/EntityTestViewsFieldAccessTest.php b/core/modules/views/src/Tests/Handler/EntityTestViewsFieldAccessTest.php deleted file mode 100644 index 1e2704b..0000000 --- a/core/modules/views/src/Tests/Handler/EntityTestViewsFieldAccessTest.php +++ /dev/null @@ -1,45 +0,0 @@ -installEntitySchema('entity_test'); - } - - public function testEntityTestFields() { - $entity_test = EntityTest::create([ - 'name' => 'test entity name', - ]); - $entity_test->save(); - - // @todo Expand the test coverage in https://www.drupal.org/node/2464635 - - $this->assertFieldAccess('entity_test', 'id', $entity_test->id()); - $this->assertFieldAccess('entity_test', 'langcode', $entity_test->language()->getName()); - $this->assertFieldAccess('entity_test', 'name', $entity_test->getName()); - } - -} diff --git a/core/modules/views/src/Tests/Handler/FieldBooleanTest.php b/core/modules/views/src/Tests/Handler/FieldBooleanTest.php deleted file mode 100644 index da32482..0000000 --- a/core/modules/views/src/Tests/Handler/FieldBooleanTest.php +++ /dev/null @@ -1,84 +0,0 @@ -setDisplay(); - - $view->displayHandlers->get('default')->overrideOption('fields', array( - 'age' => array( - 'id' => 'age', - 'table' => 'views_test_data', - 'field' => 'age', - 'relationship' => 'none', - ), - )); - - $this->executeView($view); - - // This is john, which has no age, there are no custom formats defined, yet. - $this->assertEqual(t('No'), $view->field['age']->advancedRender($view->result[0])); - $this->assertEqual(t('Yes'), $view->field['age']->advancedRender($view->result[1])); - - // Reverse the output. - $view->field['age']->options['not'] = TRUE; - $this->assertEqual(t('Yes'), $view->field['age']->advancedRender($view->result[0])); - $this->assertEqual(t('No'), $view->field['age']->advancedRender($view->result[1])); - - unset($view->field['age']->options['not']); - - // Use another output format. - $view->field['age']->options['type'] = 'true-false'; - $this->assertEqual(t('False'), $view->field['age']->advancedRender($view->result[0])); - $this->assertEqual(t('True'), $view->field['age']->advancedRender($view->result[1])); - - // test awesome unicode. - $view->field['age']->options['type'] = 'unicode-yes-no'; - $this->assertEqual('✖', $view->field['age']->advancedRender($view->result[0])); - $this->assertEqual('✔', $view->field['age']->advancedRender($view->result[1])); - - // Set a custom output format. - $view->field['age']->formats['test'] = array(t('Test-True'), t('Test-False')); - $view->field['age']->options['type'] = 'test'; - $this->assertEqual(t('Test-False'), $view->field['age']->advancedRender($view->result[0])); - $this->assertEqual(t('Test-True'), $view->field['age']->advancedRender($view->result[1])); - } - -} diff --git a/core/modules/views/src/Tests/Handler/FieldCounterTest.php b/core/modules/views/src/Tests/Handler/FieldCounterTest.php deleted file mode 100644 index 4561ad6..0000000 --- a/core/modules/views/src/Tests/Handler/FieldCounterTest.php +++ /dev/null @@ -1,96 +0,0 @@ -setDisplay(); - $view->displayHandlers->get('default')->overrideOption('fields', array( - 'counter' => array( - 'id' => 'counter', - 'table' => 'views', - 'field' => 'counter', - 'relationship' => 'none', - ), - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - ), - )); - $view->preview(); - - $counter = $view->style_plugin->getField(0, 'counter'); - $this->assertEqual($counter, '1', format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 1, '@counter' => $counter))); - $counter = $view->style_plugin->getField(1, 'counter'); - $this->assertEqual($counter, '2', format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 2, '@counter' => $counter))); - $counter = $view->style_plugin->getField(2, 'counter'); - $this->assertEqual($counter, '3', format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 3, '@counter' => $counter))); - $view->destroy(); - $view->storage->invalidateCaches(); - - $view->setDisplay(); - $rand_start = rand(5, 10); - $view->displayHandlers->get('default')->overrideOption('fields', array( - 'counter' => array( - 'id' => 'counter', - 'table' => 'views', - 'field' => 'counter', - 'relationship' => 'none', - 'counter_start' => $rand_start - ), - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - ), - )); - $view->preview(); - - $counter = $view->style_plugin->getField(0, 'counter'); - $expected_number = 0 + $rand_start; - $this->assertEqual($counter, (string) $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); - $counter = $view->style_plugin->getField(1, 'counter'); - $expected_number = 1 + $rand_start; - $this->assertEqual($counter, (string) $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); - $counter = $view->style_plugin->getField(2, 'counter'); - $expected_number = 2 + $rand_start; - $this->assertEqual($counter, (string) $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); - } - - // @TODO: Write tests for pager. - function testPager() { - } - -} diff --git a/core/modules/views/src/Tests/Handler/FieldCustomTest.php b/core/modules/views/src/Tests/Handler/FieldCustomTest.php deleted file mode 100644 index cc3d92b..0000000 --- a/core/modules/views/src/Tests/Handler/FieldCustomTest.php +++ /dev/null @@ -1,123 +0,0 @@ -setDisplay(); - - // Alter the text of the field to a random string. - $random = '
                          ' . $this->randomMachineName() . '
                          '; - $view->displayHandlers->get('default')->overrideOption('fields', array( - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'alter' => array( - 'text' => $random, - ), - ), - )); - - $this->executeView($view); - - $this->assertEqual($random, $view->style_plugin->getField(0, 'name')); - } - - /** - * Ensure that custom fields can use tokens. - */ - public function testFieldCustomTokens() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - $view->displayHandlers->get('default')->overrideOption('fields', [ - 'age' => [ - 'id' => 'age', - 'exclude' => TRUE, - 'table' => 'views_test_data', - 'field' => 'age', - ], - 'name' => [ - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'alter' => [ - 'text' => 'Amount of kittens: {{ age }}', - ], - ], - ]); - - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = \Drupal::service('renderer'); - $preview = $view->preview(); - $output = $renderer->renderRoot($preview); - - $expected_text = 'Amount of kittens: ' . $view->style_plugin->getField(0, 'age'); - $this->assertTrue(strpos((string) $output, $expected_text), 'The views token has been successfully replaced.'); - } - - /** - * Ensure that custom field content is XSS filtered. - */ - public function testCustomFieldXss() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Alter the text of the field to include XSS. - $text = ''; - $view->displayHandlers->get('default')->overrideOption('fields', array( - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'alter' => array( - 'text' => $text, - ), - ), - )); - $this->executeView($view); - $this->assertEqual(Xss::filter($text), $view->style_plugin->getField(0, 'name')); - } - -} diff --git a/core/modules/views/src/Tests/Handler/FieldDateTest.php b/core/modules/views/src/Tests/Handler/FieldDateTest.php deleted file mode 100644 index 004118f..0000000 --- a/core/modules/views/src/Tests/Handler/FieldDateTest.php +++ /dev/null @@ -1,198 +0,0 @@ - "The destruction date of this record", - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => FALSE, - 'default' => 0, - ); - return $schema; - } - - /** - * {@inheritdoc} - */ - public function viewsData() { - $data = parent::viewsData(); - $data['views_test_data']['created']['field']['id'] = 'date'; - $data['views_test_data']['destroyed'] = array( - 'title' => 'Destroyed', - 'help' => 'Date in future this will be destroyed.', - 'field' => array('id' => 'date'), - 'argument' => array('id' => 'date'), - 'filter' => array('id' => 'date'), - 'sort' => array('id' => 'date'), - ); - return $data; - } - - /** - * {@inheritdoc} - */ - public function dataSet() { - $datas = parent::dataSet(); - foreach ($datas as $i => $data) { - $datas[$i]['destroyed'] = gmmktime(0, 0, 0, 1, 1, 2050); - } - return $datas; - } - - /** - * Sets up functional test of the views date field. - */ - public function testFieldDate() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - $view->displayHandlers->get('default')->overrideOption('fields', array( - 'created' => array( - 'id' => 'created', - 'table' => 'views_test_data', - 'field' => 'created', - 'relationship' => 'none', - // ISO 8601 format @see http://php.net/manual/en/function.date.php - 'custom_date_format' => 'c', - ), - 'destroyed' => array( - 'id' => 'destroyed', - 'table' => 'views_test_data', - 'field' => 'destroyed', - 'relationship' => 'none', - 'custom_date_format' => 'c', - ), - )); - $time = gmmktime(0, 0, 0, 1, 1, 2000); - - $this->executeView($view); - - $timezones = array( - NULL, - 'UTC', - 'America/New_York', - ); - - // Check each date/time in various timezones. - foreach ($timezones as $timezone) { - $dates = array( - 'short' => format_date($time, 'short', '', $timezone), - 'medium' => format_date($time, 'medium', '', $timezone), - 'long' => format_date($time, 'long', '', $timezone), - 'custom' => format_date($time, 'custom', 'c', $timezone), - 'fallback' => format_date($time, 'fallback', '', $timezone), - 'html_date' => format_date($time, 'html_date', '', $timezone), - 'html_datetime' => format_date($time, 'html_datetime', '', $timezone), - 'html_month' => format_date($time, 'html_month', '', $timezone), - 'html_time' => format_date($time, 'html_time', '', $timezone), - 'html_week' => format_date($time, 'html_week', '', $timezone), - 'html_year' => format_date($time, 'html_year', '', $timezone), - 'html_yearless_date' => format_date($time, 'html_yearless_date', '', $timezone), - ); - $this->assertRenderedDatesEqual($view, $dates, $timezone); - } - - // Check times in the past. - $time_since = $this->container->get('date.formatter')->formatTimeDiffSince($time); - $intervals = array( - 'raw time ago' => $time_since, - 'time ago' => t('%time ago', array('%time' => $time_since)), - 'raw time span' => $time_since, - 'inverse time span' => -$time_since, - 'time span' => t('%time ago', array('%time' => $time_since)), - ); - $this->assertRenderedDatesEqual($view, $intervals); - - // Check times in the future. - $time = gmmktime(0, 0, 0, 1, 1, 2050); - $formatted = $this->container->get('date.formatter')->formatTimeDiffUntil($time); - $intervals = array( - 'raw time span' => -$formatted, - 'time span' => t('%time hence', array( - '%time' => $formatted, - )), - ); - $this->assertRenderedFutureDatesEqual($view, $intervals); - } - - /** - * Asserts properly formatted display against 'created' field in view. - * - * @param mixed $view - * View to be tested. - * @param array $map - * Data map. - * @param null $timezone - * Optional timezone. - */ - protected function assertRenderedDatesEqual($view, $map, $timezone = NULL) { - foreach ($map as $date_format => $expected_result) { - $view->field['created']->options['date_format'] = $date_format; - $t_args = array( - '%value' => $expected_result, - '%format' => $date_format, - ); - if (isset($timezone)) { - $t_args['%timezone'] = $timezone; - $message = t('Value %value in %format format for timezone %timezone matches.', $t_args); - $view->field['created']->options['timezone'] = $timezone; - } - else { - $message = t('Value %value in %format format matches.', $t_args); - } - $actual_result = $view->field['created']->advancedRender($view->result[0]); - $this->assertEqual($expected_result, $actual_result, $message); - } - } - - /** - * Asserts properly formatted display against 'destroyed' field in view. - * - * @param mixed $view - * View to be tested. - * @param array $map - * Data map. - */ - protected function assertRenderedFutureDatesEqual($view, $map) { - foreach ($map as $format => $result) { - $view->field['destroyed']->options['date_format'] = $format; - $view_result = $view->field['destroyed']->advancedRender($view->result[0]); - $t_args = array( - '%value' => $result, - '%format' => $format, - '%actual' => $view_result, - ); - $message = t('Value %value in %format matches %actual', $t_args); - $this->assertEqual($view_result, $result, $message); - } - } - -} diff --git a/core/modules/views/src/Tests/Handler/FieldEntityLinkTest.php b/core/modules/views/src/Tests/Handler/FieldEntityLinkTest.php deleted file mode 100644 index 0ab8409..0000000 --- a/core/modules/views/src/Tests/Handler/FieldEntityLinkTest.php +++ /dev/null @@ -1,131 +0,0 @@ -installEntitySchema('user'); - $this->installEntitySchema('entity_test'); - $this->installConfig(['user']); - - // Create some test entities. - for ($i = 0; $i < 5; $i++) { - EntityTest::create(['name' => $this->randomString()])->save(); - } - - // Create and admin user. - $this->adminUser = $this->createUser(['view test entity'], FALSE, TRUE); - - Role::load(AccountInterface::ANONYMOUS_ROLE) - ->grantPermission('view test entity') - ->save(); - } - - /** - * Tests entity link fields. - */ - public function testEntityLink() { - // Anonymous users cannot see edit/delete links. - $expected_results = ['canonical' => TRUE, 'edit-form' => FALSE, 'delete-form' => FALSE]; - $this->doTestEntityLink(\Drupal::currentUser(), $expected_results); - - // Admin users cannot see all links. - $expected_results = ['canonical' => TRUE, 'edit-form' => TRUE, 'delete-form' => TRUE]; - $this->doTestEntityLink($this->adminUser, $expected_results); - } - - /** - * Tests whether entity links behave as expected. - * - * @param \Drupal\Core\Session\AccountInterface $account - * The user account to be used to run the test; - * @param bool[] $expected_results - * An associative array of expected results keyed by link template name. - */ - protected function doTestEntityLink(AccountInterface $account, $expected_results) { - \Drupal::currentUser()->setAccount($account); - - $view = Views::getView('test_entity_test_link'); - $view->preview(); - - $info = [ - 'canonical' => [ - 'label' => 'View entity test', - 'field_id' => 'view_entity_test', - 'destination' => FALSE, - ], - 'edit-form' => [ - 'label' => 'Edit entity test', - 'field_id' => 'edit_entity_test', - 'destination' => TRUE, - ], - 'delete-form' => [ - 'label' => 'Delete entity test', - 'field_id' => 'delete_entity_test', - 'destination' => TRUE, - ], - ]; - - $index = 0; - foreach (EntityTest::loadMultiple() as $entity) { - foreach ($expected_results as $template => $expected_result) { - $expected_link = ''; - if ($expected_result) { - $path = $entity->url($template); - $destination = $info[$template]['destination'] ? '?destination=/' : ''; - $expected_link = '' . $info[$template]['label'] . ''; - } - $link = $view->style_plugin->getField($index, $info[$template]['field_id']); - $this->assertEqual($link, $expected_link); - } - $index++; - } - } - -} diff --git a/core/modules/views/src/Tests/Handler/FieldFieldAccessTestBase.php b/core/modules/views/src/Tests/Handler/FieldFieldAccessTestBase.php deleted file mode 100644 index 48bd00c..0000000 --- a/core/modules/views/src/Tests/Handler/FieldFieldAccessTestBase.php +++ /dev/null @@ -1,144 +0,0 @@ -installEntitySchema('user'); - - $role_with_access = Role::create([ - 'id' => 'with_access', - 'permissions' => ['view test entity field'], - ]); - $role_with_access->save(); - $role_without_access = Role::create([ - 'id' => 'without_access', - 'permissions' => [], - ]); - $role_without_access->save(); - - $this->userWithAccess = User::create([ - 'name' => $this->randomMachineName(), - 'roles' => [$role_with_access->id()], - ]); - $this->userWithAccess->save(); - $this->userWithoutAccess = User::create([ - 'name' => $this->randomMachineName(), - 'roles' => [$role_without_access->id()], - ]); - $this->userWithoutAccess->save(); - } - - /** - * Checks views field access for a given entity type and field name. - * - * To use this method, set up an entity of type $entity_type_id, with field - * $field_name. Create an entity instance that contains content $field_content - * in that field. - * - * This method will check that a user with permission can see the content in a - * view, and a user without access permission on that field cannot. - * - * @param string $entity_type_id - * The entity type ID. - * @param string $field_name - * The field name. - * @param string $field_content - * The expected field content. - */ - protected function assertFieldAccess($entity_type_id, $field_name, $field_content) { - \Drupal::state()->set('views_field_access_test-field', $field_name); - - $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id); - $view_id = $this->randomMachineName(); - $data_table = $entity_type->getDataTable(); - // Use the data table as long as the field is not 'uuid'. This is the only - // column that can only be obtained from the base table. - $base_table = ($data_table && ($field_name !== 'uuid')) ? $data_table : $entity_type->getBaseTable(); - $entity = View::create([ - 'id' => $view_id, - 'base_table' => $base_table, - 'display' => [ - 'default' => [ - 'display_plugin' => 'default', - 'id' => 'default', - 'display_options' => [ - 'fields' => [ - $field_name => [ - 'table' => $base_table, - 'field' => $field_name, - 'id' => $field_name, - 'plugin_id' => 'field', - ], - ], - ], - ], - ], - ]); - $entity->save(); - - /** @var \Drupal\Core\Session\AccountSwitcherInterface $account_switcher */ - $account_switcher = \Drupal::service('account_switcher'); - - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = \Drupal::service('renderer'); - - $account_switcher->switchTo($this->userWithAccess); - $executable = Views::getView($view_id); - $build = $executable->preview(); - $this->setRawContent($renderer->renderRoot($build)); - - $this->assertText($field_content); - $this->assertTrue(isset($executable->field[$field_name])); - - $account_switcher->switchTo($this->userWithoutAccess); - $executable = Views::getView($view_id); - $build = $executable->preview(); - $this->setRawContent($renderer->renderRoot($build)); - - $this->assertNoText($field_content); - $this->assertFalse(isset($executable->field[$field_name])); - - \Drupal::state()->delete('views_field_access_test-field'); - } - -} diff --git a/core/modules/views/src/Tests/Handler/FieldFieldTest.php b/core/modules/views/src/Tests/Handler/FieldFieldTest.php deleted file mode 100644 index aa2d1df..0000000 --- a/core/modules/views/src/Tests/Handler/FieldFieldTest.php +++ /dev/null @@ -1,554 +0,0 @@ -installEntitySchema('entity_test'); - $this->installEntitySchema('user'); - $this->installEntitySchema('entity_test_rev'); - - // Bypass any field access. - $this->adminUser = User::create(['name' => $this->randomString()]); - $this->adminUser->save(); - $this->container->get('current_user')->setAccount($this->adminUser); - - $this->testUsers = []; - for ($i = 0; $i < 5; $i++) { - $this->testUsers[$i] = User::create([ - 'name' => 'test ' . $i, - 'timezone' => User::getAllowedTimezones()[$i], - 'created' => REQUEST_TIME - rand(0, 3600) - ]); - $this->testUsers[$i]->save(); - } - - // Setup a field storage and field, but also change the views data for the - // entity_test entity type. - $field_storage = FieldStorageConfig::create([ - 'field_name' => 'field_test', - 'type' => 'integer', - 'entity_type' => 'entity_test', - ]); - $field_storage->save(); - - $field = FieldConfig::create([ - 'field_name' => 'field_test', - 'entity_type' => 'entity_test', - 'bundle' => 'entity_test', - ]); - $field->save(); - - $field_storage_multiple = FieldStorageConfig::create([ - 'field_name' => 'field_test_multiple', - 'type' => 'integer', - 'entity_type' => 'entity_test', - 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, - ]); - $field_storage_multiple->save(); - - $field_multiple = FieldConfig::create([ - 'field_name' => 'field_test_multiple', - 'entity_type' => 'entity_test', - 'bundle' => 'entity_test', - ]); - $field_multiple->save(); - - $random_number = (string) 30856; - $random_number_multiple = (string) 1370359990; - for ($i = 0; $i < 5; $i++) { - $this->entities[$i] = $entity = EntityTest::create([ - 'bundle' => 'entity_test', - 'name' => 'test ' . $i, - 'field_test' => $random_number[$i], - 'field_test_multiple' => [$random_number_multiple[$i * 2], $random_number_multiple[$i * 2 + 1]], - 'user_id' => $this->testUsers[$i]->id(), - ]); - $entity->save(); - } - - // Setup some test data for entities with revisions. - // We are testing both base field revisions and field config revisions. - $field_storage = FieldStorageConfig::create([ - 'field_name' => 'field_test', - 'type' => 'integer', - 'entity_type' => 'entity_test_rev', - ]); - $field_storage->save(); - - $field = FieldConfig::create([ - 'field_name' => 'field_test', - 'entity_type' => 'entity_test_rev', - 'bundle' => 'entity_test_rev', - ]); - $field->save(); - - $field_storage_multiple = FieldStorageConfig::create([ - 'field_name' => 'field_test_multiple', - 'type' => 'integer', - 'entity_type' => 'entity_test_rev', - 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, - ]); - $field_storage_multiple->save(); - - $field_multiple = FieldConfig::create([ - 'field_name' => 'field_test_multiple', - 'entity_type' => 'entity_test_rev', - 'bundle' => 'entity_test_rev', - ]); - $field_multiple->save(); - - $this->entityRevision = []; - $this->entityRevision[0] = $entity = EntityTestRev::create([ - 'name' => 'base value', - 'field_test' => 1, - 'field_test_multiple' => [1, 3, 7], - 'user_id' => $this->testUsers[0]->id(), - ]); - $entity->save(); - $original_entity = clone $entity; - - $entity = clone $original_entity; - $entity->setNewRevision(TRUE); - $entity->name->value = 'revision value1'; - $entity->field_test->value = 2; - $entity->field_test_multiple[0]->value = 0; - $entity->field_test_multiple[1]->value = 3; - $entity->field_test_multiple[2]->value = 5; - $entity->user_id->target_id = $this->testUsers[1]->id(); - $entity->save(); - $this->entityRevision[1] = $entity; - - $entity = clone $original_entity; - $entity->setNewRevision(TRUE); - $entity->name->value = 'revision value2'; - $entity->field_test->value = 3; - $entity->field_test_multiple[0]->value = 9; - $entity->field_test_multiple[1]->value = 9; - $entity->field_test_multiple[2]->value = 9; - $entity->user_id->target_id = $this->testUsers[2]->id(); - $entity->save(); - $this->entityRevision[2] = $entity; - - $this->entityRevision[3] = $entity = EntityTestRev::create([ - 'name' => 'next entity value', - 'field_test' => 4, - 'field_test_multiple' => [2, 9, 9], - 'user_id' => $this->testUsers[3]->id(), - ]); - $entity->save(); - - \Drupal::state()->set('entity_test.views_data', [ - 'entity_test' => [ - 'id' => [ - 'field' => [ - 'id' => 'field', - ], - ], - ], - 'entity_test_rev_revision' => [ - 'id' => [ - 'field' => [ - 'id' => 'field', - ], - ], - ], - ]); - - Views::viewsData()->clear(); - } - - /** - * Tests the result of a view with base fields and configurable fields. - */ - public function testSimpleExecute() { - $executable = Views::getView('test_field_field_test'); - $executable->execute(); - - $this->assertTrue($executable->field['id'] instanceof Field); - $this->assertTrue($executable->field['field_test'] instanceof Field); - - $this->assertIdenticalResultset($executable, - [ - ['id' => 1, 'field_test' => 3], - ['id' => 2, 'field_test' => 0], - ['id' => 3, 'field_test' => 8], - ['id' => 4, 'field_test' => 5], - ['id' => 5, 'field_test' => 6], - ], - ['id' => 'id', 'field_test' => 'field_test'] - ); - } - - /** - * Tests the output of a view with base fields and configurable fields. - */ - public function testSimpleRender() { - $executable = Views::getView('test_field_field_test'); - $executable->execute(); - - $this->assertEqual('1', $executable->getStyle()->getField(0, 'id')); - $this->assertEqual('3', $executable->getStyle()->getField(0, 'field_test')); - $this->assertEqual('2', $executable->getStyle()->getField(1, 'id')); - // @todo Switch this assertion to assertIdentical('', ...) when - // https://www.drupal.org/node/2488006 gets fixed. - $this->assertEqual('0', $executable->getStyle()->getField(1, 'field_test')); - $this->assertEqual('3', $executable->getStyle()->getField(2, 'id')); - $this->assertEqual('8', $executable->getStyle()->getField(2, 'field_test')); - $this->assertEqual('4', $executable->getStyle()->getField(3, 'id')); - $this->assertEqual('5', $executable->getStyle()->getField(3, 'field_test')); - $this->assertEqual('5', $executable->getStyle()->getField(4, 'id')); - $this->assertEqual('6', $executable->getStyle()->getField(4, 'field_test')); - } - - /** - * Tests that formatter's #attached assets are correctly preserved. - * - * @see \Drupal\views_test_formatter\Plugin\Field\FieldFormatter\AttachmentTestFormatter::viewElements() - */ - public function testAttachedRender() { - $executable = Views::getView('test_field_field_attachment_test'); - $executable->execute(); - - // Check that the attachments added by AttachmentTestFormatter have been - // preserved in the render array. - $render = $executable->display_handler->render(); - $expected_attachments = [ - 'library' => [ - 'views/views.module' - ] - ]; - foreach ($this->entities as $entity) { - $expected_attachments['library'][] = 'foo/fake_library'; - $expected_attachments['drupalSettings']['AttachmentIntegerFormatter'][$entity->id()] = $entity->id(); - } - $this->assertEqual($expected_attachments, $render['#attached']); - } - - /** - * Tests the result of a view with complex field configuration. - * - * A complex field configuration contains multiple times the same field, with - * different delta limit / offset. - */ - public function testFieldAlias() { - $executable = Views::getView('test_field_alias_test'); - $executable->execute(); - - $this->assertTrue($executable->field['id'] instanceof Field); - $this->assertTrue($executable->field['name'] instanceof Field); - $this->assertTrue($executable->field['name_alias'] instanceof Field); - - $this->assertIdenticalResultset($executable, - [ - ['id' => 1, 'name' => 'test 0', 'name_alias' => 'test 0'], - ['id' => 2, 'name' => 'test 1', 'name_alias' => 'test 1'], - ['id' => 3, 'name' => 'test 2', 'name_alias' => 'test 2'], - ['id' => 4, 'name' => 'test 3', 'name_alias' => 'test 3'], - ['id' => 5, 'name' => 'test 4', 'name_alias' => 'test 4'], - ], - ['id' => 'id', 'name' => 'name', 'name_alias' => 'name_alias'] - ); - } - - /** - * Tests the result of a view with complex field configuration. - * - * A complex field configuration contains multiple times the same field, with - * different delta limit / offset. - */ - public function testFieldAliasRender() { - $executable = Views::getView('test_field_alias_test'); - $executable->execute(); - - for ($i = 0; $i < 5; $i++) { - $this->assertEqual((string) ($i + 1), $executable->getStyle()->getField($i, 'id')); - $this->assertEqual('test ' . $i, $executable->getStyle()->getField($i, 'name')); - $entity = EntityTest::load($i + 1); - $this->assertEqual('test ' . $i . '', (string) $executable->getStyle()->getField($i, 'name_alias')); - } - } - - /** - * Tests the result of a view with complex field configuration. - * - * A complex field configuration contains multiple times the same field, with - * different delta limit / offset. - */ - public function testComplexExecute() { - $executable = Views::getView('test_field_field_complex_test'); - $executable->execute(); - - $timezones = []; - foreach ($this->testUsers as $user) { - $timezones[] = $user->getTimeZone(); - } - - $this->assertTrue($executable->field['field_test_multiple'] instanceof Field); - $this->assertTrue($executable->field['field_test_multiple_1'] instanceof Field); - $this->assertTrue($executable->field['field_test_multiple_2'] instanceof Field); - $this->assertTrue($executable->field['timezone'] instanceof Field); - - $this->assertIdenticalResultset($executable, - [ - ['timezone' => $timezones[0], 'field_test_multiple' => [1, 3], 'field_test_multiple_1' => [1, 3], 'field_test_multiple_2' => [1, 3]], - ['timezone' => $timezones[1], 'field_test_multiple' => [7, 0], 'field_test_multiple_1' => [7, 0], 'field_test_multiple_2' => [7, 0]], - ['timezone' => $timezones[2], 'field_test_multiple' => [3, 5], 'field_test_multiple_1' => [3, 5], 'field_test_multiple_2' => [3, 5]], - ['timezone' => $timezones[3], 'field_test_multiple' => [9, 9], 'field_test_multiple_1' => [9, 9], 'field_test_multiple_2' => [9, 9]], - ['timezone' => $timezones[4], 'field_test_multiple' => [9, 0], 'field_test_multiple_1' => [9, 0], 'field_test_multiple_2' => [9, 0]], - ], - ['timezone' => 'timezone', 'field_test_multiple' => 'field_test_multiple', 'field_test_multiple_1' => 'field_test_multiple_1', 'field_test_multiple_2' => 'field_test_multiple_2'] - ); - } - - /** - * Tests the output of a view with complex field configuration. - */ - public function testComplexRender() { - $executable = Views::getView('test_field_field_complex_test'); - $executable->execute(); - $date_formatter = \Drupal::service('date.formatter'); - - $this->assertEqual($this->testUsers[0]->getTimeZone(), $executable->getStyle()->getField(0, 'timezone')); - $this->assertEqual("1, 3", $executable->getStyle()->getField(0, 'field_test_multiple')); - $this->assertEqual("1", $executable->getStyle()->getField(0, 'field_test_multiple_1')); - $this->assertEqual("3", $executable->getStyle()->getField(0, 'field_test_multiple_2')); - $this->assertEqual($date_formatter->format($this->testUsers[0]->getCreatedTime(), 'custom', 'Y'), $executable->getStyle()->getField(0, 'created')); - $this->assertEqual($date_formatter->format($this->testUsers[0]->getCreatedTime(), 'custom', 'H:i:s'), $executable->getStyle()->getField(0, 'created_1')); - $this->assertEqual($date_formatter->format($this->testUsers[0]->getCreatedTime(), 'fallback'), $executable->getStyle()->getField(0, 'created_2')); - - $this->assertEqual($this->testUsers[1]->getTimeZone(), $executable->getStyle()->getField(1, 'timezone')); - $this->assertEqual("7, 0", $executable->getStyle()->getField(1, 'field_test_multiple')); - $this->assertEqual("7", $executable->getStyle()->getField(1, 'field_test_multiple_1')); - $this->assertEqual("0", $executable->getStyle()->getField(1, 'field_test_multiple_2')); - $this->assertEqual($date_formatter->format($this->testUsers[1]->getCreatedTime(), 'custom', 'Y'), $executable->getStyle()->getField(1, 'created')); - $this->assertEqual($date_formatter->format($this->testUsers[1]->getCreatedTime(), 'custom', 'H:i:s'), $executable->getStyle()->getField(1, 'created_1')); - $this->assertEqual($date_formatter->format($this->testUsers[1]->getCreatedTime(), 'fallback'), $executable->getStyle()->getField(1, 'created_2')); - - $this->assertEqual($this->testUsers[2]->getTimeZone(), $executable->getStyle()->getField(2, 'timezone')); - $this->assertEqual("3, 5", $executable->getStyle()->getField(2, 'field_test_multiple')); - $this->assertEqual("3", $executable->getStyle()->getField(2, 'field_test_multiple_1')); - $this->assertEqual("5", $executable->getStyle()->getField(2, 'field_test_multiple_2')); - $this->assertEqual($date_formatter->format($this->testUsers[2]->getCreatedTime(), 'custom', 'Y'), $executable->getStyle()->getField(2, 'created')); - $this->assertEqual($date_formatter->format($this->testUsers[2]->getCreatedTime(), 'custom', 'H:i:s'), $executable->getStyle()->getField(2, 'created_1')); - $this->assertEqual($date_formatter->format($this->testUsers[2]->getCreatedTime(), 'fallback'), $executable->getStyle()->getField(2, 'created_2')); - - $this->assertEqual($this->testUsers[3]->getTimeZone(), $executable->getStyle()->getField(3, 'timezone')); - $this->assertEqual("9, 9", $executable->getStyle()->getField(3, 'field_test_multiple')); - $this->assertEqual("9", $executable->getStyle()->getField(3, 'field_test_multiple_1')); - $this->assertEqual("9", $executable->getStyle()->getField(3, 'field_test_multiple_2')); - $this->assertEqual($date_formatter->format($this->testUsers[3]->getCreatedTime(), 'custom', 'Y'), $executable->getStyle()->getField(3, 'created')); - $this->assertEqual($date_formatter->format($this->testUsers[3]->getCreatedTime(), 'custom', 'H:i:s'), $executable->getStyle()->getField(3, 'created_1')); - $this->assertEqual($date_formatter->format($this->testUsers[3]->getCreatedTime(), 'fallback'), $executable->getStyle()->getField(3, 'created_2')); - - $this->assertEqual($this->testUsers[4]->getTimeZone(), $executable->getStyle()->getField(4, 'timezone')); - $this->assertEqual("9, 0", $executable->getStyle()->getField(4, 'field_test_multiple')); - $this->assertEqual("9", $executable->getStyle()->getField(4, 'field_test_multiple_1')); - $this->assertEqual("0", $executable->getStyle()->getField(4, 'field_test_multiple_2')); - $this->assertEqual($date_formatter->format($this->testUsers[4]->getCreatedTime(), 'custom', 'Y'), $executable->getStyle()->getField(4, 'created')); - $this->assertEqual($date_formatter->format($this->testUsers[4]->getCreatedTime(), 'custom', 'H:i:s'), $executable->getStyle()->getField(4, 'created_1')); - $this->assertEqual($date_formatter->format($this->testUsers[4]->getCreatedTime(), 'fallback'), $executable->getStyle()->getField(4, 'created_2')); - } - - /** - * Tests the revision result. - */ - public function testRevisionExecute() { - $executable = Views::getView('test_field_field_revision_test'); - $executable->execute(); - - $this->assertTrue($executable->field['name'] instanceof Field); - $this->assertTrue($executable->field['field_test'] instanceof Field); - - $this->assertIdenticalResultset($executable, - [ - ['id' => 1, 'field_test' => 1, 'revision_id' => 1, 'name' => 'base value'], - ['id' => 1, 'field_test' => 2, 'revision_id' => 2, 'name' => 'revision value1'], - ['id' => 1, 'field_test' => 3, 'revision_id' => 3, 'name' => 'revision value2'], - ['id' => 2, 'field_test' => 4, 'revision_id' => 4, 'name' => 'next entity value'], - ], - ['entity_test_rev_revision_id' => 'id', 'revision_id' => 'revision_id', 'name' => 'name', 'field_test' => 'field_test'] - ); - } - - /** - * Tests the output of a revision view with base and configurable fields. - */ - public function testRevisionRender() { - $executable = Views::getView('test_field_field_revision_test'); - $executable->execute(); - - $this->assertEqual('1', $executable->getStyle()->getField(0, 'id')); - $this->assertEqual('1', $executable->getStyle()->getField(0, 'revision_id')); - $this->assertEqual('1', $executable->getStyle()->getField(0, 'field_test')); - $this->assertEqual('base value', $executable->getStyle()->getField(0, 'name')); - - $this->assertEqual('1', $executable->getStyle()->getField(1, 'id')); - $this->assertEqual('2', $executable->getStyle()->getField(1, 'revision_id')); - $this->assertEqual('2', $executable->getStyle()->getField(1, 'field_test')); - $this->assertEqual('revision value1', $executable->getStyle()->getField(1, 'name')); - - $this->assertEqual('1', $executable->getStyle()->getField(2, 'id')); - $this->assertEqual('3', $executable->getStyle()->getField(2, 'revision_id')); - $this->assertEqual('3', $executable->getStyle()->getField(2, 'field_test')); - $this->assertEqual('revision value2', $executable->getStyle()->getField(2, 'name')); - - $this->assertEqual('2', $executable->getStyle()->getField(3, 'id')); - $this->assertEqual('4', $executable->getStyle()->getField(3, 'revision_id')); - $this->assertEqual('4', $executable->getStyle()->getField(3, 'field_test')); - $this->assertEqual('next entity value', $executable->getStyle()->getField(3, 'name')); - } - - /** - * Tests the result set of a complex revision view. - */ - public function testRevisionComplexExecute() { - $executable = Views::getView('test_field_field_revision_complex_test'); - $executable->execute(); - - $timezones = []; - foreach ($this->testUsers as $user) { - $timezones[] = $user->getTimeZone(); - } - - $this->assertTrue($executable->field['id'] instanceof Field); - $this->assertTrue($executable->field['revision_id'] instanceof Field); - $this->assertTrue($executable->field['timezone'] instanceof Field); - $this->assertTrue($executable->field['field_test_multiple'] instanceof Field); - $this->assertTrue($executable->field['field_test_multiple_1'] instanceof Field); - $this->assertTrue($executable->field['field_test_multiple_2'] instanceof Field); - - $this->assertIdenticalResultset($executable, - [ - ['id' => 1, 'field_test' => 1, 'revision_id' => 1, 'uid' => $this->testUsers[0]->id(), 'timezone' => $timezones[0], 'field_test_multiple' => [1, 3, 7], 'field_test_multiple_1' => [1, 3, 7], 'field_test_multiple_2' => [1, 3, 7]], - ['id' => 1, 'field_test' => 2, 'revision_id' => 2, 'uid' => $this->testUsers[1]->id(), 'timezone' => $timezones[1], 'field_test_multiple' => [0, 3, 5], 'field_test_multiple_1' => [0, 3, 5], 'field_test_multiple_2' => [0, 3, 5]], - ['id' => 1, 'field_test' => 3, 'revision_id' => 3, 'uid' => $this->testUsers[2]->id(), 'timezone' => $timezones[2], 'field_test_multiple' => [9, 9, 9], 'field_test_multiple_1' => [9, 9, 9], 'field_test_multiple_2' => [9, 9, 9]], - ['id' => 2, 'field_test' => 4, 'revision_id' => 4, 'uid' => $this->testUsers[3]->id(), 'timezone' => $timezones[3], 'field_test_multiple' => [2, 9, 9], 'field_test_multiple_1' => [2, 9, 9], 'field_test_multiple_2' => [2, 9, 9]], - ], - ['entity_test_rev_revision_id' => 'id', 'revision_id' => 'revision_id', 'users_field_data_entity_test_rev_revision_uid' => 'uid', 'timezone' => 'timezone', 'field_test_multiple' => 'field_test_multiple', 'field_test_multiple_1' => 'field_test_multiple_1', 'field_test_multiple_2' => 'field_test_multiple_2'] - ); - } - - /** - * Tests the output of a revision view with base fields and configurable fields. - */ - public function testRevisionComplexRender() { - $executable = Views::getView('test_field_field_revision_complex_test'); - $executable->execute(); - - $this->assertEqual('1', $executable->getStyle()->getField(0, 'id')); - $this->assertEqual('1', $executable->getStyle()->getField(0, 'revision_id')); - $this->assertEqual($this->testUsers[0]->getTimeZone(), $executable->getStyle()->getField(0, 'timezone')); - $this->assertEqual('1, 3, 7', $executable->getStyle()->getField(0, 'field_test_multiple')); - $this->assertEqual('1', $executable->getStyle()->getField(0, 'field_test_multiple_1')); - $this->assertEqual('3, 7', $executable->getStyle()->getField(0, 'field_test_multiple_2')); - - $this->assertEqual('1', $executable->getStyle()->getField(1, 'id')); - $this->assertEqual('2', $executable->getStyle()->getField(1, 'revision_id')); - $this->assertEqual($this->testUsers[1]->getTimeZone(), $executable->getStyle()->getField(1, 'timezone')); - $this->assertEqual('0, 3, 5', $executable->getStyle()->getField(1, 'field_test_multiple')); - $this->assertEqual('0', $executable->getStyle()->getField(1, 'field_test_multiple_1')); - $this->assertEqual('3, 5', $executable->getStyle()->getField(1, 'field_test_multiple_2')); - - $this->assertEqual('1', $executable->getStyle()->getField(2, 'id')); - $this->assertEqual('3', $executable->getStyle()->getField(2, 'revision_id')); - $this->assertEqual($this->testUsers[2]->getTimeZone(), $executable->getStyle()->getField(2, 'timezone')); - $this->assertEqual('9, 9, 9', $executable->getStyle()->getField(2, 'field_test_multiple')); - $this->assertEqual('9', $executable->getStyle()->getField(2, 'field_test_multiple_1')); - $this->assertEqual('9, 9', $executable->getStyle()->getField(2, 'field_test_multiple_2')); - - $this->assertEqual('2', $executable->getStyle()->getField(3, 'id')); - $this->assertEqual('4', $executable->getStyle()->getField(3, 'revision_id')); - $this->assertEqual($this->testUsers[3]->getTimeZone(), $executable->getStyle()->getField(3, 'timezone')); - $this->assertEqual('2, 9, 9', $executable->getStyle()->getField(3, 'field_test_multiple')); - $this->assertEqual('2', $executable->getStyle()->getField(3, 'field_test_multiple_1')); - $this->assertEqual('9, 9', $executable->getStyle()->getField(3, 'field_test_multiple_2')); - } - - /** - * Tests that a field not available for every bundle is rendered as empty. - */ - public function testMissingBundleFieldRender() { - // Create a new bundle not having the test field attached. - $bundle = $this->randomMachineName(); - entity_test_create_bundle($bundle); - - $entity = EntityTest::create([ - 'type' => $bundle, - 'name' => $this->randomString(), - 'user_id' => $this->testUsers[0]->id(), - ]); - $entity->save(); - - $executable = Views::getView('test_field_field_test'); - $executable->execute(); - - $this->assertEqual('', $executable->getStyle()->getField(6, 'field_test')); - } - -} diff --git a/core/modules/views/src/Tests/Handler/FieldFileSizeTest.php b/core/modules/views/src/Tests/Handler/FieldFileSizeTest.php deleted file mode 100644 index dd97f1b..0000000 --- a/core/modules/views/src/Tests/Handler/FieldFileSizeTest.php +++ /dev/null @@ -1,72 +0,0 @@ -setDisplay(); - - $view->displayHandlers->get('default')->overrideOption('fields', array( - 'age' => array( - 'id' => 'age', - 'table' => 'views_test_data', - 'field' => 'age', - ), - )); - - $this->executeView($view); - - // Test with the formatted option. - $this->assertEqual($view->field['age']->advancedRender($view->result[0]), ''); - $this->assertEqual($view->field['age']->advancedRender($view->result[1]), '10 bytes'); - $this->assertEqual($view->field['age']->advancedRender($view->result[2]), '1000 bytes'); - $this->assertEqual($view->field['age']->advancedRender($view->result[3]), '9.77 KB'); - // Test with the bytes option. - $view->field['age']->options['file_size_display'] = 'bytes'; - $this->assertEqual($view->field['age']->advancedRender($view->result[0]), ''); - $this->assertEqual($view->field['age']->advancedRender($view->result[1]), '10'); - $this->assertEqual($view->field['age']->advancedRender($view->result[2]), '1000'); - $this->assertEqual($view->field['age']->advancedRender($view->result[3]), '10000'); - } - -} diff --git a/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php b/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php index 9cfd854..fa1a6aa 100644 --- a/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php +++ b/core/modules/views/src/Tests/Handler/FieldGroupRowsTest.php @@ -9,7 +9,9 @@ use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Render\RenderContext; +use Drupal\field\Entity\FieldConfig; use Drupal\views\Views; +use Drupal\field\Entity\FieldStorageConfig; /** * Tests the "Display all values in the same row" setting. @@ -48,7 +50,7 @@ protected function setUp() { $node_type = $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page')); // Create the unlimited text field. - $field_storage = entity_create('field_storage_config', array( + $field_storage = FieldStorageConfig::create(array( 'field_name' => $this->fieldName, 'entity_type' => 'node', 'type' => 'text', @@ -61,7 +63,7 @@ protected function setUp() { 'field_storage' => $field_storage, 'bundle' => $node_type->id(), ); - entity_create('field_config', $field)->save(); + FieldConfig::create($field)->save(); } /** diff --git a/core/modules/views/src/Tests/Handler/FieldKernelTest.php b/core/modules/views/src/Tests/Handler/FieldKernelTest.php deleted file mode 100644 index 815b1ec..0000000 --- a/core/modules/views/src/Tests/Handler/FieldKernelTest.php +++ /dev/null @@ -1,797 +0,0 @@ - 'name', - ); - - /** - * {@inheritdoc} - */ - protected function viewsData() { - $data = parent::viewsData(); - $data['views_test_data']['job']['field']['id'] = 'test_field'; - $data['views_test_data']['job']['field']['click sortable'] = FALSE; - $data['views_test_data']['id']['field']['click sortable'] = TRUE; - return $data; - } - - /** - * Tests that the render function is called. - */ - public function testRender() { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = \Drupal::service('renderer'); - - $view = Views::getView('test_field_tokens'); - $this->executeView($view); - - $random_text = $this->randomMachineName(); - $view->field['job']->setTestValue($random_text); - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['job']->theme($view->result[0]); - }); - $this->assertEqual($output, $random_text, 'Make sure the render method rendered the manual set value.'); - } - - /** - * Tests all things related to the query. - */ - public function testQuery() { - // Tests adding additional fields to the query. - $view = Views::getView('test_view'); - $view->initHandlers(); - - $id_field = $view->field['id']; - $id_field->additional_fields['job'] = 'job'; - // Choose also a field alias key which doesn't match to the table field. - $id_field->additional_fields['created_test'] = array('table' => 'views_test_data', 'field' => 'created'); - $view->build(); - - // Make sure the field aliases have the expected value. - $this->assertEqual($id_field->aliases['job'], 'views_test_data_job'); - $this->assertEqual($id_field->aliases['created_test'], 'views_test_data_created'); - - $this->executeView($view); - // Tests the getValue method with and without a field aliases. - foreach ($this->dataSet() as $key => $row) { - $id = $key + 1; - $result = $view->result[$key]; - $this->assertEqual($id_field->getValue($result), $id); - $this->assertEqual($id_field->getValue($result, 'job'), $row['job']); - $this->assertEqual($id_field->getValue($result, 'created_test'), $row['created']); - } - } - - /** - * Asserts that a string is part of another string. - * - * @param string $haystack - * The value to search in. - * @param string $needle - * The value to search for. - * @param string $message - * (optional) A message to display with the assertion. Do not translate - * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed - * variables in the message text, not t(). If left blank, a default message - * will be displayed. - * @param string $group - * (optional) The group this message is in, which is displayed in a column - * in test output. Use 'Debug' to indicate this is debugging output. Do not - * translate this string. Defaults to 'Other'; most tests do not override - * this default. - * - * @return bool - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertSubString($haystack, $needle, $message = '', $group = 'Other') { - return $this->assertTrue(strpos($haystack, $needle) !== FALSE, $message, $group); - } - - /** - * Asserts that a string is not part of another string. - * - * @param string $haystack - * The value to search in. - * @param string $needle - * The value to search for. - * @param string $message - * (optional) A message to display with the assertion. Do not translate - * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed - * variables in the message text, not t(). If left blank, a default message - * will be displayed. - * @param string $group - * (optional) The group this message is in, which is displayed in a column - * in test output. Use 'Debug' to indicate this is debugging output. Do not - * translate this string. Defaults to 'Other'; most tests do not override - * this default. - * - * @return bool - * TRUE if the assertion succeeded, FALSE otherwise. - */ - protected function assertNotSubString($haystack, $needle, $message = '', $group = 'Other') { - return $this->assertTrue(strpos($haystack, $needle) === FALSE, $message, $group); - } - - /** - * Tests general rewriting of the output. - */ - public function testRewrite() { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = \Drupal::service('renderer'); - - $view = Views::getView('test_view'); - $view->initHandlers(); - $this->executeView($view); - $row = $view->result[0]; - $id_field = $view->field['id']; - - // Don't check the rewrite checkbox, so the text shouldn't appear. - $id_field->options['alter']['text'] = $random_text = $this->randomMachineName(); - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) { - return $id_field->theme($row); - }); - $this->assertNotSubString($output, $random_text); - - $id_field->options['alter']['alter_text'] = TRUE; - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) { - return $id_field->theme($row); - }); - $this->assertSubString($output, $random_text); - } - - /** - * Tests the arguments tokens on field level. - */ - public function testArgumentTokens() { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = \Drupal::service('renderer'); - - $view = Views::getView('test_field_argument_tokens'); - $this->executeView($view, ['{{ { "#pre_render": ["views_test_data_test_pre_render_function"]} }}']); - - $name_field_0 = $view->field['name']; - - // Test the old style tokens. - $name_field_0->options['alter']['alter_text'] = TRUE; - $name_field_0->options['alter']['text'] = '%1 !1'; - - $row = $view->result[0]; - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_0, $row) { - return $name_field_0->advancedRender($row); - }); - - $this->assertFalse(strpos((string) $output, 'views_test_data_test_pre_render_function executed') !== FALSE, 'Ensure that the pre_render function was not executed'); - $this->assertEqual('%1 !1', (string) $output, "Ensure that old style placeholders aren't replaced"); - - // This time use new style tokens but ensure that we still don't allow - // arbitrary code execution. - $name_field_0->options['alter']['alter_text'] = TRUE; - $name_field_0->options['alter']['text'] = '{{ arguments.null }} {{ raw_arguments.null }}'; - - $row = $view->result[0]; - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_0, $row) { - return $name_field_0->advancedRender($row); - }); - - $this->assertFalse(strpos((string) $output, 'views_test_data_test_pre_render_function executed') !== FALSE, 'Ensure that the pre_render function was not executed'); - $this->assertEqual('{{ { "#pre_render": ["views_test_data_test_pre_render_function"]} }} {{ { "#pre_render": ["views_test_data_test_pre_render_function"]} }}', (string) $output, 'Ensure that new style placeholders are replaced'); - } - - /** - * Tests the field tokens, row level and field level. - */ - public function testFieldTokens() { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = \Drupal::service('renderer'); - - $view = Views::getView('test_field_tokens'); - $this->executeView($view); - $name_field_0 = $view->field['name']; - $name_field_1 = $view->field['name_1']; - $name_field_2 = $view->field['name_2']; - $row = $view->result[0]; - - $name_field_0->options['alter']['alter_text'] = TRUE; - $name_field_0->options['alter']['text'] = '{{ name }}'; - - $name_field_1->options['alter']['alter_text'] = TRUE; - $name_field_1->options['alter']['text'] = '{{ name_1 }} {{ name }}'; - - $name_field_2->options['alter']['alter_text'] = TRUE; - $name_field_2->options['alter']['text'] = '{% if name_2|length > 3 %}{{ name_2 }} {{ name_1 }}{% endif %}'; - - foreach ($view->result as $row) { - $expected_output_0 = $row->views_test_data_name; - $expected_output_1 = "$row->views_test_data_name $row->views_test_data_name"; - $expected_output_2 = "$row->views_test_data_name $row->views_test_data_name $row->views_test_data_name"; - - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_0, $row) { - return $name_field_0->advancedRender($row); - }); - $this->assertEqual($output, $expected_output_0, format_string('Test token replacement: "@token" gave "@output"', [ - '@token' => $name_field_0->options['alter']['text'], - '@output' => $output, - ])); - - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_1, $row) { - return $name_field_1->advancedRender($row); - }); - $this->assertEqual($output, $expected_output_1, format_string('Test token replacement: "@token" gave "@output"', [ - '@token' => $name_field_1->options['alter']['text'], - '@output' => $output, - ])); - - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_2, $row) { - return $name_field_2->advancedRender($row); - }); - $this->assertEqual($output, $expected_output_2, format_string('Test token replacement: "@token" gave "@output"', [ - '@token' => $name_field_2->options['alter']['text'], - '@output' => $output, - ])); - } - - $job_field = $view->field['job']; - $job_field->options['alter']['alter_text'] = TRUE; - $job_field->options['alter']['text'] = '{{ job }}'; - - $random_text = $this->randomMachineName(); - $job_field->setTestValue($random_text); - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) { - return $job_field->advancedRender($row); - }); - $this->assertSubString($output, $random_text, format_string('Make sure the self token (@token => @value) appears in the output (@output)', [ - '@value' => $random_text, - '@output' => $output, - '@token' => $job_field->options['alter']['text'], - ])); - - // Verify the token format used in D7 and earlier does not get substituted. - $old_token = '[job]'; - $job_field->options['alter']['text'] = $old_token; - $random_text = $this->randomMachineName(); - $job_field->setTestValue($random_text); - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) { - return $job_field->advancedRender($row); - }); - $this->assertEqual($output, $old_token, format_string('Make sure the old token style (@token => @value) is not changed in the output (@output)', [ - '@value' => $random_text, - '@output' => $output, - '@token' => $job_field->options['alter']['text'], - ])); - - // Verify HTML tags are allowed in rewrite templates while token - // replacements are escaped. - $job_field->options['alter']['text'] = '

                          {{ job }}

                          '; - $random_text = $this->randomMachineName(); - $job_field->setTestValue('' . $random_text . ''); - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) { - return $job_field->advancedRender($row); - }); - $this->assertEqual($output, '

                          <span>' . $random_text . '</span>

                          ', 'Valid tags are allowed in rewrite templates and token replacements.'); - - // Verify '; - $job_field->options['alter']['text'] = $rewrite_template; - $random_text = $this->randomMachineName(); - $job_field->setTestValue($random_text); - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) { - return $job_field->advancedRender($row); - }); - $this->assertNotSubString($output, ''; - $job_field->options['alter']['text'] = $rewrite_template; - $random_text = $this->randomMachineName(); - $job_field->setTestValue($random_text); - $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) { - return $job_field->advancedRender($row); - }); - $this->assertEqual($output, $random_text, format_string('Make sure a script tag in the template (@template) is removed, leaving only the replaced token in the output (@output)', [ - '@output' => $output, - '@template' => $rewrite_template, - ])); - } - - /** - * Tests the exclude setting. - */ - public function testExclude() { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = $this->container->get('renderer'); - $view = Views::getView('test_field_output'); - $view->initHandlers(); - // Hide the field and see whether it's rendered. - $view->field['name']->options['exclude'] = TRUE; - - $output = $view->preview(); - $output = $renderer->renderRoot($output); - foreach ($this->dataSet() as $entry) { - $this->assertNotSubString($output, $entry['name']); - } - - // Show and check the field. - $view->field['name']->options['exclude'] = FALSE; - - $output = $view->preview(); - $output = $renderer->renderRoot($output); - foreach ($this->dataSet() as $entry) { - $this->assertSubString($output, $entry['name']); - } - } - - /** - * Tests everything related to empty output of a field. - */ - function testEmpty() { - $this->_testHideIfEmpty(); - $this->_testEmptyText(); - } - - /** - * Tests the hide if empty functionality. - * - * This tests alters the result to get easier and less coupled results. It is - * important that assertIdentical() is used in this test since in PHP 0 == ''. - */ - function _testHideIfEmpty() { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = \Drupal::service('renderer'); - - $view = Views::getView('test_view'); - $view->initDisplay(); - $this->executeView($view); - - $column_map_reversed = array_flip($this->columnMap); - $view->row_index = 0; - $random_name = $this->randomMachineName(); - $random_value = $this->randomMachineName(); - - // Test when results are not rewritten and empty values are not hidden. - $view->field['name']->options['hide_alter_empty'] = FALSE; - $view->field['name']->options['hide_empty'] = FALSE; - $view->field['name']->options['empty_zero'] = FALSE; - - // Test a valid string. - $view->result[0]->{$column_map_reversed['name']} = $random_name; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $random_name, 'By default, a string should not be treated as empty.'); - - // Test an empty string. - $view->result[0]->{$column_map_reversed['name']} = ""; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical($render, "", 'By default, "" should not be treated as empty.'); - - // Test zero as an integer. - $view->result[0]->{$column_map_reversed['name']} = 0; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, '0', 'By default, 0 should not be treated as empty.'); - - // Test zero as a string. - $view->result[0]->{$column_map_reversed['name']} = "0"; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, "0", 'By default, "0" should not be treated as empty.'); - - // Test when results are not rewritten and non-zero empty values are hidden. - $view->field['name']->options['hide_alter_empty'] = TRUE; - $view->field['name']->options['hide_empty'] = TRUE; - $view->field['name']->options['empty_zero'] = FALSE; - - // Test a valid string. - $view->result[0]->{$column_map_reversed['name']} = $random_name; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $random_name, 'If hide_empty is checked, a string should not be treated as empty.'); - - // Test an empty string. - $view->result[0]->{$column_map_reversed['name']} = ""; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical($render, "", 'If hide_empty is checked, "" should be treated as empty.'); - - // Test zero as an integer. - $view->result[0]->{$column_map_reversed['name']} = 0; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, '0', 'If hide_empty is checked, but not empty_zero, 0 should not be treated as empty.'); - - // Test zero as a string. - $view->result[0]->{$column_map_reversed['name']} = "0"; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, "0", 'If hide_empty is checked, but not empty_zero, "0" should not be treated as empty.'); - - // Test when results are not rewritten and all empty values are hidden. - $view->field['name']->options['hide_alter_empty'] = TRUE; - $view->field['name']->options['hide_empty'] = TRUE; - $view->field['name']->options['empty_zero'] = TRUE; - - // Test zero as an integer. - $view->result[0]->{$column_map_reversed['name']} = 0; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical($render, "", 'If hide_empty and empty_zero are checked, 0 should be treated as empty.'); - - // Test zero as a string. - $view->result[0]->{$column_map_reversed['name']} = "0"; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical($render, "", 'If hide_empty and empty_zero are checked, "0" should be treated as empty.'); - - // Test when results are rewritten to a valid string and non-zero empty - // results are hidden. - $view->field['name']->options['hide_alter_empty'] = FALSE; - $view->field['name']->options['hide_empty'] = TRUE; - $view->field['name']->options['empty_zero'] = FALSE; - $view->field['name']->options['alter']['alter_text'] = TRUE; - $view->field['name']->options['alter']['text'] = $random_name; - - // Test a valid string. - $view->result[0]->{$column_map_reversed['name']} = $random_value; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $random_name, 'If the rewritten string is not empty, it should not be treated as empty.'); - - // Test an empty string. - $view->result[0]->{$column_map_reversed['name']} = ""; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $random_name, 'If the rewritten string is not empty, "" should not be treated as empty.'); - - // Test zero as an integer. - $view->result[0]->{$column_map_reversed['name']} = 0; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $random_name, 'If the rewritten string is not empty, 0 should not be treated as empty.'); - - // Test zero as a string. - $view->result[0]->{$column_map_reversed['name']} = "0"; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $random_name, 'If the rewritten string is not empty, "0" should not be treated as empty.'); - - // Test when results are rewritten to an empty string and non-zero empty results are hidden. - $view->field['name']->options['hide_alter_empty'] = TRUE; - $view->field['name']->options['hide_empty'] = TRUE; - $view->field['name']->options['empty_zero'] = FALSE; - $view->field['name']->options['alter']['alter_text'] = TRUE; - $view->field['name']->options['alter']['text'] = ""; - - // Test a valid string. - $view->result[0]->{$column_map_reversed['name']} = $random_name; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $random_name, 'If the rewritten string is empty, it should not be treated as empty.'); - - // Test an empty string. - $view->result[0]->{$column_map_reversed['name']} = ""; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical($render, "", 'If the rewritten string is empty, "" should be treated as empty.'); - - // Test zero as an integer. - $view->result[0]->{$column_map_reversed['name']} = 0; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, '0', 'If the rewritten string is empty, 0 should not be treated as empty.'); - - // Test zero as a string. - $view->result[0]->{$column_map_reversed['name']} = "0"; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, "0", 'If the rewritten string is empty, "0" should not be treated as empty.'); - - // Test when results are rewritten to zero as a string and non-zero empty - // results are hidden. - $view->field['name']->options['hide_alter_empty'] = FALSE; - $view->field['name']->options['hide_empty'] = TRUE; - $view->field['name']->options['empty_zero'] = FALSE; - $view->field['name']->options['alter']['alter_text'] = TRUE; - $view->field['name']->options['alter']['text'] = "0"; - - // Test a valid string. - $view->result[0]->{$column_map_reversed['name']} = $random_name; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, "0", 'If the rewritten string is zero and empty_zero is not checked, the string rewritten as 0 should not be treated as empty.'); - - // Test an empty string. - $view->result[0]->{$column_map_reversed['name']} = ""; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, "0", 'If the rewritten string is zero and empty_zero is not checked, "" rewritten as 0 should not be treated as empty.'); - - // Test zero as an integer. - $view->result[0]->{$column_map_reversed['name']} = 0; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, "0", 'If the rewritten string is zero and empty_zero is not checked, 0 should not be treated as empty.'); - - // Test zero as a string. - $view->result[0]->{$column_map_reversed['name']} = "0"; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, "0", 'If the rewritten string is zero and empty_zero is not checked, "0" should not be treated as empty.'); - - // Test when results are rewritten to a valid string and non-zero empty - // results are hidden. - $view->field['name']->options['hide_alter_empty'] = TRUE; - $view->field['name']->options['hide_empty'] = TRUE; - $view->field['name']->options['empty_zero'] = FALSE; - $view->field['name']->options['alter']['alter_text'] = TRUE; - $view->field['name']->options['alter']['text'] = $random_value; - - // Test a valid string. - $view->result[0]->{$column_map_reversed['name']} = $random_name; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $random_value, 'If the original and rewritten strings are valid, it should not be treated as empty.'); - - // Test an empty string. - $view->result[0]->{$column_map_reversed['name']} = ""; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical($render, "", 'If either the original or rewritten string is invalid, "" should be treated as empty.'); - - // Test zero as an integer. - $view->result[0]->{$column_map_reversed['name']} = 0; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $random_value, 'If the original and rewritten strings are valid, 0 should not be treated as empty.'); - - // Test zero as a string. - $view->result[0]->{$column_map_reversed['name']} = "0"; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $random_value, 'If the original and rewritten strings are valid, "0" should not be treated as empty.'); - - // Test when results are rewritten to zero as a string and all empty - // original values and results are hidden. - $view->field['name']->options['hide_alter_empty'] = TRUE; - $view->field['name']->options['hide_empty'] = TRUE; - $view->field['name']->options['empty_zero'] = TRUE; - $view->field['name']->options['alter']['alter_text'] = TRUE; - $view->field['name']->options['alter']['text'] = "0"; - - // Test a valid string. - $view->result[0]->{$column_map_reversed['name']} = $random_name; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, "", 'If the rewritten string is zero, it should be treated as empty.'); - - // Test an empty string. - $view->result[0]->{$column_map_reversed['name']} = ""; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical($render, "", 'If the rewritten string is zero, "" should be treated as empty.'); - - // Test zero as an integer. - $view->result[0]->{$column_map_reversed['name']} = 0; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical($render, "", 'If the rewritten string is zero, 0 should not be treated as empty.'); - - // Test zero as a string. - $view->result[0]->{$column_map_reversed['name']} = "0"; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical($render, "", 'If the rewritten string is zero, "0" should not be treated as empty.'); - } - - /** - * Tests the usage of the empty text. - */ - function _testEmptyText() { - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = \Drupal::service('renderer'); - - $view = Views::getView('test_view'); - $view->initDisplay(); - $this->executeView($view); - - $column_map_reversed = array_flip($this->columnMap); - $view->row_index = 0; - - $empty_text = $view->field['name']->options['empty'] = $this->randomMachineName(); - $view->result[0]->{$column_map_reversed['name']} = ""; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $empty_text, 'If a field is empty, the empty text should be used for the output.'); - - $view->result[0]->{$column_map_reversed['name']} = "0"; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, "0", 'If a field is 0 and empty_zero is not checked, the empty text should not be used for the output.'); - - $view->result[0]->{$column_map_reversed['name']} = "0"; - $view->field['name']->options['empty_zero'] = TRUE; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $empty_text, 'If a field is 0 and empty_zero is checked, the empty text should be used for the output.'); - - $view->result[0]->{$column_map_reversed['name']} = ""; - $view->field['name']->options['alter']['alter_text'] = TRUE; - $alter_text = $view->field['name']->options['alter']['text'] = $this->randomMachineName(); - $view->field['name']->options['hide_alter_empty'] = FALSE; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $alter_text, 'If a field is empty, some rewrite text exists, but hide_alter_empty is not checked, render the rewrite text.'); - - $view->field['name']->options['hide_alter_empty'] = TRUE; - $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { - return $view->field['name']->advancedRender($view->result[0]); - }); - $this->assertIdentical((string) $render, $empty_text, 'If a field is empty, some rewrite text exists, and hide_alter_empty is checked, use the empty text.'); - } - - /** - * Tests views_handler_field::isValueEmpty(). - */ - function testIsValueEmpty() { - $view = Views::getView('test_view'); - $view->initHandlers(); - $field = $view->field['name']; - - $this->assertFalse($field->isValueEmpty("not empty", TRUE), 'A normal string is not empty.'); - $this->assertTrue($field->isValueEmpty("not empty", TRUE, FALSE), 'A normal string which skips empty() can be seen as empty.'); - - $this->assertTrue($field->isValueEmpty("", TRUE), '"" is considered as empty.'); - - $this->assertTrue($field->isValueEmpty('0', TRUE), '"0" is considered as empty if empty_zero is TRUE.'); - $this->assertTrue($field->isValueEmpty(0, TRUE), '0 is considered as empty if empty_zero is TRUE.'); - $this->assertFalse($field->isValueEmpty('0', FALSE), '"0" is considered not as empty if empty_zero is FALSE.'); - $this->assertFalse($field->isValueEmpty(0, FALSE), '0 is considered not as empty if empty_zero is FALSE.'); - - $this->assertTrue($field->isValueEmpty(NULL, TRUE, TRUE), 'Null should be always seen as empty, regardless of no_skip_empty.'); - $this->assertTrue($field->isValueEmpty(NULL, TRUE, FALSE), 'Null should be always seen as empty, regardless of no_skip_empty.'); - } - - /** - * Tests whether the filters are click sortable as expected. - */ - public function testClickSortable() { - // Test that clickSortable is TRUE by default. - $item = array( - 'table' => 'views_test_data', - 'field' => 'name', - ); - $plugin = $this->container->get('plugin.manager.views.field')->getHandler($item); - $this->assertTrue($plugin->clickSortable(), 'TRUE as a default value is correct.'); - - // Test that clickSortable is TRUE by when set TRUE in the data. - $item['field'] = 'id'; - $plugin = $this->container->get('plugin.manager.views.field')->getHandler($item); - $this->assertTrue($plugin->clickSortable(), 'TRUE as a views data value is correct.'); - - // Test that clickSortable is FALSE by when set FALSE in the data. - $item['field'] = 'job'; - $plugin = $this->container->get('plugin.manager.views.field')->getHandler($item); - $this->assertFalse($plugin->clickSortable(), 'FALSE as a views data value is correct.'); - } - - /** - * Tests the trimText method. - */ - public function testTrimText() { - // Test unicode. See https://www.drupal.org/node/513396#comment-2839416. - $text = array( - 'Tuy nhiên, những hi vọng', - 'Giả sử chúng tôi có 3 Apple', - 'siêu nhỏ này là bộ xử lý', - 'Di động của nhà sản xuất Phần Lan', - 'khoảng cách từ đại lí đến', - 'của hãng bao gồm ba dòng', - 'сд асд асд ас', - 'асд асд асд ас' - ); - // Just test maxlength without word boundary. - $alter = array( - 'max_length' => 10, - ); - $expect = array( - 'Tuy nhiên,', - 'Giả sử chú', - 'siêu nhỏ n', - 'Di động củ', - 'khoảng các', - 'của hãng b', - 'сд асд асд', - 'асд асд ас', - ); - - foreach ($text as $key => $line) { - $result_text = FieldPluginBase::trimText($alter, $line); - $this->assertEqual($result_text, $expect[$key]); - } - - // Test also word_boundary - $alter['word_boundary'] = TRUE; - $expect = array( - 'Tuy nhiên', - 'Giả sử', - 'siêu nhỏ', - 'Di động', - 'khoảng', - 'của hãng', - 'сд асд', - 'асд асд', - ); - - foreach ($text as $key => $line) { - $result_text = FieldPluginBase::trimText($alter, $line); - $this->assertEqual($result_text, $expect[$key]); - } - } - -} diff --git a/core/modules/views/src/Tests/Handler/FieldUrlTest.php b/core/modules/views/src/Tests/Handler/FieldUrlTest.php deleted file mode 100644 index d3451aa..0000000 --- a/core/modules/views/src/Tests/Handler/FieldUrlTest.php +++ /dev/null @@ -1,77 +0,0 @@ -installSchema('system', 'url_alias'); - } - - function viewsData() { - $data = parent::viewsData(); - $data['views_test_data']['name']['field']['id'] = 'url'; - return $data; - } - - public function testFieldUrl() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - $view->displayHandlers->get('default')->overrideOption('fields', array( - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'display_as_link' => FALSE, - ), - )); - - $this->executeView($view); - - $this->assertEqual('John', $view->field['name']->advancedRender($view->result[0])); - - // Make the url a link. - $view->destroy(); - $view->setDisplay(); - - $view->displayHandlers->get('default')->overrideOption('fields', array( - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - ), - )); - - $this->executeView($view); - - $this->assertEqual(\Drupal::l('John', Url::fromUri('base:John')), $view->field['name']->advancedRender($view->result[0])); - } - -} diff --git a/core/modules/views/src/Tests/Handler/FilterBooleanOperatorStringTest.php b/core/modules/views/src/Tests/Handler/FilterBooleanOperatorStringTest.php deleted file mode 100644 index d3f3688..0000000 --- a/core/modules/views/src/Tests/Handler/FilterBooleanOperatorStringTest.php +++ /dev/null @@ -1,231 +0,0 @@ - 'id', - ); - - /** - * {@inheritdoc} - */ - protected function setUp() { - parent::setUp(); - - $this->installSchema('system', array('key_value_expire')); - } - - - /** - * {@inheritdoc} - */ - protected function schemaDefinition() { - $schema = parent::schemaDefinition(); - - $schema['views_test_data']['fields']['status'] = array( - 'description' => 'The status of this record', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ); - - return $schema; - } - - /** - * {@inheritdoc} - */ - protected function viewsData() { - $views_data = parent::viewsData(); - - $views_data['views_test_data']['status']['filter']['id'] = 'boolean_string'; - - return $views_data; - } - - /** - * {@inheritdoc} - */ - protected function dataSet() { - $data = parent::dataSet(); - - foreach ($data as &$row) { - if ($row['status']) { - $row['status'] = 'Enabled'; - } - else { - $row['status'] = ''; - } - } - - return $data; - } - - /** - * Tests the BooleanOperatorString filter. - */ - public function testFilterBooleanOperatorString() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Add a the status boolean filter. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'status' => array( - 'id' => 'status', - 'field' => 'status', - 'table' => 'views_test_data', - 'value' => 0, - ), - )); - $this->executeView($view); - - $expected_result = array( - array('id' => 2), - array('id' => 4), - ); - - $this->assertEqual(2, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - - $view->destroy(); - $view->setDisplay(); - - // Add the status boolean filter. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'status' => array( - 'id' => 'status', - 'field' => 'status', - 'table' => 'views_test_data', - 'value' => 1, - ), - )); - $this->executeView($view); - - $expected_result = array( - array('id' => 1), - array('id' => 3), - array('id' => 5), - ); - - $this->assertEqual(3, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - } - - /** - * Tests the Boolean filter with grouped exposed form enabled. - */ - public function testFilterGroupedExposed() { - $filters = $this->getGroupedExposedFilters(); - $view = Views::getView('test_view'); - - $view->setExposedInput(array('status' => 1)); - $view->setDisplay(); - $view->displayHandlers->get('default')->overrideOption('filters', $filters); - - $this->executeView($view); - - $expected_result = array( - array('id' => 1), - array('id' => 3), - array('id' => 5), - ); - - $this->assertEqual(3, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - $view->destroy(); - - $view->setExposedInput(array('status' => 2)); - $view->setDisplay(); - $view->displayHandlers->get('default')->overrideOption('filters', $filters); - - $this->executeView($view); - - $expected_result = array( - array('id' => 2), - array('id' => 4), - ); - - $this->assertEqual(2, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - } - - /** - * Provides grouped exposed filter configuration. - * - * @return array - * Returns the filter configuration for exposed filters. - */ - protected function getGroupedExposedFilters() { - $filters = array( - 'status' => array( - 'id' => 'status', - 'table' => 'views_test_data', - 'field' => 'status', - 'relationship' => 'none', - 'exposed' => TRUE, - 'expose' => array( - 'operator' => 'status_op', - 'label' => 'status', - 'identifier' => 'status', - ), - 'is_grouped' => TRUE, - 'group_info' => array( - 'label' => 'status', - 'identifier' => 'status', - 'default_group' => 'All', - 'group_items' => array( - 1 => array( - 'title' => 'Active', - 'operator' => '=', - 'value' => '1', - ), - 2 => array( - 'title' => 'Blocked', - 'operator' => '=', - 'value' => '0', - ), - ), - ), - ), - ); - return $filters; - } - -} diff --git a/core/modules/views/src/Tests/Handler/FilterBooleanOperatorTest.php b/core/modules/views/src/Tests/Handler/FilterBooleanOperatorTest.php deleted file mode 100644 index 7e6c310..0000000 --- a/core/modules/views/src/Tests/Handler/FilterBooleanOperatorTest.php +++ /dev/null @@ -1,229 +0,0 @@ - 'id', - ); - - protected function setUp() { - parent::setUp(); - - $this->installSchema('system', array('key_value_expire')); - } - - /** - * Tests the BooleanOperator filter. - */ - public function testFilterBooleanOperator() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Add a the status boolean filter. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'status' => array( - 'id' => 'status', - 'field' => 'status', - 'table' => 'views_test_data', - 'value' => 0, - ), - )); - $this->executeView($view); - - $expected_result = array( - array('id' => 2), - array('id' => 4), - ); - - $this->assertEqual(2, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - - $view->destroy(); - $view->setDisplay(); - - // Add the status boolean filter. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'status' => array( - 'id' => 'status', - 'field' => 'status', - 'table' => 'views_test_data', - 'value' => 1, - ), - )); - $this->executeView($view); - - $expected_result = array( - array('id' => 1), - array('id' => 3), - array('id' => 5), - ); - - $this->assertEqual(3, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - - $view->destroy(); - $view->setDisplay(); - - // Testing the same scenario but using the reverse status and operation. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'status' => array( - 'id' => 'status', - 'field' => 'status', - 'table' => 'views_test_data', - 'value' => 0, - 'operator' => '!=', - ), - )); - $this->executeView($view); - - $expected_result = array( - array('id' => 1), - array('id' => 3), - array('id' => 5), - ); - - $this->assertEqual(3, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - } - - /** - * Tests the boolean filter with grouped exposed form enabled. - */ - public function testFilterGroupedExposed() { - $filters = $this->getGroupedExposedFilters(); - $view = Views::getView('test_view'); - - $view->setExposedInput(array('status' => 1)); - $view->setDisplay(); - $view->displayHandlers->get('default')->overrideOption('filters', $filters); - - $this->executeView($view); - - $expected_result = array( - array('id' => 1), - array('id' => 3), - array('id' => 5), - ); - - $this->assertEqual(3, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - $view->destroy(); - - $view->setExposedInput(array('status' => 2)); - $view->setDisplay(); - $view->displayHandlers->get('default')->overrideOption('filters', $filters); - - $this->executeView($view); - - $expected_result = array( - array('id' => 2), - array('id' => 4), - ); - - $this->assertEqual(2, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - - $view->destroy(); - - // Expecting the same results as for ['status' => 1]. - $view->setExposedInput(['status' => 3]); - $view->setDisplay(); - $view->displayHandlers->get('default')->overrideOption('filters', $filters); - - $this->executeView($view); - - $expected_result = array( - array('id' => 1), - array('id' => 3), - array('id' => 5), - ); - - $this->assertEqual(3, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - } - - /** - * Provides grouped exposed filter configuration. - * - * @return array - */ - protected function getGroupedExposedFilters() { - $filters = array( - 'status' => array( - 'id' => 'status', - 'table' => 'views_test_data', - 'field' => 'status', - 'relationship' => 'none', - 'exposed' => TRUE, - 'expose' => array( - 'operator' => 'status_op', - 'label' => 'status', - 'identifier' => 'status', - ), - 'is_grouped' => TRUE, - 'group_info' => array( - 'label' => 'status', - 'identifier' => 'status', - 'default_group' => 'All', - 'group_items' => array( - 1 => array( - 'title' => 'Active', - 'operator' => '=', - 'value' => '1', - ), - 2 => array( - 'title' => 'Blocked', - 'operator' => '=', - 'value' => '0', - ), - // This group should return the same results as group 1, because it - // is the negation of group 2. - 3 => array( - 'title' => 'Active (reverse)', - 'operator' => '!=', - 'value' => '0', - ), - ), - ), - ), - ); - return $filters; - } - -} - diff --git a/core/modules/views/src/Tests/Handler/FilterCombineTest.php b/core/modules/views/src/Tests/Handler/FilterCombineTest.php deleted file mode 100644 index 9ad83c3..0000000 --- a/core/modules/views/src/Tests/Handler/FilterCombineTest.php +++ /dev/null @@ -1,161 +0,0 @@ - 'name', - 'views_test_data_job' => 'job', - ); - - public function testFilterCombineContains() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - $fields = $view->displayHandlers->get('default')->getOption('fields'); - $view->displayHandlers->get('default')->overrideOption('fields', $fields + array( - 'job' => array( - 'id' => 'job', - 'table' => 'views_test_data', - 'field' => 'job', - 'relationship' => 'none', - ), - )); - - // Change the filtering. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'age' => array( - 'id' => 'combine', - 'table' => 'views', - 'field' => 'combine', - 'relationship' => 'none', - 'operator' => 'contains', - 'fields' => array( - 'name', - 'job', - ), - 'value' => 'ing', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - 'job' => 'Singer', - ), - array( - 'name' => 'George', - 'job' => 'Singer', - ), - array( - 'name' => 'Ringo', - 'job' => 'Drummer', - ), - array( - 'name' => 'Ginger', - 'job' => NULL, - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - /** - * Tests if the filter can handle removed fields. - * - * Tests the combined filter handler when a field overwrite is done - * and fields set in the combine filter are removed from the display - * but not from the combined filter settings. - */ - public function testFilterCombineContainsFieldsOverwritten() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - $fields = $view->displayHandlers->get('default')->getOption('fields'); - $view->displayHandlers->get('default')->overrideOption('fields', $fields + array( - 'job' => array( - 'id' => 'job', - 'table' => 'views_test_data', - 'field' => 'job', - 'relationship' => 'none', - ), - )); - - // Change the filtering. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'age' => array( - 'id' => 'combine', - 'table' => 'views', - 'field' => 'combine', - 'relationship' => 'none', - 'operator' => 'contains', - 'fields' => array( - 'name', - 'job', - // Add a dummy field to the combined fields to simulate - // a removed or deleted field. - 'dummy', - ), - 'value' => 'ing', - ), - )); - - $this->executeView($view); - // Make sure this view will not get displayed. - $this->assertTrue($view->build_info['fail'], "View build has been marked as failed."); - // Make sure this view does not pass validation with the right error. - $errors = $view->validate(); - $this->assertEqual(reset($errors['default']), t('Field %field set in %filter is not set in this display.', array('%field' => 'dummy', '%filter' => 'Global: Combine fields filter'))); - } - - /** - * Additional data to test the NULL issue. - */ - protected function dataSet() { - $data_set = parent::dataSet(); - $data_set[] = array( - 'name' => 'Ginger', - 'age' => 25, - 'job' => NULL, - 'created' => gmmktime(0, 0, 0, 1, 2, 2000), - 'status' => 1, - ); - return $data_set; - } - - /** - * Allow {views_test_data}.job to be NULL. - */ - protected function schemaDefinition() { - $schema = parent::schemaDefinition(); - unset($schema['views_test_data']['fields']['job']['not null']); - return $schema; - } - -} diff --git a/core/modules/views/src/Tests/Handler/FilterEqualityTest.php b/core/modules/views/src/Tests/Handler/FilterEqualityTest.php deleted file mode 100644 index a25d9ba..0000000 --- a/core/modules/views/src/Tests/Handler/FilterEqualityTest.php +++ /dev/null @@ -1,199 +0,0 @@ - 'name', - ); - - protected function setUp() { - parent::setUp(); - - $this->installSchema('system', array('key_value_expire')); - } - - function viewsData() { - $data = parent::viewsData(); - $data['views_test_data']['name']['filter']['id'] = 'equality'; - return $data; - } - - function testEqual() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'operator' => '=', - 'value' => 'Ringo', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'Ringo', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - public function testEqualGroupedExposed() { - $filters = $this->getGroupedExposedFilters(); - $view = Views::getView('test_view'); - $view->newDisplay('page', 'Page', 'page_1'); - - // Filter: Name, Operator: =, Value: Ringo - $filters['name']['group_info']['default_group'] = 1; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'Ringo', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testNotEqual() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'operator' => '!=', - 'value' => 'Ringo', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - ), - array( - 'name' => 'George', - ), - array( - 'name' => 'Paul', - ), - array( - 'name' => 'Meredith', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - public function testEqualGroupedNotExposed() { - $filters = $this->getGroupedExposedFilters(); - $view = Views::getView('test_view'); - $view->newDisplay('page', 'Page', 'page_1'); - - // Filter: Name, Operator: !=, Value: Ringo - $filters['name']['group_info']['default_group'] = 2; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - ), - array( - 'name' => 'George', - ), - array( - 'name' => 'Paul', - ), - array( - 'name' => 'Meredith', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - - protected function getGroupedExposedFilters() { - $filters = array( - 'name' => array( - 'id' => 'name', - 'plugin_id' => 'equality', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'group' => 1, - 'exposed' => TRUE, - 'expose' => array( - 'operator' => 'name_op', - 'label' => 'name', - 'identifier' => 'name', - ), - 'is_grouped' => TRUE, - 'group_info' => array( - 'label' => 'name', - 'identifier' => 'name', - 'default_group' => 'All', - 'group_items' => array( - 1 => array( - 'title' => 'Name is equal to Ringo', - 'operator' => '=', - 'value' => 'Ringo', - ), - 2 => array( - 'title' => 'Name is not equal to Ringo', - 'operator' => '!=', - 'value' => 'Ringo', - ), - ), - ), - ), - ); - return $filters; - } - -} diff --git a/core/modules/views/src/Tests/Handler/FilterInOperatorTest.php b/core/modules/views/src/Tests/Handler/FilterInOperatorTest.php deleted file mode 100644 index 65c645c..0000000 --- a/core/modules/views/src/Tests/Handler/FilterInOperatorTest.php +++ /dev/null @@ -1,209 +0,0 @@ - 'name', - 'views_test_data_age' => 'age', - ); - - protected function setUp() { - parent::setUp(); - - $this->installSchema('system', array('key_value_expire')); - } - - function viewsData() { - $data = parent::viewsData(); - $data['views_test_data']['age']['filter']['id'] = 'in_operator'; - return $data; - } - - public function testFilterInOperatorSimple() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Add a in_operator ordering. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'age' => array( - 'id' => 'age', - 'field' => 'age', - 'table' => 'views_test_data', - 'value' => array(26, 30), - 'operator' => 'in', - ), - )); - - $this->executeView($view); - - $expected_result = array( - array( - 'name' => 'Paul', - 'age' => 26, - ), - array( - 'name' => 'Meredith', - 'age' => 30, - ), - ); - - $this->assertEqual(2, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - - $view->destroy(); - $view->setDisplay(); - - // Add a in_operator ordering. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'age' => array( - 'id' => 'age', - 'field' => 'age', - 'table' => 'views_test_data', - 'value' => array(26, 30), - 'operator' => 'not in', - ), - )); - - $this->executeView($view); - - $expected_result = array( - array( - 'name' => 'John', - 'age' => 25, - ), - array( - 'name' => 'George', - 'age' => 27, - ), - array( - 'name' => 'Ringo', - 'age' => 28, - ), - ); - - $this->assertEqual(3, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - } - - public function testFilterInOperatorGroupedExposedSimple() { - $filters = $this->getGroupedExposedFilters(); - $view = Views::getView('test_view'); - - // Filter: Age, Operator: in, Value: 26, 30 - $filters['age']['group_info']['default_group'] = 1; - $view->setDisplay(); - $view->displayHandlers->get('default')->overrideOption('filters', $filters); - - $this->executeView($view); - - $expected_result = array( - array( - 'name' => 'Paul', - 'age' => 26, - ), - array( - 'name' => 'Meredith', - 'age' => 30, - ), - ); - - $this->assertEqual(2, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - } - - public function testFilterNotInOperatorGroupedExposedSimple() { - $filters = $this->getGroupedExposedFilters(); - $view = Views::getView('test_view'); - - // Filter: Age, Operator: in, Value: 26, 30 - $filters['age']['group_info']['default_group'] = 2; - $view->setDisplay(); - $view->displayHandlers->get('default')->overrideOption('filters', $filters); - - $this->executeView($view); - - $expected_result = array( - array( - 'name' => 'John', - 'age' => 25, - ), - array( - 'name' => 'George', - 'age' => 27, - ), - array( - 'name' => 'Ringo', - 'age' => 28, - ), - ); - - $this->assertEqual(3, count($view->result)); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - } - - protected function getGroupedExposedFilters() { - $filters = array( - 'age' => array( - 'id' => 'age', - 'table' => 'views_test_data', - 'field' => 'age', - 'relationship' => 'none', - 'exposed' => TRUE, - 'expose' => array( - 'operator' => 'age_op', - 'label' => 'age', - 'identifier' => 'age', - ), - 'is_grouped' => TRUE, - 'group_info' => array( - 'label' => 'age', - 'identifier' => 'age', - 'default_group' => 'All', - 'group_items' => array( - 1 => array( - 'title' => 'Age is one of 26, 30', - 'operator' => 'in', - 'value' => array(26, 30), - ), - 2 => array( - 'title' => 'Age is not one of 26, 30', - 'operator' => 'not in', - 'value' => array(26, 30), - ), - ), - ), - ), - ); - return $filters; - } - -} diff --git a/core/modules/views/src/Tests/Handler/FilterNumericTest.php b/core/modules/views/src/Tests/Handler/FilterNumericTest.php deleted file mode 100644 index 2da40ce..0000000 --- a/core/modules/views/src/Tests/Handler/FilterNumericTest.php +++ /dev/null @@ -1,436 +0,0 @@ - 'name', - 'views_test_data_age' => 'age', - ); - - protected function setUp() { - parent::setUp(); - - $this->installSchema('system', array('key_value_expire')); - } - - function viewsData() { - $data = parent::viewsData(); - $data['views_test_data']['age']['filter']['allow empty'] = TRUE; - $data['views_test_data']['id']['filter']['allow empty'] = FALSE; - - return $data; - } - - public function testFilterNumericSimple() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'age' => array( - 'id' => 'age', - 'table' => 'views_test_data', - 'field' => 'age', - 'relationship' => 'none', - 'operator' => '=', - 'value' => array('value' => 28), - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'Ringo', - 'age' => 28, - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - public function testFilterNumericExposedGroupedSimple() { - $filters = $this->getGroupedExposedFilters(); - $view = Views::getView('test_view'); - $view->newDisplay('page', 'Page', 'page_1'); - - // Filter: Age, Operator: =, Value: 28 - $filters['age']['group_info']['default_group'] = 1; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'Ringo', - 'age' => 28, - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - public function testFilterNumericBetween() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'age' => array( - 'id' => 'age', - 'table' => 'views_test_data', - 'field' => 'age', - 'relationship' => 'none', - 'operator' => 'between', - 'value' => array( - 'min' => 26, - 'max' => 29, - ), - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'George', - 'age' => 27, - ), - array( - 'name' => 'Ringo', - 'age' => 28, - ), - array( - 'name' => 'Paul', - 'age' => 26, - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - - // test not between - $view->destroy(); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'age' => array( - 'id' => 'age', - 'table' => 'views_test_data', - 'field' => 'age', - 'relationship' => 'none', - 'operator' => 'not between', - 'value' => array( - 'min' => 26, - 'max' => 29, - ), - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - 'age' => 25, - ), - array( - 'name' => 'Paul', - 'age' => 26, - ), - array( - 'name' => 'Meredith', - 'age' => 30, - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - public function testFilterNumericExposedGroupedBetween() { - $filters = $this->getGroupedExposedFilters(); - $view = Views::getView('test_view'); - $view->newDisplay('page', 'Page', 'page_1'); - - // Filter: Age, Operator: between, Value: 26 and 29 - $filters['age']['group_info']['default_group'] = 2; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'George', - 'age' => 27, - ), - array( - 'name' => 'Ringo', - 'age' => 28, - ), - array( - 'name' => 'Paul', - 'age' => 26, - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - public function testFilterNumericExposedGroupedNotBetween() { - $filters = $this->getGroupedExposedFilters(); - $view = Views::getView('test_view'); - $view->newDisplay('page', 'Page', 'page_1'); - - // Filter: Age, Operator: between, Value: 26 and 29 - $filters['age']['group_info']['default_group'] = 3; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - 'age' => 25, - ), - array( - 'name' => 'Paul', - 'age' => 26, - ), - array( - 'name' => 'Meredith', - 'age' => 30, - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - public function testFilterNumericEmpty() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'age' => array( - 'id' => 'age', - 'table' => 'views_test_data', - 'field' => 'age', - 'relationship' => 'none', - 'operator' => 'empty', - ), - )); - - $this->executeView($view); - $resultset = array( - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - - $view->destroy(); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'age' => array( - 'id' => 'age', - 'table' => 'views_test_data', - 'field' => 'age', - 'relationship' => 'none', - 'operator' => 'not empty', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - 'age' => 25, - ), - array( - 'name' => 'George', - 'age' => 27, - ), - array( - 'name' => 'Ringo', - 'age' => 28, - ), - array( - 'name' => 'Paul', - 'age' => 26, - ), - array( - 'name' => 'Meredith', - 'age' => 30, - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - public function testFilterNumericExposedGroupedEmpty() { - $filters = $this->getGroupedExposedFilters(); - $view = Views::getView('test_view'); - $view->newDisplay('page', 'Page', 'page_1'); - - // Filter: Age, Operator: empty, Value: - $filters['age']['group_info']['default_group'] = 4; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - $resultset = array( - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - public function testFilterNumericExposedGroupedNotEmpty() { - $filters = $this->getGroupedExposedFilters(); - $view = Views::getView('test_view'); - $view->newDisplay('page', 'Page', 'page_1'); - - // Filter: Age, Operator: empty, Value: - $filters['age']['group_info']['default_group'] = 5; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - 'age' => 25, - ), - array( - 'name' => 'George', - 'age' => 27, - ), - array( - 'name' => 'Ringo', - 'age' => 28, - ), - array( - 'name' => 'Paul', - 'age' => 26, - ), - array( - 'name' => 'Meredith', - 'age' => 30, - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - public function testAllowEmpty() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'id' => array( - 'id' => 'id', - 'table' => 'views_test_data', - 'field' => 'id', - 'relationship' => 'none', - ), - 'age' => array( - 'id' => 'age', - 'table' => 'views_test_data', - 'field' => 'age', - 'relationship' => 'none', - ), - )); - - $view->initHandlers(); - - $id_operators = $view->filter['id']->operators(); - $age_operators = $view->filter['age']->operators(); - - $this->assertFalse(isset($id_operators['empty'])); - $this->assertFalse(isset($id_operators['not empty'])); - $this->assertTrue(isset($age_operators['empty'])); - $this->assertTrue(isset($age_operators['not empty'])); - } - - protected function getGroupedExposedFilters() { - $filters = array( - 'age' => array( - 'id' => 'age', - 'plugin_id' => 'numeric', - 'table' => 'views_test_data', - 'field' => 'age', - 'relationship' => 'none', - 'exposed' => TRUE, - 'expose' => array( - 'operator' => 'age_op', - 'label' => 'age', - 'identifier' => 'age', - ), - 'is_grouped' => TRUE, - 'group_info' => array( - 'label' => 'age', - 'identifier' => 'age', - 'default_group' => 'All', - 'group_items' => array( - 1 => array( - 'title' => 'Age is 28', - 'operator' => '=', - 'value' => array('value' => 28), - ), - 2 => array( - 'title' => 'Age is between 26 and 29', - 'operator' => 'between', - 'value' => array( - 'min' => 26, - 'max' => 29, - ), - ), - 3 => array( - 'title' => 'Age is not between 26 and 29', - 'operator' => 'not between', - 'value' => array( - 'min' => 26, - 'max' => 29, - ), - ), - 4 => array( - 'title' => 'Age is empty', - 'operator' => 'empty', - ), - 5 => array( - 'title' => 'Age is not empty', - 'operator' => 'not empty', - ), - ), - ), - ), - ); - return $filters; - } - -} diff --git a/core/modules/views/src/Tests/Handler/FilterStringTest.php b/core/modules/views/src/Tests/Handler/FilterStringTest.php deleted file mode 100644 index acd3f50..0000000 --- a/core/modules/views/src/Tests/Handler/FilterStringTest.php +++ /dev/null @@ -1,869 +0,0 @@ - 'name', - ); - - protected function setUp() { - parent::setUp(); - - $this->installSchema('system', array('key_value_expire')); - } - - function viewsData() { - $data = parent::viewsData(); - $data['views_test_data']['name']['filter']['allow empty'] = TRUE; - $data['views_test_data']['job']['filter']['allow empty'] = FALSE; - $data['views_test_data']['description'] = $data['views_test_data']['name']; - - return $data; - } - - protected function schemaDefinition() { - $schema = parent::schemaDefinition(); - $schema['views_test_data']['fields']['description'] = array( - 'description' => "A person's description", - 'type' => 'text', - 'not null' => FALSE, - 'size' => 'big', - ); - - return $schema; - } - - /** - * An extended test dataset. - */ - protected function dataSet() { - $dataset = parent::dataSet(); - $dataset[0]['description'] = 'John Winston Ono Lennon, MBE (9 October 1940 – 8 December 1980) was an English musician and singer-songwriter who rose to worldwide fame as one of the founding members of The Beatles, one of the most commercially successful and critically acclaimed acts in the history of popular music. Along with fellow Beatle Paul McCartney, he formed one of the most successful songwriting partnerships of the 20th century.'; - $dataset[1]['description'] = 'George Harrison,[1] MBE (25 February 1943 – 29 November 2001)[2] was an English rock guitarist, singer-songwriter, actor and film producer who achieved international fame as lead guitarist of The Beatles.'; - $dataset[2]['description'] = 'Richard Starkey, MBE (born 7 July 1940), better known by his stage name Ringo Starr, is an English musician, singer-songwriter, and actor who gained worldwide fame as the drummer for The Beatles.'; - $dataset[3]['description'] = 'Sir James Paul McCartney, MBE (born 18 June 1942) is an English musician, singer-songwriter and composer. Formerly of The Beatles (1960–1970) and Wings (1971–1981), McCartney is the most commercially successful songwriter in the history of popular music, according to Guinness World Records.[1]'; - $dataset[4]['description'] = NULL; - - return $dataset; - } - - /** - * Build and return a Page view of the views_test_data table. - * - * @return view - */ - protected function getBasicPageView() { - $view = Views::getView('test_view'); - - // In order to test exposed filters, we have to disable - // the exposed forms cache. - \Drupal::service('views.exposed_form_cache')->reset(); - - $view->newDisplay('page', 'Page', 'page_1'); - return $view; - } - - function testFilterStringEqual() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'operator' => '=', - 'value' => 'Ringo', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'Ringo', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringGroupedExposedEqual() { - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Name, Operator: =, Value: Ringo - $filters['name']['group_info']['default_group'] = 1; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - - $resultset = array( - array( - 'name' => 'Ringo', - ), - ); - - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringNotEqual() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'operator' => '!=', - 'value' => 'Ringo', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - ), - array( - 'name' => 'George', - ), - array( - 'name' => 'Paul', - ), - array( - 'name' => 'Meredith', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringGroupedExposedNotEqual() { - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Name, Operator: !=, Value: Ringo - $filters['name']['group_info']['default_group'] = '2'; - - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - - $resultset = array( - array( - 'name' => 'John', - ), - array( - 'name' => 'George', - ), - array( - 'name' => 'Paul', - ), - array( - 'name' => 'Meredith', - ), - ); - - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringContains() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'operator' => 'contains', - 'value' => 'ing', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'Ringo', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - - function testFilterStringGroupedExposedContains() { - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Name, Operator: contains, Value: ing - $filters['name']['group_info']['default_group'] = '3'; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - - $resultset = array( - array( - 'name' => 'Ringo', - ), - ); - - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - - function testFilterStringWord() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'description' => array( - 'id' => 'description', - 'table' => 'views_test_data', - 'field' => 'description', - 'relationship' => 'none', - 'operator' => 'word', - 'value' => 'actor', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'George', - ), - array( - 'name' => 'Ringo', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - $view->destroy(); - - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'description' => array( - 'id' => 'description', - 'table' => 'views_test_data', - 'field' => 'description', - 'relationship' => 'none', - 'operator' => 'allwords', - 'value' => 'Richard Starkey', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'Ringo', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - - function testFilterStringGroupedExposedWord() { - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Name, Operator: contains, Value: ing - $filters['name']['group_info']['default_group'] = '3'; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - - $resultset = array( - array( - 'name' => 'Ringo', - ), - ); - - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - $view->destroy(); - - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Description, Operator: contains, Value: actor - $filters['description']['group_info']['default_group'] = '1'; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'George', - ), - array( - 'name' => 'Ringo', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringStarts() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'description' => array( - 'id' => 'description', - 'table' => 'views_test_data', - 'field' => 'description', - 'relationship' => 'none', - 'operator' => 'starts', - 'value' => 'George', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'George', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringGroupedExposedStarts() { - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Name, Operator: starts, Value: George - $filters['description']['group_info']['default_group'] = 2; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - - $resultset = array( - array( - 'name' => 'George', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringNotStarts() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'description' => array( - 'id' => 'description', - 'table' => 'views_test_data', - 'field' => 'description', - 'relationship' => 'none', - 'operator' => 'not_starts', - 'value' => 'George', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - ), - array( - 'name' => 'Ringo', - ), - array( - 'name' => 'Paul', - ), - // There is no Meredith returned because his description is empty - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringGroupedExposedNotStarts() { - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Name, Operator: not_starts, Value: George - $filters['description']['group_info']['default_group'] = 3; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - - $resultset = array( - array( - 'name' => 'John', - ), - array( - 'name' => 'Ringo', - ), - array( - 'name' => 'Paul', - ), - // There is no Meredith returned because his description is empty - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringEnds() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'description' => array( - 'id' => 'description', - 'table' => 'views_test_data', - 'field' => 'description', - 'relationship' => 'none', - 'operator' => 'ends', - 'value' => 'Beatles.', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'George', - ), - array( - 'name' => 'Ringo', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringGroupedExposedEnds() { - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Description, Operator: ends, Value: Beatles - $filters['description']['group_info']['default_group'] = 4; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - - $resultset = array( - array( - 'name' => 'George', - ), - array( - 'name' => 'Ringo', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringNotEnds() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'description' => array( - 'id' => 'description', - 'table' => 'views_test_data', - 'field' => 'description', - 'relationship' => 'none', - 'operator' => 'not_ends', - 'value' => 'Beatles.', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - ), - array( - 'name' => 'Paul', - ), - // There is no Meredith returned because his description is empty - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringGroupedExposedNotEnds() { - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Description, Operator: not_ends, Value: Beatles - $filters['description']['group_info']['default_group'] = 5; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - - $resultset = array( - array( - 'name' => 'John', - ), - array( - 'name' => 'Paul', - ), - // There is no Meredith returned because his description is empty - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringNot() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'description' => array( - 'id' => 'description', - 'table' => 'views_test_data', - 'field' => 'description', - 'relationship' => 'none', - 'operator' => 'not', - 'value' => 'Beatles.', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - ), - array( - 'name' => 'Paul', - ), - // There is no Meredith returned because his description is empty - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - - function testFilterStringGroupedExposedNot() { - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Description, Operator: not (does not contains), Value: Beatles - $filters['description']['group_info']['default_group'] = 6; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - - $resultset = array( - array( - 'name' => 'John', - ), - array( - 'name' => 'Paul', - ), - // There is no Meredith returned because his description is empty - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - - } - - function testFilterStringShorter() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'operator' => 'shorterthan', - 'value' => 5, - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - ), - array( - 'name' => 'Paul', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringGroupedExposedShorter() { - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Name, Operator: shorterthan, Value: 5 - $filters['name']['group_info']['default_group'] = 4; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'John', - ), - array( - 'name' => 'Paul', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringLonger() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'operator' => 'longerthan', - 'value' => 7, - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'Meredith', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringGroupedExposedLonger() { - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Name, Operator: longerthan, Value: 4 - $filters['name']['group_info']['default_group'] = 5; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'Meredith', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - - function testFilterStringEmpty() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the filtering - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'description' => array( - 'id' => 'description', - 'table' => 'views_test_data', - 'field' => 'description', - 'relationship' => 'none', - 'operator' => 'empty', - ), - )); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'Meredith', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - function testFilterStringGroupedExposedEmpty() { - $filters = $this->getGroupedExposedFilters(); - $view = $this->getBasicPageView(); - - // Filter: Description, Operator: empty, Value: - $filters['description']['group_info']['default_group'] = 7; - $view->setDisplay('page_1'); - $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); - $view->save(); - $this->container->get('router.builder')->rebuild(); - - $this->executeView($view); - $resultset = array( - array( - 'name' => 'Meredith', - ), - ); - $this->assertIdenticalResultset($view, $resultset, $this->columnMap); - } - - protected function getGroupedExposedFilters() { - $filters = array( - 'name' => array( - 'id' => 'name', - 'plugin_id' => 'string', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - 'exposed' => TRUE, - 'expose' => array( - 'operator' => 'name_op', - 'label' => 'name', - 'identifier' => 'name', - ), - 'is_grouped' => TRUE, - 'group_info' => array( - 'label' => 'name', - 'identifier' => 'name', - 'default_group' => 'All', - 'group_items' => array( - 1 => array( - 'title' => 'Is Ringo', - 'operator' => '=', - 'value' => 'Ringo', - ), - 2 => array( - 'title' => 'Is not Ringo', - 'operator' => '!=', - 'value' => 'Ringo', - ), - 3 => array( - 'title' => 'Contains ing', - 'operator' => 'contains', - 'value' => 'ing', - ), - 4 => array( - 'title' => 'Shorter than 5 letters', - 'operator' => 'shorterthan', - 'value' => 5, - ), - 5 => array( - 'title' => 'Longer than 7 letters', - 'operator' => 'longerthan', - 'value' => 7, - ), - ), - ), - ), - 'description' => array( - 'id' => 'description', - 'plugin_id' => 'string', - 'table' => 'views_test_data', - 'field' => 'description', - 'relationship' => 'none', - 'exposed' => TRUE, - 'expose' => array( - 'operator' => 'description_op', - 'label' => 'description', - 'identifier' => 'description', - ), - 'is_grouped' => TRUE, - 'group_info' => array( - 'label' => 'description', - 'identifier' => 'description', - 'default_group' => 'All', - 'group_items' => array( - 1 => array( - 'title' => 'Contains the word: Actor', - 'operator' => 'word', - 'value' => 'actor', - ), - 2 => array( - 'title' => 'Starts with George', - 'operator' => 'starts', - 'value' => 'George', - ), - 3 => array( - 'title' => 'Not Starts with: George', - 'operator' => 'not_starts', - 'value' => 'George', - ), - 4 => array( - 'title' => 'Ends with: Beatles', - 'operator' => 'ends', - 'value' => 'Beatles.', - ), - 5 => array( - 'title' => 'Not Ends with: Beatles', - 'operator' => 'not_ends', - 'value' => 'Beatles.', - ), - 6 => array( - 'title' => 'Does not contain: Beatles', - 'operator' => 'not', - 'value' => 'Beatles.', - ), - 7 => array( - 'title' => 'Empty description', - 'operator' => 'empty', - 'value' => '', - ), - ), - ), - ), - ); - return $filters; - } - -} diff --git a/core/modules/views/src/Tests/Handler/HandlerAliasTest.php b/core/modules/views/src/Tests/Handler/HandlerAliasTest.php deleted file mode 100644 index 3a229a6..0000000 --- a/core/modules/views/src/Tests/Handler/HandlerAliasTest.php +++ /dev/null @@ -1,89 +0,0 @@ -installEntitySchema('user'); - } - - /** - * {@inheritdoc} - */ - protected function viewsData() { - $data = parent::viewsData(); - // User the existing test_filter plugin. - $data['views_test_data_alias']['table']['real table'] = 'views_test_data'; - $data['views_test_data_alias']['name_alias']['filter']['id'] = 'test_filter'; - $data['views_test_data_alias']['name_alias']['filter']['real field'] = 'name'; - - return $data; - } - - public function testPluginAliases() { - $view = Views::getView('test_filter'); - $view->initDisplay(); - - // Change the filtering. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'test_filter' => array( - 'id' => 'test_filter', - 'table' => 'views_test_data_alias', - 'field' => 'name_alias', - 'operator' => '=', - 'value' => 'John', - 'group' => 0, - ), - )); - - $this->executeView($view); - - $filter = $view->filter['test_filter']; - - // Check the definition values are present. - $this->assertIdentical($filter->definition['real table'], 'views_test_data'); - $this->assertIdentical($filter->definition['real field'], 'name'); - - $this->assertIdentical($filter->table, 'views_test_data'); - $this->assertIdentical($filter->realField, 'name'); - - // Test an existing user uid field. - $view = Views::getView('test_alias'); - $view->initDisplay(); - $this->executeView($view); - - $filter = $view->filter['uid_raw']; - - $this->assertIdentical($filter->definition['real field'], 'uid'); - - $this->assertIdentical($filter->field, 'uid_raw'); - $this->assertIdentical($filter->table, 'users_field_data'); - $this->assertIdentical($filter->realField, 'uid'); - } - -} diff --git a/core/modules/views/src/Tests/Handler/HandlerAllTest.php b/core/modules/views/src/Tests/Handler/HandlerAllTest.php index 84579a8..4c321a8 100644 --- a/core/modules/views/src/Tests/Handler/HandlerAllTest.php +++ b/core/modules/views/src/Tests/Handler/HandlerAllTest.php @@ -11,6 +11,7 @@ use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\HandlerBase; use Drupal\views\Plugin\views\filter\InOperator; +use Drupal\views\Entity\View; /** * Tests instances of all handlers. @@ -59,7 +60,7 @@ public function testHandlers() { continue; } - $view = entity_create('view', array('base_table' => $base_table)); + $view = View::create(array('base_table' => $base_table)); $view = $view->getExecutable(); // @todo The groupwise relationship is currently broken. diff --git a/core/modules/views/src/Tests/Handler/RelationshipTest.php b/core/modules/views/src/Tests/Handler/RelationshipTest.php deleted file mode 100644 index 884aca6..0000000 --- a/core/modules/views/src/Tests/Handler/RelationshipTest.php +++ /dev/null @@ -1,184 +0,0 @@ - 'name', - 'users_field_data_views_test_data_uid' => 'uid', - ); - - /** - * Tests the query result of a view with a relationship. - */ - public function testRelationshipQuery() { - // Set the first entry to have the admin as author. - db_query("UPDATE {views_test_data} SET uid = 1 WHERE id = 1"); - db_query("UPDATE {views_test_data} SET uid = 2 WHERE id <> 1"); - - $view = Views::getView('test_view'); - $view->setDisplay(); - - $view->displayHandlers->get('default')->overrideOption('relationships', array( - 'uid' => array( - 'id' => 'uid', - 'table' => 'views_test_data', - 'field' => 'uid', - ), - )); - - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'uid' => array( - 'id' => 'uid', - 'table' => 'users_field_data', - 'field' => 'uid', - 'relationship' => 'uid', - ), - )); - - $fields = $view->displayHandlers->get('default')->getOption('fields'); - $view->displayHandlers->get('default')->overrideOption('fields', $fields + array( - 'uid' => array( - 'id' => 'uid', - 'table' => 'users_field_data', - 'field' => 'uid', - 'relationship' => 'uid', - ), - )); - - $view->initHandlers(); - - // Check for all beatles created by admin. - $view->filter['uid']->value = array(1); - $this->executeView($view); - - $expected_result = array( - array( - 'name' => 'John', - 'uid' => 1 - ) - ); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - $view->destroy(); - - // Check for all beatles created by another user, which so doesn't exist. - $view->initHandlers(); - $view->filter['uid']->value = array(3); - $this->executeView($view); - $expected_result = array(); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - $view->destroy(); - - // Set the relationship to required, so only results authored by the admin - // should return. - $view->initHandlers(); - $view->relationship['uid']->options['required'] = TRUE; - $this->executeView($view); - - $expected_result = array( - array( - 'name' => 'John', - 'uid' => 1 - ) - ); - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - $view->destroy(); - - // Set the relationship to optional should cause to return all beatles. - $view->initHandlers(); - $view->relationship['uid']->options['required'] = FALSE; - $this->executeView($view); - - $expected_result = $this->dataSet(); - // Alter the expected result to contain the right uids. - foreach ($expected_result as &$row) { - // Only John has an existing author. - if ($row['name'] == 'John') { - $row['uid'] = 1; - } - else { - // The LEFT join should set an empty {users}.uid field. - $row['uid'] = NULL; - } - } - - $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); - } - - /** - * Tests rendering of a view with a relationship. - */ - public function testRelationshipRender() { - $author1 = $this->createUser(); - db_query("UPDATE {views_test_data} SET uid = :uid WHERE id = 1", [':uid' => $author1->id()]); - $author2 = $this->createUser(); - db_query("UPDATE {views_test_data} SET uid = :uid WHERE id = 2", [':uid' => $author2->id()]); - // Set uid to non-existing author uid for row 3. - db_query("UPDATE {views_test_data} SET uid = :uid WHERE id = 3", [':uid' => $author2->id() + 123]); - - $view = Views::getView('test_view'); - // Add a relationship for authors. - $view->getDisplay()->overrideOption('relationships', [ - 'uid' => [ - 'id' => 'uid', - 'table' => 'views_test_data', - 'field' => 'uid', - ], - ]); - // Add fields for {views_test_data}.id and author name. - $view->getDisplay()->overrideOption('fields', [ - 'id' => [ - 'id' => 'id', - 'table' => 'views_test_data', - 'field' => 'id', - ], - 'author' => [ - 'id' => 'author', - 'table' => 'users_field_data', - 'field' => 'name', - 'relationship' => 'uid', - ], - ]); - - // Render the view. - $output = $view->preview(); - $html = $this->container->get('renderer')->renderRoot($output); - $this->setRawContent($html); - - // Check that the output contains correct values. - $xpath = '//div[@class="views-row" and div[@class="views-field views-field-id"]=:id and div[@class="views-field views-field-author"]=:author]'; - $this->assertEqual(1, count($this->xpath($xpath, [':id' => 1, ':author' => $author1->getUsername()]))); - $this->assertEqual(1, count($this->xpath($xpath, [':id' => 2, ':author' => $author2->getUsername()]))); - $this->assertEqual(1, count($this->xpath($xpath, [':id' => 3, ':author' => '']))); - } - -} diff --git a/core/modules/views/src/Tests/Handler/SortDateTest.php b/core/modules/views/src/Tests/Handler/SortDateTest.php deleted file mode 100644 index b494cf6..0000000 --- a/core/modules/views/src/Tests/Handler/SortDateTest.php +++ /dev/null @@ -1,208 +0,0 @@ - 'John'), - array('name' => 'Paul'), - array('name' => 'Meredith'), - array('name' => 'Ringo'), - array('name' => 'George'), - ); - break; - case 'minute': - $expected = array( - array('name' => 'John'), - array('name' => 'Paul'), - array('name' => 'Ringo'), - array('name' => 'Meredith'), - array('name' => 'George'), - ); - break; - case 'hour': - $expected = array( - array('name' => 'John'), - array('name' => 'Ringo'), - array('name' => 'Paul'), - array('name' => 'Meredith'), - array('name' => 'George'), - ); - break; - case 'day': - $expected = array( - array('name' => 'John'), - array('name' => 'Ringo'), - array('name' => 'Paul'), - array('name' => 'Meredith'), - array('name' => 'George'), - ); - break; - case 'month': - $expected = array( - array('name' => 'John'), - array('name' => 'George'), - array('name' => 'Ringo'), - array('name' => 'Paul'), - array('name' => 'Meredith'), - ); - break; - case 'year': - $expected = array( - array('name' => 'John'), - array('name' => 'George'), - array('name' => 'Ringo'), - array('name' => 'Paul'), - array('name' => 'Meredith'), - ); - break; - } - } - else { - switch ($granularity) { - case 'second': - $expected = array( - array('name' => 'George'), - array('name' => 'Ringo'), - array('name' => 'Meredith'), - array('name' => 'Paul'), - array('name' => 'John'), - ); - break; - case 'minute': - $expected = array( - array('name' => 'George'), - array('name' => 'Ringo'), - array('name' => 'Meredith'), - array('name' => 'Paul'), - array('name' => 'John'), - ); - break; - case 'hour': - $expected = array( - array('name' => 'George'), - array('name' => 'Ringo'), - array('name' => 'Paul'), - array('name' => 'Meredith'), - array('name' => 'John'), - ); - break; - case 'day': - $expected = array( - array('name' => 'George'), - array('name' => 'John'), - array('name' => 'Ringo'), - array('name' => 'Paul'), - array('name' => 'Meredith'), - ); - break; - case 'month': - $expected = array( - array('name' => 'John'), - array('name' => 'George'), - array('name' => 'Ringo'), - array('name' => 'Paul'), - array('name' => 'Meredith'), - ); - break; - case 'year': - $expected = array( - array('name' => 'John'), - array('name' => 'George'), - array('name' => 'Ringo'), - array('name' => 'Paul'), - array('name' => 'Meredith'), - ); - break; - } - } - - return $expected; - } - - /** - * Tests numeric ordering of the result set. - */ - public function testDateOrdering() { - foreach (array('second', 'minute', 'hour', 'day', 'month', 'year') as $granularity) { - foreach (array(FALSE, TRUE) as $reverse) { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the fields. - $view->displayHandlers->get('default')->overrideOption('fields', array( - 'name' => array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - ), - 'created' => array( - 'id' => 'created', - 'table' => 'views_test_data', - 'field' => 'created', - 'relationship' => 'none', - ), - )); - - // Change the ordering - $view->displayHandlers->get('default')->overrideOption('sorts', array( - 'created' => array( - 'id' => 'created', - 'table' => 'views_test_data', - 'field' => 'created', - 'relationship' => 'none', - 'granularity' => $granularity, - 'order' => $reverse ? 'DESC' : 'ASC', - ), - 'id' => array( - 'id' => 'id', - 'table' => 'views_test_data', - 'field' => 'id', - 'relationship' => 'none', - 'order' => 'ASC', - ), - )); - - // Execute the view. - $this->executeView($view); - - // Verify the result. - $this->assertEqual(count($this->dataSet()), count($view->result), 'The number of returned rows match.'); - $this->assertIdenticalResultset($view, $this->expectedResultSet($granularity, $reverse), array( - 'views_test_data_name' => 'name', - ), SafeMarkup::format('Result is returned correctly when ordering by granularity @granularity, @reverse.', array('@granularity' => $granularity, '@reverse' => $reverse ? 'reverse' : 'forward'))); - $view->destroy(); - unset($view); - } - } - } - -} diff --git a/core/modules/views/src/Tests/Handler/SortRandomTest.php b/core/modules/views/src/Tests/Handler/SortRandomTest.php deleted file mode 100644 index 9a9b5a6..0000000 --- a/core/modules/views/src/Tests/Handler/SortRandomTest.php +++ /dev/null @@ -1,144 +0,0 @@ - 'name_' . $i, - 'age' => $i, - 'job' => 'job_' . $i, - 'created' => rand(0, time()), - 'status' => 1, - ); - } - return $data; - } - - /** - * Return a basic view with random ordering. - */ - protected function getBasicRandomView() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Add a random ordering. - $view->displayHandlers->get('default')->overrideOption('sorts', array( - 'random' => array( - 'id' => 'random', - 'field' => 'random', - 'table' => 'views', - ), - )); - - return $view; - } - - /** - * Tests random ordering of the result set. - * - * @see DatabaseSelectTestCase::testRandomOrder() - */ - public function testRandomOrdering() { - // Execute a basic view first. - $view = Views::getView('test_view'); - $this->executeView($view); - - // Verify the result. - $this->assertEqual(count($this->dataSet()), count($view->result), 'The number of returned rows match.'); - $this->assertIdenticalResultset($view, $this->dataSet(), array( - 'views_test_data_name' => 'name', - 'views_test_data_age' => 'age', - )); - - // Execute a random view, we expect the result set to be different. - $view_random = $this->getBasicRandomView(); - $this->executeView($view_random); - $this->assertEqual(count($this->dataSet()), count($view_random->result), 'The number of returned rows match.'); - $this->assertNotIdenticalResultset($view_random, $view->result, array( - 'views_test_data_name' => 'views_test_data_name', - 'views_test_data_age' => 'views_test_data_name', - )); - - // Execute a second random view, we expect the result set to be different again. - $view_random_2 = $this->getBasicRandomView(); - $this->executeView($view_random_2); - $this->assertEqual(count($this->dataSet()), count($view_random_2->result), 'The number of returned rows match.'); - $this->assertNotIdenticalResultset($view_random, $view->result, array( - 'views_test_data_name' => 'views_test_data_name', - 'views_test_data_age' => 'views_test_data_name', - )); - } - - /** - * Tests random ordering with tags based caching. - * - * The random sorting should opt out of caching by defining a max age of 0. - * At the same time, the row render caching still works. - */ - public function testRandomOrderingWithRenderCaching() { - $view_random = $this->getBasicRandomView(); - - $display = &$view_random->storage->getDisplay('default'); - $display['display_options']['cache'] = [ - 'type' => 'tag', - ]; - - $view_random->storage->save(); - - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = \Drupal::service('renderer'); - /** @var \Drupal\Core\Render\RenderCacheInterface $render_cache */ - $render_cache = \Drupal::service('render_cache'); - - $original = $build = DisplayPluginBase::buildBasicRenderable($view_random->id(), 'default'); - $result = $renderer->renderPlain($build); - - $original['#cache'] += ['contexts' => []]; - $original['#cache']['contexts'] = Cache::mergeContexts($original['#cache']['contexts'], $this->container->getParameter('renderer.config')['required_cache_contexts']); - - $this->assertFalse($render_cache->get($original), 'Ensure there is no render cache entry.'); - - $build = DisplayPluginBase::buildBasicRenderable($view_random->id(), 'default'); - $result2 = $renderer->renderPlain($build); - - // Ensure that the random ordering works and don't produce the same result. - $this->assertNotEqual($result, $result2); - } - -} diff --git a/core/modules/views/src/Tests/Handler/SortTest.php b/core/modules/views/src/Tests/Handler/SortTest.php deleted file mode 100644 index e050a3f..0000000 --- a/core/modules/views/src/Tests/Handler/SortTest.php +++ /dev/null @@ -1,133 +0,0 @@ -setDisplay(); - - // Change the ordering - $view->displayHandlers->get('default')->overrideOption('sorts', array( - 'age' => array( - 'order' => 'ASC', - 'id' => 'age', - 'table' => 'views_test_data', - 'field' => 'age', - 'relationship' => 'none', - ), - )); - - // Execute the view. - $this->executeView($view); - - // Verify the result. - $this->assertEqual(count($this->dataSet()), count($view->result), 'The number of returned rows match.'); - $this->assertIdenticalResultset($view, $this->orderResultSet($this->dataSet(), 'age'), array( - 'views_test_data_name' => 'name', - 'views_test_data_age' => 'age', - )); - - $view->destroy(); - $view->setDisplay(); - - // Reverse the ordering - $view->displayHandlers->get('default')->overrideOption('sorts', array( - 'age' => array( - 'order' => 'DESC', - 'id' => 'age', - 'table' => 'views_test_data', - 'field' => 'age', - 'relationship' => 'none', - ), - )); - - // Execute the view. - $this->executeView($view); - - // Verify the result. - $this->assertEqual(count($this->dataSet()), count($view->result), 'The number of returned rows match.'); - $this->assertIdenticalResultset($view, $this->orderResultSet($this->dataSet(), 'age', TRUE), array( - 'views_test_data_name' => 'name', - 'views_test_data_age' => 'age', - )); - } - - /** - * Tests string ordering of the result set. - */ - public function testStringOrdering() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Change the ordering - $view->displayHandlers->get('default')->overrideOption('sorts', array( - 'name' => array( - 'order' => 'ASC', - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - ), - )); - - // Execute the view. - $this->executeView($view); - - // Verify the result. - $this->assertEqual(count($this->dataSet()), count($view->result), 'The number of returned rows match.'); - $this->assertIdenticalResultset($view, $this->orderResultSet($this->dataSet(), 'name'), array( - 'views_test_data_name' => 'name', - 'views_test_data_age' => 'age', - )); - - $view->destroy(); - $view->setDisplay(); - - // Reverse the ordering - $view->displayHandlers->get('default')->overrideOption('sorts', array( - 'name' => array( - 'order' => 'DESC', - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'relationship' => 'none', - ), - )); - - // Execute the view. - $this->executeView($view); - - // Verify the result. - $this->assertEqual(count($this->dataSet()), count($view->result), 'The number of returned rows match.'); - $this->assertIdenticalResultset($view, $this->orderResultSet($this->dataSet(), 'name', TRUE), array( - 'views_test_data_name' => 'name', - 'views_test_data_age' => 'age', - )); - } - -} diff --git a/core/modules/views/src/Tests/ModuleTest.php b/core/modules/views/src/Tests/ModuleTest.php deleted file mode 100644 index 0c7ff8b..0000000 --- a/core/modules/views/src/Tests/ModuleTest.php +++ /dev/null @@ -1,377 +0,0 @@ - $this->randomMachineName(), - 'field' => $this->randomMachineName(), - ); - $handler = $this->container->get('plugin.manager.views.' . $type)->getHandler($item); - $this->assertEqual('Drupal\views\Plugin\views\\' . $type . '\Broken', get_class($handler), new FormattableMarkup('Make sure that a broken handler of type: @type is created.', ['@type' => $type])); - } - - $views_data = $this->viewsData(); - $test_tables = array('views_test_data' => array('id', 'name')); - foreach ($test_tables as $table => $fields) { - foreach ($fields as $field) { - $data = $views_data[$table][$field]; - $item = array( - 'table' => $table, - 'field' => $field, - ); - foreach ($data as $id => $field_data) { - if (!in_array($id, array('title', 'help'))) { - $handler = $this->container->get('plugin.manager.views.' . $id)->getHandler($item); - $this->assertInstanceHandler($handler, $table, $field, $id); - } - } - } - } - - // Test the override handler feature. - $item = array( - 'table' => 'views_test_data', - 'field' => 'job', - ); - $handler = $this->container->get('plugin.manager.views.filter')->getHandler($item, 'standard'); - $this->assertTrue($handler instanceof Standard); - - // @todo Reinstate these tests when the debug() in views_get_handler() is - // restored. - return; - - // Test non-existent tables/fields. - set_error_handler(array($this, 'customErrorHandler')); - $item = array( - 'table' => 'views_test_data', - 'field' => 'field_invalid', - ); - $this->container->get('plugin.manager.views.field')->getHandler($item); - $this->assertTrue(strpos($this->lastErrorMessage, format_string("Missing handler: @table @field @type", array('@table' => 'views_test_data', '@field' => 'field_invalid', '@type' => 'field'))) !== FALSE, 'An invalid field name throws a debug message.'); - unset($this->lastErrorMessage); - - $item = array( - 'table' => 'table_invalid', - 'field' => 'id', - ); - $this->container->get('plugin.manager.views.filter')->getHandler($item); - $this->assertEqual(strpos($this->lastErrorMessage, format_string("Missing handler: @table @field @type", array('@table' => 'table_invalid', '@field' => 'id', '@type' => 'filter'))) !== FALSE, 'An invalid table name throws a debug message.'); - unset($this->lastErrorMessage); - - $item = array( - 'table' => 'table_invalid', - 'field' => 'id', - ); - $this->container->get('plugin.manager.views.filter')->getHandler($item); - $this->assertEqual(strpos($this->lastErrorMessage, format_string("Missing handler: @table @field @type", array('@table' => 'table_invalid', '@field' => 'id', '@type' => 'filter'))) !== FALSE, 'An invalid table name throws a debug message.'); - unset($this->lastErrorMessage); - - restore_error_handler(); - } - - /** - * Defines an error handler which is used in the test. - * - * @param int $error_level - * The level of the error raised. - * @param string $message - * The error message. - * @param string $filename - * The filename that the error was raised in. - * @param int $line - * The line number the error was raised at. - * @param array $context - * An array that points to the active symbol table at the point the error - * occurred. - * - * Because this is registered in set_error_handler(), it has to be public. - * @see set_error_handler() - */ - public function customErrorHandler($error_level, $message, $filename, $line, $context) { - $this->lastErrorMessage = $message; - } - - /** - * Tests the load wrapper/helper functions. - */ - public function testLoadFunctions() { - $this->enableModules(array('field', 'text', 'node')); - $this->installConfig(array('node')); - $storage = $this->container->get('entity.manager')->getStorage('view'); - - // Test views_view_is_enabled/disabled. - $archive = $storage->load('archive'); - $this->assertTrue(views_view_is_disabled($archive), 'views_view_is_disabled works as expected.'); - // Enable the view and check this. - $archive->enable(); - $this->assertTrue(views_view_is_enabled($archive), ' views_view_is_enabled works as expected.'); - - // We can store this now, as we have enabled/disabled above. - $all_views = $storage->loadMultiple(); - - // Test Views::getAllViews(). - $this->assertIdentical(array_keys($all_views), array_keys(Views::getAllViews()), 'Views::getAllViews works as expected.'); - - // Test Views::getEnabledViews(). - $expected_enabled = array_filter($all_views, function($view) { - return views_view_is_enabled($view); - }); - $this->assertIdentical(array_keys($expected_enabled), array_keys(Views::getEnabledViews()), 'Expected enabled views returned.'); - - // Test Views::getDisabledViews(). - $expected_disabled = array_filter($all_views, function($view) { - return views_view_is_disabled($view); - }); - $this->assertIdentical(array_keys($expected_disabled), array_keys(Views::getDisabledViews()), 'Expected disabled views returned.'); - - // Test Views::getViewsAsOptions(). - // Test the $views_only parameter. - $this->assertIdentical(array_keys($all_views), array_keys(Views::getViewsAsOptions(TRUE)), 'Expected option keys for all views were returned.'); - $expected_options = array(); - foreach ($all_views as $id => $view) { - $expected_options[$id] = $view->label(); - } - $this->assertIdentical($expected_options, $this->castSafeStrings(Views::getViewsAsOptions(TRUE)), 'Expected options array was returned.'); - - // Test the default. - $this->assertIdentical($this->formatViewOptions($all_views), $this->castSafeStrings(Views::getViewsAsOptions()), 'Expected options array for all views was returned.'); - // Test enabled views. - $this->assertIdentical($this->formatViewOptions($expected_enabled), $this->castSafeStrings(Views::getViewsAsOptions(FALSE, 'enabled')), 'Expected enabled options array was returned.'); - // Test disabled views. - $this->assertIdentical($this->formatViewOptions($expected_disabled), $this->castSafeStrings(Views::getViewsAsOptions(FALSE, 'disabled')), 'Expected disabled options array was returned.'); - - // Test the sort parameter. - $all_views_sorted = $all_views; - ksort($all_views_sorted); - $this->assertIdentical(array_keys($all_views_sorted), array_keys(Views::getViewsAsOptions(TRUE, 'all', NULL, FALSE, TRUE)), 'All view id keys returned in expected sort order'); - - // Test $exclude_view parameter. - $this->assertFalse(array_key_exists('archive', Views::getViewsAsOptions(TRUE, 'all', 'archive')), 'View excluded from options based on name'); - $this->assertFalse(array_key_exists('archive:default', Views::getViewsAsOptions(FALSE, 'all', 'archive:default')), 'View display excluded from options based on name'); - $this->assertFalse(array_key_exists('archive', Views::getViewsAsOptions(TRUE, 'all', $archive->getExecutable())), 'View excluded from options based on object'); - - // Test the $opt_group parameter. - $expected_opt_groups = array(); - foreach ($all_views as $view) { - foreach ($view->get('display') as $display) { - $expected_opt_groups[$view->id()][$view->id() . ':' . $display['id']] = (string) t('@view : @display', array('@view' => $view->id(), '@display' => $display['id'])); - } - } - $this->assertIdentical($expected_opt_groups, $this->castSafeStrings(Views::getViewsAsOptions(FALSE, 'all', NULL, TRUE)), 'Expected option array for an option group returned.'); - } - - /** - * Tests view enable and disable procedural wrapper functions. - */ - function testStatusFunctions() { - $view = Views::getView('test_view_status')->storage; - - $this->assertFalse($view->status(), 'The view status is disabled.'); - - views_enable_view($view); - $this->assertTrue($view->status(), 'A view has been enabled.'); - $this->assertEqual($view->status(), views_view_is_enabled($view), 'views_view_is_enabled is correct.'); - - views_disable_view($view); - $this->assertFalse($view->status(), 'A view has been disabled.'); - $this->assertEqual(!$view->status(), views_view_is_disabled($view), 'views_view_is_disabled is correct.'); - } - - /** - * Tests the \Drupal\views\Views::fetchPluginNames() method. - */ - public function testViewsFetchPluginNames() { - // All style plugins should be returned, as we have not specified a type. - $plugins = Views::fetchPluginNames('style'); - $definitions = $this->container->get('plugin.manager.views.style')->getDefinitions(); - $expected = array(); - foreach ($definitions as $id =>$definition) { - $expected[$id] = $definition['title']; - } - asort($expected); - $this->assertIdentical(array_keys($plugins), array_keys($expected)); - - // Test using the 'test' style plugin type only returns the test_style and - // mapping_test plugins. - $plugins = Views::fetchPluginNames('style', 'test'); - $this->assertIdentical(array_keys($plugins), array('mapping_test', 'test_style', 'test_template_style')); - - // Test a non existent style plugin type returns no plugins. - $plugins = Views::fetchPluginNames('style', $this->randomString()); - $this->assertIdentical($plugins, array()); - } - - /** - * Tests the \Drupal\views\Views::pluginList() method. - */ - public function testViewsPluginList() { - $plugin_list = Views::pluginList(); - // Only plugins used by 'test_view' should be in the plugin list. - foreach (array('display:default', 'pager:none') as $key) { - list($plugin_type, $plugin_id) = explode(':', $key); - $plugin_def = $this->container->get("plugin.manager.views.$plugin_type")->getDefinition($plugin_id); - - $this->assertTrue(isset($plugin_list[$key]), SafeMarkup::format('The expected @key plugin list key was found.', array('@key' => $key))); - $plugin_details = $plugin_list[$key]; - - $this->assertEqual($plugin_details['type'], $plugin_type, 'The expected plugin type was found.'); - $this->assertEqual($plugin_details['title'], $plugin_def['title'], 'The expected plugin title was found.'); - $this->assertEqual($plugin_details['provider'], $plugin_def['provider'], 'The expected plugin provider was found.'); - $this->assertTrue(in_array('test_view', $plugin_details['views']), 'The test_view View was found in the list of views using this plugin.'); - } - } - - /** - * Tests views.module: views_embed_view(). - */ - public function testViewsEmbedView() { - $this->enableModules(array('user')); - - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = \Drupal::service('renderer'); - - $result = views_embed_view('test_argument'); - $renderer->renderPlain($result); - $this->assertEqual(count($result['view_build']['#view']->result), 5); - - $result = views_embed_view('test_argument', 'default', 1); - $renderer->renderPlain($result); - $this->assertEqual(count($result['view_build']['#view']->result), 1); - - $result = views_embed_view('test_argument', 'default', '1,2'); - $renderer->renderPlain($result); - $this->assertEqual(count($result['view_build']['#view']->result), 2); - - $result = views_embed_view('test_argument', 'default', '1,2', 'John'); - $renderer->renderPlain($result); - $this->assertEqual(count($result['view_build']['#view']->result), 1); - - $result = views_embed_view('test_argument', 'default', '1,2', 'John,George'); - $renderer->renderPlain($result); - $this->assertEqual(count($result['view_build']['#view']->result), 2); - } - - /** - * Tests the \Drupal\views\ViewsExecutable::preview() method. - */ - public function testViewsPreview() { - $this->enableModules(array('user')); - - $view = Views::getView('test_argument'); - $result = $view->preview('default'); - $this->assertEqual(count($result['#view']->result), 5); - - $view = Views::getView('test_argument'); - $result = $view->preview('default', array('0' => 1)); - $this->assertEqual(count($result['#view']->result), 1); - - $view = Views::getView('test_argument'); - $result = $view->preview('default', array('3' => 1)); - $this->assertEqual(count($result['#view']->result), 1); - - $view = Views::getView('test_argument'); - $result = $view->preview('default', array('0' => '1,2')); - $this->assertEqual(count($result['#view']->result), 2); - - $view = Views::getView('test_argument'); - $result = $view->preview('default', array('3' => '1,2')); - $this->assertEqual(count($result['#view']->result), 2); - - $view = Views::getView('test_argument'); - $result = $view->preview('default', array('0' => '1,2', '1' => 'John')); - $this->assertEqual(count($result['#view']->result), 1); - - $view = Views::getView('test_argument'); - $result = $view->preview('default', array('3' => '1,2', '4' => 'John')); - $this->assertEqual(count($result['#view']->result), 1); - - $view = Views::getView('test_argument'); - $result = $view->preview('default', array('0' => '1,2', '1' => 'John,George')); - $this->assertEqual(count($result['#view']->result), 2); - - $view = Views::getView('test_argument'); - $result = $view->preview('default', array('3' => '1,2', '4' => 'John,George')); - $this->assertEqual(count($result['#view']->result), 2); - } - - /** - * Helper to return an expected views option array. - * - * @param array $views - * An array of Drupal\views\Entity\View objects for which to - * create an options array. - * - * @return array - * A formatted options array that matches the expected output. - */ - protected function formatViewOptions(array $views = array()) { - $expected_options = array(); - foreach ($views as $view) { - foreach ($view->get('display') as $display) { - $expected_options[$view->id() . ':' . $display['id']] = (string) t('View: @view - Display: @display', - array('@view' => $view->id(), '@display' => $display['id'])); - } - } - - return $expected_options; - } - - /** - * Ensure that a certain handler is a instance of a certain table/field. - */ - function assertInstanceHandler($handler, $table, $field, $id) { - $table_data = $this->container->get('views.views_data')->get($table); - $field_data = $table_data[$field][$id]; - - $this->assertEqual($field_data['id'], $handler->getPluginId()); - } - -} diff --git a/core/modules/views/src/Tests/Plugin/ArgumentValidatorTest.php b/core/modules/views/src/Tests/Plugin/ArgumentValidatorTest.php deleted file mode 100644 index 08b8984..0000000 --- a/core/modules/views/src/Tests/Plugin/ArgumentValidatorTest.php +++ /dev/null @@ -1,70 +0,0 @@ -initHandlers(); - $this->assertFalse($view->argument['null']->validateArgument($this->randomString())); - // Reset safed argument validation. - $view->argument['null']->argument_validated = NULL; - $this->assertTrue($view->argument['null']->validateArgument(12)); - } - - /** - * Tests the argument validator test plugin. - * - * @see Drupal\views_test_data\Plugin\views\argument_validator\ArgumentValidatorTest - */ - public function testArgumentValidatorPlugin() { - $view = Views::getView('test_view'); - - // Add a new argument and set the test plugin for the argument_validator. - $options = [ - 'specify_validation' => TRUE, - 'validate' => [ - 'type' => 'argument_validator_test' - ] - ]; - $id = $view->addHandler('default', 'argument', 'views_test_data', 'name', $options); - $view->initHandlers(); - - $test_value = $this->randomMachineName(); - - $argument = $view->argument[$id]; - $argument->options['validate_options']['test_value'] = $test_value; - $this->assertFalse($argument->validateArgument($this->randomMachineName()), 'A random value does not validate.'); - // Reset internal flag. - $argument->argument_validated = NULL; - $this->assertTrue($argument->validateArgument($test_value), 'The right argument validates.'); - - $plugin = $argument->getPlugin('argument_validator'); - $this->assertTrue($plugin instanceof ArgumentValidatorTestPlugin, 'The correct argument validator plugin is used.'); - $this->assertFalse($plugin->validateArgument($this->randomMachineName()), 'A random value does not validate.'); - $this->assertTrue($plugin->validateArgument($test_value), 'The right argument validates.'); - } - -} diff --git a/core/modules/views/src/Tests/Plugin/BlockDependenciesTest.php b/core/modules/views/src/Tests/Plugin/BlockDependenciesTest.php deleted file mode 100644 index 6a5064c..0000000 --- a/core/modules/views/src/Tests/Plugin/BlockDependenciesTest.php +++ /dev/null @@ -1,114 +0,0 @@ -createBlock('views_exposed_filter_block:test_exposed_block-page_1'); - $dependencies = $block->calculateDependencies()->getDependencies(); - $expected = array( - 'config' => array('views.view.test_exposed_block'), - 'module' => array('views'), - 'theme' => array('stark') - ); - $this->assertIdentical($expected, $dependencies); - } - - /** - * Tests that exposed filter blocks have the correct dependencies. - * - * @see \Drupal\views\Plugin\Derivative\ViewsBlock::getDerivativeDefinitions() - */ - public function testViewsBlock() { - $block = $this->createBlock('views_block:content_recent-block_1'); - $dependencies = $block->calculateDependencies()->getDependencies(); - $expected = array( - 'config' => array('views.view.content_recent'), - 'module' => array('views'), - 'theme' => array('stark') - ); - $this->assertIdentical($expected, $dependencies); - } - - /** - * Creates a block instance based on default settings. - * - * @param string $plugin_id - * The plugin ID of the block type for this block instance. - * @param array $settings - * (optional) An associative array of settings for the block entity. - * Override the defaults by specifying the key and value in the array, for - * example: - * @code - * $this->createBlock('system_powered_by_block', array( - * 'label' => t('Hello, world!'), - * )); - * @endcode - * The following defaults are provided: - * - label: Random string. - * - id: Random string. - * - region: 'sidebar_first'. - * - theme: The default theme. - * - visibility: Empty array. - * - * @return \Drupal\block\Entity\Block - * The block entity. - */ - protected function createBlock($plugin_id, array $settings = array()) { - $settings += array( - 'plugin' => $plugin_id, - 'region' => 'sidebar_first', - 'id' => strtolower($this->randomMachineName(8)), - 'theme' => $this->config('system.theme')->get('default'), - 'label' => $this->randomMachineName(8), - 'visibility' => array(), - 'weight' => 0, - ); - $values = []; - foreach (array('region', 'id', 'theme', 'plugin', 'weight', 'visibility') as $key) { - $values[$key] = $settings[$key]; - // Remove extra values that do not belong in the settings array. - unset($settings[$key]); - } - foreach ($values['visibility'] as $id => $visibility) { - $values['visibility'][$id]['id'] = $id; - } - $values['settings'] = $settings; - $block = entity_create('block', $values); - $block->save(); - return $block; - } - -} diff --git a/core/modules/views/src/Tests/Plugin/CacheTest.php b/core/modules/views/src/Tests/Plugin/CacheTest.php deleted file mode 100644 index be23770..0000000 --- a/core/modules/views/src/Tests/Plugin/CacheTest.php +++ /dev/null @@ -1,391 +0,0 @@ -installEntitySchema('node'); - $this->installEntitySchema('taxonomy_term'); - $this->installEntitySchema('user'); - - // Setup the current time properly. - \Drupal::request()->server->set('REQUEST_TIME', time()); - } - - /** - * {@inheritdoc} - */ - protected function viewsData() { - $data = parent::viewsData(); - - $data['views_test_data']['test_cache_context'] = [ - 'real field' => 'name', - 'title' => 'Test cache context', - 'filter' => [ - 'id' => 'views_test_test_cache_context', - ], - ]; - - return $data; - } - - - /** - * Tests time based caching. - * - * @see views_plugin_cache_time - */ - public function testTimeResultCaching() { - $view = Views::getView('test_cache'); - $view->setDisplay(); - $view->display_handler->overrideOption('cache', array( - 'type' => 'time', - 'options' => array( - 'results_lifespan' => '3600', - 'output_lifespan' => '3600', - ) - )); - - // Test the default (non-paged) display. - $this->executeView($view); - // Verify the result. - $this->assertEqual(5, count($view->result), 'The number of returned rows match.'); - - // Add another man to the beatles. - $record = array( - 'name' => 'Rod Davis', - 'age' => 29, - 'job' => 'Banjo', - ); - db_insert('views_test_data')->fields($record)->execute(); - - // The result should be the same as before, because of the caching. (Note - // that views_test_data records don't have associated cache tags, and hence - // the results cache items aren't invalidated.) - $view->destroy(); - $this->executeView($view); - // Verify the result. - $this->assertEqual(5, count($view->result), 'The number of returned rows match.'); - } - - /** - * Tests result caching with filters. - * - * @see views_plugin_cache_time - */ - public function testTimeResultCachingWithFilter() { - // Check that we can find the test filter plugin. - $plugin = $this->container->get('plugin.manager.views.filter')->createInstance('test_filter'); - $this->assertTrue($plugin instanceof FilterPlugin, 'Test filter plugin found.'); - - $view = Views::getView('test_filter'); - $view->initDisplay(); - $view->display_handler->overrideOption('cache', array( - 'type' => 'time', - 'options' => array( - 'results_lifespan' => '3600', - 'output_lifespan' => '3600', - ), - )); - - // Change the filtering. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'test_filter' => array( - 'id' => 'test_filter', - 'table' => 'views_test_data', - 'field' => 'name', - 'operator' => '=', - 'value' => 'John', - 'group' => 0, - ), - )); - - $this->executeView($view); - - // Get the cache item. - $cid1 = $view->display_handler->getPlugin('cache')->generateResultsKey(); - - // Build the expected result. - $dataset = array(array('name' => 'John')); - - // Verify the result. - $this->assertEqual(1, count($view->result), 'The number of returned rows match.'); - $this->assertIdenticalResultSet($view, $dataset, array( - 'views_test_data_name' => 'name', - )); - - $view->destroy(); - - $view->initDisplay(); - - // Change the filtering. - $view->displayHandlers->get('default')->overrideOption('filters', array( - 'test_filter' => array( - 'id' => 'test_filter', - 'table' => 'views_test_data', - 'field' => 'name', - 'operator' => '=', - 'value' => 'Ringo', - 'group' => 0, - ), - )); - - $this->executeView($view); - - // Get the cache item. - $cid2 = $view->display_handler->getPlugin('cache')->generateResultsKey(); - $this->assertNotEqual($cid1, $cid2, "Results keys are different."); - - // Build the expected result. - $dataset = array(array('name' => 'Ringo')); - - // Verify the result. - $this->assertEqual(1, count($view->result), 'The number of returned rows match.'); - $this->assertIdenticalResultSet($view, $dataset, array( - 'views_test_data_name' => 'name', - )); - } - - /** - * Tests result caching with a pager. - */ - public function testTimeResultCachingWithPager() { - $view = Views::getView('test_cache'); - $view->setDisplay(); - $view->display_handler->overrideOption('cache', array( - 'type' => 'time', - 'options' => array( - 'results_lifespan' => '3600', - 'output_lifespan' => '3600', - ) - )); - - $mapping = ['views_test_data_name' => 'name']; - - $view->setDisplay('page_1'); - $view->setCurrentPage(0); - $this->executeView($view); - $this->assertIdenticalResultset($view, [['name' => 'John'], ['name' => 'George']], $mapping); - $view->destroy(); - - $view->setDisplay('page_1'); - $view->setCurrentPage(1); - $this->executeView($view); - $this->assertIdenticalResultset($view, [['name' => 'Ringo'], ['name' => 'Paul']], $mapping); - $view->destroy(); - - $view->setDisplay('page_1'); - $view->setCurrentPage(0); - $this->executeView($view); - $this->assertIdenticalResultset($view, [['name' => 'John'], ['name' => 'George']], $mapping); - $view->destroy(); - - $view->setDisplay('page_1'); - $view->setCurrentPage(2); - $this->executeView($view); - $this->assertIdenticalResultset($view, [['name' => 'Meredith']], $mapping); - $view->destroy(); - } - - /** - * Tests no caching. - * - * @see views_plugin_cache_time - */ - function testNoneResultCaching() { - // Create a basic result which just 2 results. - $view = Views::getView('test_cache'); - $view->setDisplay(); - $view->display_handler->overrideOption('cache', array( - 'type' => 'none', - 'options' => array(), - )); - - $this->executeView($view); - // Verify the result. - $this->assertEqual(5, count($view->result), 'The number of returned rows match.'); - - // Add another man to the beatles. - $record = array( - 'name' => 'Rod Davis', - 'age' => 29, - 'job' => 'Banjo', - ); - db_insert('views_test_data')->fields($record)->execute(); - - // The Result changes, because the view is not cached. - $view = Views::getView('test_cache'); - $view->setDisplay(); - $view->display_handler->overrideOption('cache', array( - 'type' => 'none', - 'options' => array(), - )); - - $this->executeView($view); - // Verify the result. - $this->assertEqual(6, count($view->result), 'The number of returned rows match.'); - } - - /** - * Tests css/js storage and restoring mechanism. - */ - function testHeaderStorage() { - // Create a view with output caching enabled. - // Some hook_views_pre_render in views_test_data.module adds the test css/js file. - // so they should be added to the css/js storage. - $view = Views::getView('test_view'); - $view->setDisplay(); - $view->storage->set('id', 'test_cache_header_storage'); - $view->display_handler->overrideOption('cache', array( - 'type' => 'time', - 'options' => array( - 'output_lifespan' => '3600', - ) - )); - - $output = $view->buildRenderable(); - /** @var \Drupal\Core\Render\RendererInterface $renderer */ - $renderer = \Drupal::service('renderer'); - $renderer->executeInRenderContext(new RenderContext(), function () use (&$output, $renderer) { - return $renderer->render($output); - }); - - unset($view->pre_render_called); - $view->destroy(); - - $view->setDisplay(); - $output = $view->buildRenderable(); - $renderer->executeInRenderContext(new RenderContext(), function () use (&$output, $renderer) { - return $renderer->render($output); - }); - - $this->assertTrue(in_array('views_test_data/test', $output['#attached']['library']), 'Make sure libraries are added for cached views.'); - $this->assertEqual(['foo' => 'bar'], $output['#attached']['drupalSettings'], 'Make sure drupalSettings are added for cached views.'); - // Note: views_test_data_views_pre_render() adds some cache tags. - $this->assertEqual(['config:views.view.test_cache_header_storage', 'views_test_data:1'], $output['#cache']['tags']); - $this->assertEqual(['non-existing-placeholder-just-for-testing-purposes' => ['#lazy_builder' => ['views_test_data_placeholders', ['bar']]]], $output['#attached']['placeholders']); - $this->assertFalse(!empty($view->build_info['pre_render_called']), 'Make sure hook_views_pre_render is not called for the cached view.'); - } - - /** - * Tests that Subqueries are cached as expected. - */ - public function testSubqueryStringCache() { - // Execute the view. - $view = Views::getView('test_groupwise_term_ui'); - $view->setDisplay(); - $this->executeView($view); - // Request for the cache. - $cid = 'views_relationship_groupwise_max:test_groupwise_term_ui:default:tid_representative'; - $cache = \Drupal::cache('data')->get($cid); - $this->assertEqual($cid, $cache->cid, 'Subquery String cached as expected.'); - } - - /** - * Tests the data contained in cached items. - */ - public function testCacheData() { - for ($i = 1; $i <= 5; $i++) { - Node::create([ - 'title' => $this->randomMachineName(8), - 'type' => 'page', - ])->save(); - } - - $view = Views::getView('test_display'); - $view->setDisplay(); - $view->display_handler->overrideOption('cache', array( - 'type' => 'time', - 'options' => array( - 'results_lifespan' => '3600', - 'output_lifespan' => '3600', - ) - )); - $this->executeView($view); - - // Get the cache item. - $cid = $view->display_handler->getPlugin('cache')->generateResultsKey(); - $cache = \Drupal::cache('data')->get($cid); - - // Assert there are results, empty results would mean this test case would - // pass otherwise. - $this->assertTrue(count($cache->data['result']), 'Results saved in cached data.'); - - // Assert each row doesn't contain '_entity' or '_relationship_entities' - // items. - foreach ($cache->data['result'] as $row) { - $this->assertIdentical($row->_entity, NULL, 'Cached row "_entity" property is NULL'); - $this->assertIdentical($row->_relationship_entities, [], 'Cached row "_relationship_entities" property is empty'); - } - } - - /** - * Tests the cache context integration for views result cache. - */ - public function testCacheContextIntegration() { - $view = Views::getView('test_cache'); - $view->setDisplay('page_2'); - \Drupal::state()->set('views_test_cache_context', 'George'); - $this->executeView($view); - - $map = ['views_test_data_name' => 'name']; - $this->assertIdenticalResultset($view, [['name' => 'George']], $map); - - // Update the entry in the DB to ensure that result caching works. - \Drupal::database()->update('views_test_data') - ->condition('name', 'George') - ->fields(['name' => 'egroeG']) - ->execute(); - - $view = Views::getView('test_cache'); - $view->setDisplay('page_2'); - $this->executeView($view); - $this->assertIdenticalResultset($view, [['name' => 'George']], $map); - - // Now change the cache context value, a different query should be executed. - $view = Views::getView('test_cache'); - $view->setDisplay('page_2'); - \Drupal::state()->set('views_test_cache_context', 'Paul'); - $this->executeView($view); - - $this->assertIdenticalResultset($view, [['name' => 'Paul']], $map); - } - -} diff --git a/core/modules/views/src/Tests/Plugin/DisplayKernelTest.php b/core/modules/views/src/Tests/Plugin/DisplayKernelTest.php deleted file mode 100644 index e07a536..0000000 --- a/core/modules/views/src/Tests/Plugin/DisplayKernelTest.php +++ /dev/null @@ -1,122 +0,0 @@ -mergeDefaults(); - $view->save(); - - // Reload to get saved storage values. - $view = Views::getView('test_display_defaults'); - $view->initDisplay(); - $display_data = $view->storage->get('display'); - - foreach ($view->displayHandlers as $id => $display) { - // Test the view plugin options against the storage. - foreach ($this->pluginTypes as $type) { - $options = $display->getOption($type); - $this->assertIdentical($display_data[$id]['display_options'][$type]['options'], $options['options']); - } - // Test the view handler options against the storage. - foreach ($this->handlerTypes as $type) { - $options = $display->getOption($type); - $this->assertIdentical($display_data[$id]['display_options'][$type], $options); - } - } - } - - /** - * Tests the \Drupal\views\Plugin\views\display\DisplayPluginBase::getPlugin() method. - */ - public function testGetPlugin() { - $view = Views::getView('test_display_defaults'); - $view->initDisplay(); - $display_handler = $view->display_handler; - - $this->assertTrue($display_handler->getPlugin('access') instanceof AccessPluginBase, 'An access plugin instance was returned.'); - $this->assertTrue($display_handler->getPlugin('cache') instanceof CachePluginBase, 'A cache plugin instance was returned.'); - $this->assertTrue($display_handler->getPlugin('exposed_form') instanceof ExposedFormPluginBase, 'An exposed_form plugin instance was returned.'); - $this->assertTrue($display_handler->getPlugin('pager') instanceof PagerPluginBase, 'A pager plugin instance was returned.'); - $this->assertTrue($display_handler->getPlugin('query') instanceof QueryPluginBase, 'A query plugin instance was returned.'); - $this->assertTrue($display_handler->getPlugin('row') instanceof RowPluginBase, 'A row plugin instance was returned.'); - $this->assertTrue($display_handler->getPlugin('style') instanceof StylePluginBase, 'A style plugin instance was returned.'); - // Test that nothing is returned when an invalid type is requested. - $this->assertNull($display_handler->getPlugin('invalid'), 'NULL was returned for an invalid instance'); - // Test that nothing was returned for an instance with no 'type' in options. - unset($display_handler->options['access']); - $this->assertNull($display_handler->getPlugin('access'), 'NULL was returned for a plugin type with no "type" option'); - - // Get a plugin twice, and make sure the same instance is returned. - $view->destroy(); - $view->initDisplay(); - $first = spl_object_hash($display_handler->getPlugin('style')); - $second = spl_object_hash($display_handler->getPlugin('style')); - $this->assertIdentical($first, $second, 'The same plugin instance was returned.'); - } - -} diff --git a/core/modules/views/src/Tests/Plugin/DisplayPageTest.php b/core/modules/views/src/Tests/Plugin/DisplayPageTest.php deleted file mode 100644 index 7c7593d..0000000 --- a/core/modules/views/src/Tests/Plugin/DisplayPageTest.php +++ /dev/null @@ -1,167 +0,0 @@ -installSchema('system', array('url_alias')); - } - - /** - * Checks the behavior of the page for access denied/not found behaviors. - */ - public function testPageResponses() { - \Drupal::currentUser()->setAccount(new AnonymousUserSession()); - $subrequest = Request::create('/test_page_display_403', 'GET'); - $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST); - $this->assertEqual($response->getStatusCode(), 403); - - $subrequest = Request::create('/test_page_display_404', 'GET'); - $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST); - $this->assertEqual($response->getStatusCode(), 404); - - $subrequest = Request::create('/test_page_display_200', 'GET'); - $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST); - $this->assertEqual($response->getStatusCode(), 200); - - $subrequest = Request::create('/test_page_display_200', 'GET'); - \Drupal::getContainer()->get('request_stack')->push($subrequest); - - // Test accessing a disabled page for a view. - $view = Views::getView('test_page_display'); - // Disable the view, rebuild menu, and request the page again. - $view->storage->disable()->save(); - // Router rebuild would occur in a kernel terminate event so we need to - // simulate that here. - \Drupal::service('router.builder')->rebuild(); - - $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST); - $this->assertEqual($response->getStatusCode(), 404); - } - - /** - * Checks that the router items are properly registered - */ - public function testPageRouterItems() { - $collection = \Drupal::service('views.route_subscriber')->routes(); - - // Check the controller defaults. - foreach ($collection as $id => $route) { - $this->assertEqual($route->getDefault('_controller'), 'Drupal\views\Routing\ViewPageController::handle'); - $id_parts = explode('.', $id); - $this->assertEqual($route->getDefault('view_id'), $id_parts[1]); - $this->assertEqual($route->getDefault('display_id'), $id_parts[2]); - } - - // Check the generated patterns and default values. - $route = $collection->get('view.test_page_display_route.page_1'); - $this->assertEqual($route->getPath(), '/test_route_without_arguments'); - - $route = $collection->get('view.test_page_display_route.page_2'); - $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_0}'); - $this->assertTrue($route->hasDefault('arg_0'), 'A default value is set for the optional argument id.'); - - $route = $collection->get('view.test_page_display_route.page_3'); - $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_0}/suffix'); - $this->assertFalse($route->hasDefault('arg_0'), 'No default value is set for the required argument id.'); - - $route = $collection->get('view.test_page_display_route.page_4'); - $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_0}/suffix/{arg_1}'); - $this->assertFalse($route->hasDefault('arg_0'), 'No default value is set for the required argument id.'); - $this->assertTrue($route->hasDefault('arg_1'), 'A default value is set for the optional argument id_2.'); - - $route = $collection->get('view.test_page_display_route.page_5'); - $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_0}/{arg_1}'); - $this->assertTrue($route->hasDefault('arg_0'), 'A default value is set for the optional argument id.'); - $this->assertTrue($route->hasDefault('arg_1'), 'A default value is set for the optional argument id_2.'); - - $route = $collection->get('view.test_page_display_route.page_6'); - $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_0}/{arg_1}'); - $this->assertFalse($route->hasDefault('arg_0'), 'No default value is set for the required argument id.'); - $this->assertFalse($route->hasDefault('arg_1'), 'No default value is set for the required argument id_2.'); - } - - /** - * Tests the generated menu links of views. - */ - public function testMenuLinks() { - \Drupal::service('plugin.manager.menu.link')->rebuild(); - $tree = \Drupal::menuTree()->load('admin', new MenuTreeParameters()); - $this->assertTrue(isset($tree['system.admin']->subtree['views_view:views.test_page_display_menu.page_4'])); - $menu_link = $tree['system.admin']->subtree['views_view:views.test_page_display_menu.page_4']->link; - $this->assertEqual($menu_link->getTitle(), 'Test child (with parent)'); - $this->assertEqual($menu_link->isExpanded(), TRUE); - $this->assertEqual($menu_link->getDescription(), 'Sample description.'); - } - - /** - * Tests the calculated dependencies for various views using Page displays. - */ - public function testDependencies() { - $view = Views::getView('test_page_display'); - $this->assertIdentical([], $view->getDependencies()); - - $view = Views::getView('test_page_display_route'); - $expected = [ - 'content' => ['StaticTest'], - 'module' => ['views_test_data'], - ]; - $this->assertIdentical($expected, $view->getDependencies()); - - $view = Views::getView('test_page_display_menu'); - $expected = [ - 'config' => [ - 'system.menu.admin', - 'system.menu.tools', - ], - ]; - $this->assertIdentical($expected, $view->getDependencies()); - } - -} diff --git a/core/modules/views/src/Tests/Plugin/DisplayTest.php b/core/modules/views/src/Tests/Plugin/DisplayTest.php index 20aa722..be003cf 100644 --- a/core/modules/views/src/Tests/Plugin/DisplayTest.php +++ b/core/modules/views/src/Tests/Plugin/DisplayTest.php @@ -8,6 +8,7 @@ namespace Drupal\views\Tests\Plugin; use Drupal\language\Entity\ConfigurableLanguage; +use Drupal\views\Entity\View; use Drupal\views\Views; use Drupal\views_test_data\Plugin\views\display\DisplayTest as DisplayTestPlugin; @@ -230,7 +231,7 @@ public function testReadMore() { $this->assertTrue(empty($result), 'The more link is not shown when view has more records.'); // Test the default value of use_more_always. - $view = entity_create('view')->getExecutable(); + $view = View::create()->getExecutable(); $this->assertTrue($view->getDisplay()->getOption('use_more_always'), 'Always display the more link by default.'); } diff --git a/core/modules/views/src/Tests/Plugin/ExposedFormTest.php b/core/modules/views/src/Tests/Plugin/ExposedFormTest.php index 1c520e0..3634e23 100644 --- a/core/modules/views/src/Tests/Plugin/ExposedFormTest.php +++ b/core/modules/views/src/Tests/Plugin/ExposedFormTest.php @@ -85,6 +85,69 @@ public function testSubmitButton() { } /** + * Tests the exposed form with a non-standard identifier. + */ + public function testExposedIdentifier() { + // Alter the identifier of the filter to a random string. + $view = Views::getView('test_exposed_form_buttons'); + $view->setDisplay(); + $identifier = 'new_identifier'; + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'type' => [ + 'exposed' => TRUE, + 'field' => 'type', + 'id' => 'type', + 'table' => 'node_field_data', + 'plugin_id' => 'in_operator', + 'entity_type' => 'node', + 'entity_field' => 'type', + 'expose' => [ + 'identifier' => $identifier, + 'label' => 'Content: Type', + 'operator_id' => 'type_op', + 'reduce' => FALSE, + 'description' => 'Exposed overridden description' + ], + ] + )); + $view->save(); + $this->drupalGet('test_exposed_form_buttons', array('query' => array($identifier => 'article'))); + $this->assertFieldById(Html::getId('edit-' . $identifier), 'article', "Article type filter set with new identifier."); + + // Alter the identifier of the filter to a random string containing + // restricted characters. + $view = Views::getView('test_exposed_form_buttons'); + $view->setDisplay(); + $identifier = 'bad identifier'; + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'type' => [ + 'exposed' => TRUE, + 'field' => 'type', + 'id' => 'type', + 'table' => 'node_field_data', + 'plugin_id' => 'in_operator', + 'entity_type' => 'node', + 'entity_field' => 'type', + 'expose' => [ + 'identifier' => $identifier, + 'label' => 'Content: Type', + 'operator_id' => 'type_op', + 'reduce' => FALSE, + 'description' => 'Exposed overridden description' + ], + ] + )); + $this->executeView($view); + + $errors = $view->validate(); + $expected = [ + 'default' => ['This identifier has illegal characters.'], + 'page_1' => ['This identifier has illegal characters.'], + ]; + $this->assertEqual($errors, $expected); + } + + /** * Tests whether the reset button works on an exposed form. */ public function testResetButton() { diff --git a/core/modules/views/src/Tests/Plugin/JoinTest.php b/core/modules/views/src/Tests/Plugin/JoinTest.php deleted file mode 100644 index fa1460c..0000000 --- a/core/modules/views/src/Tests/Plugin/JoinTest.php +++ /dev/null @@ -1,211 +0,0 @@ -manager = $this->container->get('plugin.manager.views.join'); - } - - /** - * Tests an example join plugin. - */ - public function testExamplePlugin() { - - // Setup a simple join and test the result sql. - $view = Views::getView('test_view'); - $view->initDisplay(); - $view->initQuery(); - - $configuration = array( - 'left_table' => 'views_test_data', - 'left_field' => 'uid', - 'table' => 'users_field_data', - 'field' => 'uid', - ); - $join = $this->manager->createInstance('join_test', $configuration); - $this->assertTrue($join instanceof JoinTestPlugin, 'The correct join class got loaded.'); - - $rand_int = rand(0, 1000); - $join->setJoinValue($rand_int); - - $query = db_select('views_test_data'); - $table = array('alias' => 'users_field_data'); - $join->buildJoin($query, $table, $view->query); - - $tables = $query->getTables(); - $join_info = $tables['users_field_data']; - $this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = $rand_int") !== FALSE, 'Make sure that the custom join plugin can extend the join base and alter the result.'); - } - - /** - * Tests the join plugin base. - */ - public function testBasePlugin() { - - // Setup a simple join and test the result sql. - $view = Views::getView('test_view'); - $view->initDisplay(); - $view->initQuery(); - - // First define a simple join without an extra condition. - // Set the various options on the join object. - $configuration = array( - 'left_table' => 'views_test_data', - 'left_field' => 'uid', - 'table' => 'users_field_data', - 'field' => 'uid', - 'adjusted' => TRUE, - ); - $join = $this->manager->createInstance('standard', $configuration); - $this->assertTrue($join instanceof JoinPluginBase, 'The correct join class got loaded.'); - $this->assertNull($join->extra, 'The field extra was not overridden.'); - $this->assertTrue($join->adjusted, 'The field adjusted was set correctly.'); - - // Build the actual join values and read them back from the dbtng query - // object. - $query = db_select('views_test_data'); - $table = array('alias' => 'users_field_data'); - $join->buildJoin($query, $table, $view->query); - - $tables = $query->getTables(); - $join_info = $tables['users_field_data']; - $this->assertEqual($join_info['join type'], 'LEFT', 'Make sure the default join type is LEFT'); - $this->assertEqual($join_info['table'], $configuration['table']); - $this->assertEqual($join_info['alias'], 'users_field_data'); - $this->assertEqual($join_info['condition'], 'views_test_data.uid = users_field_data.uid'); - - // Set a different alias and make sure table info is as expected. - $join = $this->manager->createInstance('standard', $configuration); - $table = array('alias' => 'users1'); - $join->buildJoin($query, $table, $view->query); - - $tables = $query->getTables(); - $join_info = $tables['users1']; - $this->assertEqual($join_info['alias'], 'users1'); - - // Set a different join type (INNER) and make sure it is used. - $configuration['type'] = 'INNER'; - $join = $this->manager->createInstance('standard', $configuration); - $table = array('alias' => 'users2'); - $join->buildJoin($query, $table, $view->query); - - $tables = $query->getTables(); - $join_info = $tables['users2']; - $this->assertEqual($join_info['join type'], 'INNER'); - - // Setup addition conditions and make sure it is used. - $random_name_1 = $this->randomMachineName(); - $random_name_2 = $this->randomMachineName(); - $configuration['extra'] = array( - array( - 'field' => 'name', - 'value' => $random_name_1 - ), - array( - 'field' => 'name', - 'value' => $random_name_2, - 'operator' => '<>' - ), - ); - $join = $this->manager->createInstance('standard', $configuration); - $table = array('alias' => 'users3'); - $join->buildJoin($query, $table, $view->query); - - $tables = $query->getTables(); - $join_info = $tables['users3']; - $this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = users3.uid") !== FALSE, 'Make sure the join condition appears in the query.'); - $this->assertTrue(strpos($join_info['condition'], "users3.name = :views_join_condition_0") !== FALSE, 'Make sure the first extra join condition appears in the query and uses the first placeholder.'); - $this->assertTrue(strpos($join_info['condition'], "users3.name <> :views_join_condition_1") !== FALSE, 'Make sure the second extra join condition appears in the query and uses the second placeholder.'); - $this->assertEqual(array_values($join_info['arguments']), array($random_name_1, $random_name_2), 'Make sure the arguments are in the right order'); - - // Test that 'IN' conditions are properly built. - $random_name_1 = $this->randomMachineName(); - $random_name_2 = $this->randomMachineName(); - $random_name_3 = $this->randomMachineName(); - $random_name_4 = $this->randomMachineName(); - $configuration['extra'] = array( - array( - 'field' => 'name', - 'value' => $random_name_1 - ), - array( - 'field' => 'name', - 'value' => array($random_name_2, $random_name_3, $random_name_4), - ), - ); - $join = $this->manager->createInstance('standard', $configuration); - $table = array('alias' => 'users4'); - $join->buildJoin($query, $table, $view->query); - - $tables = $query->getTables(); - $join_info = $tables['users4']; - $this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = users4.uid") !== FALSE, 'Make sure the join condition appears in the query.'); - $this->assertTrue(strpos($join_info['condition'], "users4.name = :views_join_condition_2") !== FALSE, 'Make sure the first extra join condition appears in the query.'); - $this->assertTrue(strpos($join_info['condition'], "users4.name IN ( :views_join_condition_3, :views_join_condition_4, :views_join_condition_5 )") !== FALSE, 'The IN condition for the join is properly formed.'); - - // Test that all the conditions are properly built. - $configuration['extra'] = array( - array( - 'field' => 'langcode', - 'value' => 'en' - ), - array( - 'left_field' => 'status', - 'value' => 0, - 'numeric' => TRUE, - ), - array( - 'field' => 'name', - 'left_field' => 'name' - ), - ); - $join = $this->manager->createInstance('standard', $configuration); - $table = array('alias' => 'users5'); - $join->buildJoin($query, $table, $view->query); - - $tables = $query->getTables(); - $join_info = $tables['users5']; - $this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = users5.uid") !== FALSE, 'Make sure the join condition appears in the query.'); - $this->assertTrue(strpos($join_info['condition'], "users5.langcode = :views_join_condition_6") !== FALSE, 'Make sure the first extra join condition appears in the query.'); - $this->assertTrue(strpos($join_info['condition'], "views_test_data.status = :views_join_condition_7") !== FALSE, 'Make sure the second extra join condition appears in the query.'); - $this->assertTrue(strpos($join_info['condition'], "users5.name = views_test_data.name") !== FALSE, 'Make sure the third extra join condition appears in the query.'); - $this->assertEqual(array_values($join_info['arguments']), array('en', 0), 'Make sure the arguments are in the right order'); - } - -} diff --git a/core/modules/views/src/Tests/Plugin/NumericFormatPluralTest.php b/core/modules/views/src/Tests/Plugin/NumericFormatPluralTest.php index 8e75e4d..8efa45a 100644 --- a/core/modules/views/src/Tests/Plugin/NumericFormatPluralTest.php +++ b/core/modules/views/src/Tests/Plugin/NumericFormatPluralTest.php @@ -8,6 +8,7 @@ namespace Drupal\views\Tests\Plugin; use Drupal\Component\Gettext\PoHeader; +use Drupal\file\Entity\File; use Drupal\views\Tests\ViewTestBase; /** @@ -144,7 +145,7 @@ function testNumericFormatPlural() { */ protected function createFile() { // Create a new file entity. - $file = entity_create('file', array( + $file = File::create([ 'uid' => 1, 'filename' => 'druplicon.txt', 'uri' => 'public://druplicon.txt', @@ -152,7 +153,7 @@ protected function createFile() { 'created' => 1, 'changed' => 1, 'status' => FILE_STATUS_PERMANENT, - )); + ]); file_put_contents($file->getFileUri(), 'hello world'); // Save it, inserting a new record. diff --git a/core/modules/views/src/Tests/Plugin/PagerKernelTest.php b/core/modules/views/src/Tests/Plugin/PagerKernelTest.php deleted file mode 100644 index 9edfa4c..0000000 --- a/core/modules/views/src/Tests/Plugin/PagerKernelTest.php +++ /dev/null @@ -1,74 +0,0 @@ -installEntitySchema('node'); - $this->installEntitySchema('user'); - } - - /** - * Tests pager-related setter methods on ViewExecutable. - * - * @see \Drupal\views\ViewExecutable::setItemsPerPage - * @see \Drupal\views\ViewExecutable::setOffset - * @see \Drupal\views\ViewExecutable::setCurrentPage - */ - public function testSetPagerMethods() { - $view = Views::getView('test_pager_full'); - - // Mark the view as cacheable in order have the cache checking working - // below. - $display = &$view->storage->getDisplay('default'); - $display['display_options']['cache']['type'] = 'tag'; - $view->storage->save(); - - $output = $view->preview(); - - \Drupal::service('renderer')->renderPlain($output); - $this->assertIdentical(CacheBackendInterface::CACHE_PERMANENT, $output['#cache']['max-age']); - - foreach (['setItemsPerPage', 'setOffset', 'setCurrentPage'] as $method) { - $view = Views::getView('test_pager_full'); - $view->setDisplay('default'); - $view->{$method}(1); - $output = $view->preview(); - - \Drupal::service('renderer')->renderPlain($output); - $this->assertIdentical(CacheBackendInterface::CACHE_PERMANENT, $output['#cache']['max-age'], 'Max age kept.'); - } - - } - -} diff --git a/core/modules/views/src/Tests/Plugin/PagerTest.php b/core/modules/views/src/Tests/Plugin/PagerTest.php index bdd95ed..a4f2f42 100644 --- a/core/modules/views/src/Tests/Plugin/PagerTest.php +++ b/core/modules/views/src/Tests/Plugin/PagerTest.php @@ -47,6 +47,9 @@ class PagerTest extends PluginTestBase { * @see https://www.drupal.org/node/652712 */ public function testStorePagerSettings() { + // Show the master display so the override selection is shown. + \Drupal::configFactory()->getEditable('views.settings')->set('ui.show.master_display', TRUE)->save(); + $admin_user = $this->drupalCreateUser(array('administer views', 'administer site configuration')); $this->drupalLogin($admin_user); // Test behavior described in @@ -106,7 +109,7 @@ public function testStorePagerSettings() { 'pager[type]' => 'mini', ); $this->drupalPostForm('admin/structure/views/nojs/display/test_store_pager_settings/page_1/pager', $edit, t('Apply')); - $this->drupalGet('admin/structure/views/view/test_store_pager_settings/edit'); + $this->drupalGet('admin/structure/views/view/test_store_pager_settings/edit/page_1'); $this->assertText('Mini', 'Changed pager plugin, should change some text'); $edit = array( diff --git a/core/modules/views/src/Tests/Plugin/QueryTest.php b/core/modules/views/src/Tests/Plugin/QueryTest.php deleted file mode 100644 index 7dcff7e..0000000 --- a/core/modules/views/src/Tests/Plugin/QueryTest.php +++ /dev/null @@ -1,82 +0,0 @@ -_testInitQuery(); - $this->_testQueryExecute(); - $this->queryMethodsTests(); - } - - /** - * Tests the ViewExecutable::initQuery method. - */ - public function _testInitQuery() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - $view->initQuery(); - $this->assertTrue($view->query instanceof QueryTestPlugin, 'Make sure the right query plugin got instantiated.'); - } - - public function _testQueryExecute() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - $view->initQuery(); - $view->query->setAllItems($this->dataSet()); - - $this->executeView($view); - $this->assertTrue($view->result, 'Make sure the view result got filled'); - } - - /** - * Test methods provided by the QueryPluginBase. - * - * @see \Drupal\views\Plugin\views\query\QueryPluginBase - */ - protected function queryMethodsTests() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - $view->initQuery(); - $this->assertFalse($view->query->getLimit(), 'Default to an empty limit.'); - $rand_number = rand(5, 10); - $view->query->setLimit($rand_number); - $this->assertEqual($view->query->getLimit(), $rand_number, 'set_limit adapts the amount of items.'); - } - -} diff --git a/core/modules/views/src/Tests/Plugin/RelationshipJoinTestBase.php b/core/modules/views/src/Tests/Plugin/RelationshipJoinTestBase.php deleted file mode 100644 index d57fa27..0000000 --- a/core/modules/views/src/Tests/Plugin/RelationshipJoinTestBase.php +++ /dev/null @@ -1,86 +0,0 @@ -installEntitySchema('user'); - $this->installConfig(array('user')); - parent::setUpFixtures(); - - // Create a record for uid 1. - $this->rootUser = entity_create('user', array('name' => $this->randomMachineName())); - $this->rootUser->save(); - - Views::viewsData()->clear(); - } - - /** - * Overrides \Drupal\views\Tests\ViewTestBase::schemaDefinition(). - * - * Adds a uid column to test the relationships. - */ - protected function schemaDefinition() { - $schema = parent::schemaDefinition(); - - $schema['views_test_data']['fields']['uid'] = array( - 'description' => "The {users_field_data}.uid of the author of the beatle entry.", - 'type' => 'int', - 'unsigned' => TRUE, - 'not null' => TRUE, - 'default' => 0 - ); - - return $schema; - } - - /** - * Overrides \Drupal\views\Tests\ViewTestBase::viewsData(). - * - * Adds a relationship for the uid column. - */ - protected function viewsData() { - $data = parent::viewsData(); - $data['views_test_data']['uid'] = array( - 'title' => t('UID'), - 'help' => t('The test data UID'), - 'relationship' => array( - 'id' => 'standard', - 'base' => 'users_field_data', - 'base field' => 'uid' - ) - ); - - return $data; - } - -} diff --git a/core/modules/views/src/Tests/Plugin/RowEntityTest.php b/core/modules/views/src/Tests/Plugin/RowEntityTest.php deleted file mode 100644 index 9723750..0000000 --- a/core/modules/views/src/Tests/Plugin/RowEntityTest.php +++ /dev/null @@ -1,71 +0,0 @@ -installEntitySchema('taxonomy_term'); - $this->installConfig(array('taxonomy')); - \Drupal::service('router.builder')->rebuild(); - } - - /** - * Tests the entity row handler. - */ - public function testEntityRow() { - $vocab = entity_create('taxonomy_vocabulary', array('name' => $this->randomMachineName(), 'vid' => strtolower($this->randomMachineName()))); - $vocab->save(); - $term = entity_create('taxonomy_term', array('name' => $this->randomMachineName(), 'vid' => $vocab->id() )); - $term->save(); - - $view = Views::getView('test_entity_row'); - $build = $view->preview(); - $this->render($build); - - $this->assertText($term->getName(), 'The rendered entity appears as row in the view.'); - - // Tests the available view mode options. - $form = array(); - $form_state = new FormState(); - $form_state->set('view', $view->storage); - $view->rowPlugin->buildOptionsForm($form, $form_state); - - $this->assertTrue(isset($form['view_mode']['#options']['default']), 'Ensure that the default view mode is available'); - } - -} diff --git a/core/modules/views/src/Tests/Plugin/RowRenderCacheTest.php b/core/modules/views/src/Tests/Plugin/RowRenderCacheTest.php deleted file mode 100644 index c00d584..0000000 --- a/core/modules/views/src/Tests/Plugin/RowRenderCacheTest.php +++ /dev/null @@ -1,178 +0,0 @@ -installEntitySchema('user'); - $this->installEntitySchema('node'); - $this->installSchema('node', 'node_access'); - - $type = NodeType::create(['type' => 'test']); - $type->save(); - - $this->editorUser = $this->createUser(['bypass node access']); - $this->powerUser = $this->createUser(['access content', 'create test content', 'edit own test content', 'delete own test content']); - $this->regularUser = $this->createUser(['access content']); - - // Create some test entities. - for ($i = 0; $i < 5; $i++) { - Node::create(['title' => 'b' . $i . $this->randomMachineName(), 'type' => 'test'])->save(); - } - - // Create a power user node. - Node::create(['title' => 'z' . $this->randomMachineName(), 'uid' => $this->powerUser->id(), 'type' => 'test'])->save(); - } - - /** - * Test complex field rewriting and uncacheable field handlers. - */ - public function testAdvancedCaching() { - // Test that row field output is actually cached and with the proper cache - // contexts. - $this->doTestRenderedOutput($this->editorUser); - $this->doTestRenderedOutput($this->editorUser, TRUE); - $this->doTestRenderedOutput($this->powerUser); - $this->doTestRenderedOutput($this->powerUser, TRUE); - $this->doTestRenderedOutput($this->regularUser); - $this->doTestRenderedOutput($this->regularUser, TRUE); - - // Alter the result set order and check that counter is still working - // correctly. - $this->doTestRenderedOutput($this->editorUser); - /** @var \Drupal\node\NodeInterface $node */ - $node = Node::load(6); - $node->setTitle('a' . $this->randomMachineName()); - $node->save(); - $this->doTestRenderedOutput($this->editorUser); - } - - /** - * Check whether the rendered output matches expectations. - * - * @param \Drupal\Core\Session\AccountInterface $account - * The user account to tests rendering with. - * @param bool $check_cache - * (optional) Whether explicitly test render cache entries. - */ - protected function doTestRenderedOutput(AccountInterface $account, $check_cache = FALSE) { - $this->setCurrentUser($account); - $view = Views::getView('test_row_render_cache'); - $view->setDisplay(); - $view->preview(); - - /** @var \Drupal\Core\Render\RenderCacheInterface $render_cache */ - $render_cache = $this->container->get('render_cache'); - - /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache_plugin */ - $cache_plugin = $view->display_handler->getPlugin('cache'); - - // Retrieve nodes and sort them in alphabetical order to match view results. - $nodes = Node::loadMultiple(); - usort($nodes, function (NodeInterface $a, NodeInterface $b) { - return strcmp($a->label(), $b->label()); - }); - - $index = 0; - foreach ($nodes as $node) { - $nid = $node->id(); - $access = $node->access('update'); - - $counter = $index + 1; - $expected = "$nid: $counter (just in case: $nid)"; - $counter_output = $view->style_plugin->getField($index, 'counter'); - $this->assertEqual($counter_output, $expected); - - $node_url = $node->url(); - $expected = "{$node->label()} $counter_output"; - $output = $view->style_plugin->getField($index, 'title'); - $this->assertEqual($output, $expected); - - $expected = $access ? "edit" : ""; - $output = $view->style_plugin->getField($index, 'edit_node'); - $this->assertEqual($output, $expected); - - $expected = $access ? "delete" : ""; - $output = $view->style_plugin->getField($index, 'delete_node'); - $this->assertEqual($output, $expected); - - $expected = $access ? "
                          " : ""; - $output = $view->style_plugin->getField($index, 'operations'); - $this->assertEqual($output, $expected); - - if ($check_cache) { - $keys = $cache_plugin->getRowCacheKeys($view->result[$index]); - $user_context = !$account->hasPermission('edit any test content') ? 'user' : 'user.permissions'; - $element = $render_cache->get(['#cache' => ['keys' => $keys, 'contexts' => ['languages:language_interface', 'theme', $user_context]]]); - $this->assertTrue($element); - } - - $index++; - } - } - -} diff --git a/core/modules/views/src/Tests/Plugin/SqlQueryTest.php b/core/modules/views/src/Tests/Plugin/SqlQueryTest.php deleted file mode 100644 index 0880a07..0000000 --- a/core/modules/views/src/Tests/Plugin/SqlQueryTest.php +++ /dev/null @@ -1,87 +0,0 @@ - 'test_metadata', 'key2' => 'test_metadata2'); - - return $data; - } - - /** - * Tests adding some metadata/tags to the views query. - */ - public function testExecuteMetadata() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - $view->initQuery(); - $view->execute(); - /** @var \Drupal\Core\Database\Query\Select $query */ - $main_query = $view->build_info['query']; - /** @var \Drupal\Core\Database\Query\Select $count_query */ - $count_query = $view->build_info['count_query']; - - foreach (array($main_query, $count_query) as $query) { - // Check query access tags. - $this->assertTrue($query->hasTag('test_tag')); - - // Check metadata. - $this->assertIdentical($query->getMetaData('key1'), 'test_metadata'); - $this->assertIdentical($query->getMetaData('key2'), 'test_metadata2'); - } - - $query_options = $view->display_handler->getOption('query'); - $query_options['options']['disable_sql_rewrite'] = TRUE; - $view->display_handler->setOption('query', $query_options); - $view->save(); - $view->destroy(); - - $view = Views::getView('test_view'); - $view->setDisplay(); - $view->initQuery(); - $view->execute(); - /** @var \Drupal\Core\Database\Query\Select $query */ - $main_query = $view->build_info['query']; - /** @var \Drupal\Core\Database\Query\Select $count_query */ - $count_query = $view->build_info['count_query']; - - foreach (array($main_query, $count_query) as $query) { - // Check query access tags. - $this->assertFalse($query->hasTag('test_tag')); - - // Check metadata. - $this->assertIdentical($query->getMetaData('key1'), NULL); - $this->assertIdentical($query->getMetaData('key2'), NULL); - } - } - -} diff --git a/core/modules/views/src/Tests/Plugin/StyleHtmlListTest.php b/core/modules/views/src/Tests/Plugin/StyleHtmlListTest.php deleted file mode 100644 index 1ff45b1..0000000 --- a/core/modules/views/src/Tests/Plugin/StyleHtmlListTest.php +++ /dev/null @@ -1,58 +0,0 @@ -preview(); - $output = \Drupal::service('renderer')->renderRoot($output); - - // Check that an empty class attribute is not added if the wrapper class is - // not set. - $this->assertTrue(strpos($output, '
                          ') !== FALSE, 'Empty class is not added to DIV when class is not set'); - - // Check that an empty class attribute is not added if the list class is - // not set. - $this->assertTrue(strpos($output, '
                            ') !== FALSE, 'Empty class is not added to UL when class is not set'); - - // Set wrapper class and list class in style options. - $view->style_plugin->options['class'] = 'class'; - $view->style_plugin->options['wrapper_class'] = 'wrapper-class'; - - $output = $view->preview(); - $output = \Drupal::service('renderer')->renderRoot($output); - - // Check that class attribute is present if the wrapper class is set. - $this->assertTrue(strpos($output, '
                            ') !== FALSE, 'Class is added to DIV'); - - // Check that class attribute is present if the list class is set. - $this->assertTrue(strpos($output, '
                              ') !== FALSE, 'Class is added to UL'); - } - -} diff --git a/core/modules/views/src/Tests/Plugin/StyleMappingTest.php b/core/modules/views/src/Tests/Plugin/StyleMappingTest.php deleted file mode 100644 index 58984fe..0000000 --- a/core/modules/views/src/Tests/Plugin/StyleMappingTest.php +++ /dev/null @@ -1,85 +0,0 @@ -mappedOutputHelper($view); - $this->assertTrue(strpos($output, 'job') === FALSE, 'The job field is added to the view but not in the mapping.'); - $view->destroy(); - - $view->setDisplay(); - $view->displayHandlers->get('default')->options['style']['options']['mapping']['name_field'] = 'job'; - $output = $this->mappedOutputHelper($view); - $this->assertTrue(strpos($output, 'job') !== FALSE, 'The job field is added to the view and is in the mapping.'); - } - - /** - * Tests the mapping of fields. - * - * @param \Drupal\views\ViewExecutable $view - * The view to test. - * - * @return string - * The view rendered as HTML. - */ - protected function mappedOutputHelper($view) { - $output = $view->preview(); - $rendered_output = \Drupal::service('renderer')->renderRoot($output); - $this->storeViewPreview($rendered_output); - $rows = $this->elements->body->div->div; - $data_set = $this->dataSet(); - - $count = 0; - foreach ($rows as $row) { - $attributes = $row->attributes(); - $class = (string) $attributes['class'][0]; - $this->assertTrue(strpos($class, 'views-row-mapping-test') !== FALSE, 'Make sure that each row has the correct CSS class.'); - - foreach ($row->div as $field) { - // Split up the field-level class, the first part is the mapping name - // and the second is the field ID. - $field_attributes = $field->attributes(); - $name = strtok((string) $field_attributes['class'][0], '-'); - $field_id = strtok('-'); - - // The expected result is the mapping name and the field value, - // separated by ':'. - $expected_result = $name . ':' . $data_set[$count][$field_id]; - $actual_result = (string) $field; - $this->assertIdentical($expected_result, $actual_result, format_string('The fields were mapped successfully: %name => %field_id', array('%name' => $name, '%field_id' => $field_id))); - } - - $count++; - } - - return $rendered_output; - } - -} diff --git a/core/modules/views/src/Tests/Plugin/StyleOpmlTest.php b/core/modules/views/src/Tests/Plugin/StyleOpmlTest.php index 6580c0c..c3db24b 100644 --- a/core/modules/views/src/Tests/Plugin/StyleOpmlTest.php +++ b/core/modules/views/src/Tests/Plugin/StyleOpmlTest.php @@ -74,7 +74,7 @@ public function testOpmlOutput() { $outline = $this->xpath('//outline[1]'); $this->assertEqual($outline[0]['type'], 'link', 'The correct type attribute is used for link OPML.'); $this->assertEqual($outline[0]['text'], $feed->label(), 'The correct text attribute is used for link OPML.'); - $this->assertEqual($outline[0]['url'], $feed->getUrl(), 'The correct url attribute is used for link OPML.'); + $this->assertEqual($outline[0]['url'], $feed->getUrl(), 'The correct URL attribute is used for link OPML.'); // xmlUrl should not be present when type is link. $this->assertNull($outline[0]['xmlUrl'], 'The xmlUrl attribute is not used for link OPML.'); } diff --git a/core/modules/views/src/Tests/Plugin/StyleSummaryTest.php b/core/modules/views/src/Tests/Plugin/StyleSummaryTest.php index cea8fba..cf585ce 100644 --- a/core/modules/views/src/Tests/Plugin/StyleSummaryTest.php +++ b/core/modules/views/src/Tests/Plugin/StyleSummaryTest.php @@ -20,7 +20,7 @@ class StyleSummaryTest extends ViewTestBase { /** * {@inheritdoc} */ - public static $modules = ['entity_test']; + public static $modules = ['entity_test', 'views_ui']; /** * {@inheritdoc} @@ -48,6 +48,9 @@ protected function setUp($import_test_views = TRUE) { $entity->save(); } } + + $views_user = $this->drupalCreateUser(['administer views']); + $this->drupalLogin($views_user); } /** @@ -72,6 +75,71 @@ public function testSummaryView() { $this->clickLink('type1'); $entries = $this->cssSelect('div.view-content div.views-row'); $this->assertEqual(2, count($entries)); + + // Add a base path to the summary settings. + $edit = [ + 'options[summary][options][default_summary][base_path]' => 'test-summary', + ]; + $this->drupalPostForm('admin/structure/views/nojs/handler/test_summary/page_1/argument/type', $edit, t('Apply')); + $this->drupalPostForm(NULL, [], t('Save')); + + // Test that the links still work. + $this->drupalGet('test-summary'); + $this->clickLink('type1'); + $entries = $this->cssSelect('div.view-content div.views-row'); + $this->assertEqual(2, count($entries)); + + // Change the summary display to an unformatted list displaying 3 items. + $edit = [ + 'options[summary][format]' => 'unformatted_summary', + 'options[summary][options][unformatted_summary][override]' => '1', + 'options[summary][options][unformatted_summary][items_per_page]' => '3', + ]; + $this->drupalPostForm('admin/structure/views/nojs/handler/test_summary/page_1/argument/type', $edit, t('Apply')); + $this->drupalPostForm(NULL, [], t('Save')); + + $this->drupalGet('admin/structure/views/nojs/handler/test_summary/page_1/argument/type'); + $this->drupalGet('test-summary'); + + $summary_list = $this->cssSelect('.views-summary-unformatted'); + $this->assertEqual(3, count($summary_list)); + + foreach ($summary_list as $summary_list_item) { + $this->assertEqual('(5)', trim((string) $summary_list_item)); + } + + $summary_links = $this->cssSelect('.views-summary-unformatted a'); + $this->assertEqual(3, count($summary_links)); + foreach ($summary_links as $index => $summary_link) { + $this->assertEqual('type' . $index, trim((string) $summary_link)); + } + + $this->clickLink('type1'); + $entries = $this->cssSelect('div.view-content div.views-row'); + $this->assertEqual(2, count($entries)); + + // Add a base path to the summary settings. + $edit = [ + 'options[summary][options][unformatted_summary][base_path]' => 'test-summary', + ]; + $this->drupalPostForm('admin/structure/views/nojs/handler/test_summary/page_1/argument/type', $edit, t('Apply')); + $this->drupalPostForm(NULL, [], t('Save')); + + // Test that the links still work. + $this->drupalGet('test-summary'); + $this->clickLink('type1'); + $entries = $this->cssSelect('div.view-content div.views-row'); + $this->assertEqual(2, count($entries)); + + // Set base_path to an unknown path and test that the links lead to the + // front page. + $edit = [ + 'options[summary][options][unformatted_summary][base_path]' => 'unknown-path', + ]; + $this->drupalPostForm('admin/structure/views/nojs/handler/test_summary/page_1/argument/type', $edit, t('Apply')); + $this->drupalPostForm(NULL, [], t('Save')); + $this->drupalGet('test-summary'); + $this->assertLinkByHref('/'); } } diff --git a/core/modules/views/src/Tests/Plugin/StyleTableUnitTest.php b/core/modules/views/src/Tests/Plugin/StyleTableUnitTest.php deleted file mode 100644 index 54095e4..0000000 --- a/core/modules/views/src/Tests/Plugin/StyleTableUnitTest.php +++ /dev/null @@ -1,161 +0,0 @@ -setDisplay('default'); - $view->initStyle(); - $view->initHandlers(); - $view->initQuery(); - $style_plugin = $view->style_plugin; - - // Test the buildSort() method. - $request = new Request(); - $view->setRequest($request); - - $style_plugin->options['override'] = FALSE; - - $style_plugin->options['default'] = ''; - $this->assertTrue($style_plugin->buildSort(), 'If no order and no default order is specified, the normal sort should be used.'); - - $style_plugin->options['default'] = 'id'; - $this->assertTrue($style_plugin->buildSort(), 'If no order but a default order is specified, the normal sort should be used.'); - - $request->attributes->set('order', $this->randomMachineName()); - $this->assertTrue($style_plugin->buildSort(), 'If no valid field is chosen for order, the normal sort should be used.'); - - $request->attributes->set('order', 'id'); - $this->assertTrue($style_plugin->buildSort(), 'If a valid order is specified but the table is configured to not override, the normal sort should be used.'); - - $style_plugin->options['override'] = TRUE; - - $this->assertFalse($style_plugin->buildSort(), 'If a valid order is specified and the table is configured to override, the normal sort should not be used.'); - - // Test the buildSortPost() method. - $request = new Request(); - $view->setRequest($request); - - // Setup no valid default. - $this->prepareView($view); - $style_plugin = $view->style_plugin; - $style_plugin->options['default'] = ''; - $style_plugin->buildSortPost(); - $this->assertIdentical($style_plugin->order, NULL, 'No sort order was set, when no order was specified and no default column was selected.'); - $this->assertIdentical($style_plugin->active, NULL, 'No sort field was set, when no order was specified and no default column was selected.'); - $view->destroy(); - - // Setup a valid default + column specific default sort order. - $this->prepareView($view); - $style_plugin = $view->style_plugin; - $style_plugin->options['default'] = 'id'; - $style_plugin->options['info']['id']['default_sort_order'] = 'desc'; - $style_plugin->buildSortPost(); - $this->assertIdentical($style_plugin->order, 'desc', 'Fallback to the right default sort order.'); - $this->assertIdentical($style_plugin->active, 'id', 'Fallback to the right default sort field.'); - $view->destroy(); - - // Setup a valid default + table default sort order. - $this->prepareView($view); - $style_plugin = $view->style_plugin; - $style_plugin->options['default'] = 'id'; - $style_plugin->options['info']['id']['default_sort_order'] = NULL; - $style_plugin->options['order'] = 'asc'; - $style_plugin->buildSortPost(); - $this->assertIdentical($style_plugin->order, 'asc', 'Fallback to the right default sort order.'); - $this->assertIdentical($style_plugin->active, 'id', 'Fallback to the right default sort field.'); - $view->destroy(); - - // Use an invalid field. - $this->prepareView($view); - $style_plugin = $view->style_plugin; - $request->query->set('sort', 'asc'); - $random_name = $this->randomMachineName(); - $request->query->set('order', $random_name); - $style_plugin->buildSortPost(); - $this->assertIdentical($style_plugin->order, 'asc', 'No sort order was set, when invalid sort order was specified.'); - $this->assertIdentical($style_plugin->active, NULL, 'No sort field was set, when invalid sort order was specified.'); - $view->destroy(); - - // Use a existing field, and sort both ascending and descending. - foreach (array('asc', 'desc') as $order) { - $this->prepareView($view); - $style_plugin = $view->style_plugin; - $request->query->set('sort', $order); - $request->query->set('order', 'id'); - $style_plugin->buildSortPost(); - $this->assertIdentical($style_plugin->order, $order, 'Ensure the right sort order was set.'); - $this->assertIdentical($style_plugin->active, 'id', 'Ensure the right order was set.'); - $view->destroy(); - } - - $view->destroy(); - - // Excluded field to make sure its wrapping td doesn't show. - $this->prepareView($view); - $view->field['name']->options['exclude'] = TRUE; - $output = $view->preview(); - $output = \Drupal::service('renderer')->renderRoot($output); - $this->assertFalse(strpos($output, 'views-field-name') !== FALSE, "Excluded field's wrapper was not rendered."); - $view->destroy(); - - // Render a non empty result, and ensure that the empty area handler is not - // rendered. - $this->executeView($view); - $output = $view->preview(); - $output = \Drupal::service('renderer')->renderRoot($output); - - $this->assertFalse(strpos($output, 'custom text') !== FALSE, 'Empty handler was not rendered on a non empty table.'); - - // Render an empty result, and ensure that the area handler is rendered. - $view->setDisplay('default'); - $view->executed = TRUE; - $view->result = array(); - $output = $view->preview(); - $output = \Drupal::service('renderer')->renderRoot($output); - - $this->assertTrue(strpos($output, 'custom text') !== FALSE, 'Empty handler got rendered on an empty table.'); - } - - /** - * Prepares a view executable by initializing everything which is needed. - * - * @param \Drupal\views\ViewExecutable $view - * The executable to prepare. - */ - protected function prepareView(ViewExecutable $view) { - $view->setDisplay(); - $view->initStyle(); - $view->initHandlers(); - $view->initQuery(); - } - -} diff --git a/core/modules/views/src/Tests/Plugin/StyleTestBase.php b/core/modules/views/src/Tests/Plugin/StyleTestBase.php deleted file mode 100644 index 508fc12..0000000 --- a/core/modules/views/src/Tests/Plugin/StyleTestBase.php +++ /dev/null @@ -1,38 +0,0 @@ -loadHTML('' . $output . ''); - if ($htmlDom) { - // It's much easier to work with simplexml than DOM, luckily enough - // we can just simply import our DOM tree. - $this->elements = simplexml_import_dom($htmlDom); - } - } - -} diff --git a/core/modules/views/src/Tests/Plugin/StyleUnformattedTest.php b/core/modules/views/src/Tests/Plugin/StyleUnformattedTest.php deleted file mode 100644 index af45c3c..0000000 --- a/core/modules/views/src/Tests/Plugin/StyleUnformattedTest.php +++ /dev/null @@ -1,47 +0,0 @@ -setDisplay(); - $output = $view->preview(); - $this->storeViewPreview(\Drupal::service('renderer')->renderRoot($output)); - - $rows = $this->elements->body->div->div; - $count = 0; - $count_result = count($view->result); - foreach ($rows as $row) { - $count++; - $attributes = $row->attributes(); - $class = (string) $attributes['class'][0]; - $this->assertTrue(strpos($class, 'views-row') !== FALSE, 'Make sure that the views row class is set right.'); - } - $this->assertIdentical($count, $count_result); - } - -} diff --git a/core/modules/views/src/Tests/Plugin/ViewsBlockTest.php b/core/modules/views/src/Tests/Plugin/ViewsBlockTest.php deleted file mode 100644 index 1cb8b09..0000000 --- a/core/modules/views/src/Tests/Plugin/ViewsBlockTest.php +++ /dev/null @@ -1,59 +0,0 @@ - 'views', - ); - $plugin_id = 'views_block:test_view_block-block_1'; - $views_block = ViewsBlock::create($this->container, array(), $plugin_id, $plugin_definition); - - $this->assertEqual($views_block->getMachineNameSuggestion(), 'views_block__test_view_block_block_1'); - } - -} diff --git a/core/modules/views/src/Tests/PluginInstanceTest.php b/core/modules/views/src/Tests/PluginInstanceTest.php deleted file mode 100644 index 8647030..0000000 --- a/core/modules/views/src/Tests/PluginInstanceTest.php +++ /dev/null @@ -1,101 +0,0 @@ -definitions = Views::getPluginDefinitions(); - } - - /** - * Confirms that there is plugin data for all views plugin types. - */ - public function testPluginData() { - // Check that we have an array of data. - $this->assertTrue(is_array($this->definitions), 'Plugin data is an array.'); - - // Check all plugin types. - foreach ($this->pluginTypes as $type) { - $this->assertTrue(array_key_exists($type, $this->definitions), format_string('Key for plugin type @type found.', array('@type' => $type))); - $this->assertTrue(is_array($this->definitions[$type]) && !empty($this->definitions[$type]), format_string('Plugin type @type has an array of plugins.', array('@type' => $type))); - } - - // Tests that the plugin list has not missed any types. - $diff = array_diff(array_keys($this->definitions), $this->pluginTypes); - $this->assertTrue(empty($diff), 'All plugins were found and matched.'); - } - - /** - * Tests creating instances of every views plugin. - * - * This will iterate through all plugins from _views_fetch_plugin_data(). - */ - public function testPluginInstances() { - foreach ($this->definitions as $type => $plugins) { - // Get a plugin manager for this type. - $manager = $this->container->get("plugin.manager.views.$type"); - foreach ($plugins as $id => $definition) { - // Get a reflection class for this plugin. - // We only want to test true plugins, i.e. They extend PluginBase. - $reflection = new \ReflectionClass($definition['class']); - if ($reflection->isSubclassOf('Drupal\views\Plugin\views\PluginBase')) { - // Create a plugin instance and check what it is. This is not just - // good to check they can be created but for throwing any notices for - // method signatures etc. too. - $instance = $manager->createInstance($id); - $this->assertTrue($instance instanceof $definition['class'], format_string('Instance of @type:@id created', array('@type' => $type, '@id' => $id))); - } - } - } - } - -} diff --git a/core/modules/views/src/Tests/QueryGroupByTest.php b/core/modules/views/src/Tests/QueryGroupByTest.php deleted file mode 100644 index 0cb38a1..0000000 --- a/core/modules/views/src/Tests/QueryGroupByTest.php +++ /dev/null @@ -1,339 +0,0 @@ -installEntitySchema('user'); - $this->installEntitySchema('entity_test'); - $this->installEntitySchema('entity_test_mul'); - - $this->storage = $this->container->get('entity.manager')->getStorage('entity_test'); - - ConfigurableLanguage::createFromLangcode('it')->save(); - } - - - /** - * Tests aggregate count feature. - */ - public function testAggregateCount() { - $this->setupTestEntities(); - - $view = Views::getView('test_aggregate_count'); - $this->executeView($view); - - $this->assertEqual(count($view->result), 2, 'Make sure the count of items is right.'); - - $types = array(); - foreach ($view->result as $item) { - // num_records is a alias for id. - $types[$item->entity_test_name] = $item->num_records; - } - - $this->assertEqual($types['name1'], 4, 'Groupby the name: name1 returned the expected amount of results.'); - $this->assertEqual($types['name2'], 3, 'Groupby the name: name2 returned the expected amount of results.'); - } - - /** - * Provides a test helper which runs a view with some aggregation function. - * - * @param string|null $aggregation_function - * Which aggregation function should be used, for example sum or count. If - * NULL is passed the aggregation will be tested with no function. - * @param array $values - * The expected views result. - */ - public function groupByTestHelper($aggregation_function, $values) { - $this->setupTestEntities(); - - $view = Views::getView('test_group_by_count'); - $view->setDisplay(); - // There is no need for a function in order to have aggregation. - if (empty($aggregation_function)) { - // The test table has 2 fields ('id' and 'name'). We'll remove 'id' - // because it's unique and will test aggregation on 'name'. - unset($view->displayHandlers->get('default')->options['fields']['id']); - } - else { - $view->displayHandlers->get('default')->options['fields']['id']['group_type'] = $aggregation_function; - } - - $this->executeView($view); - - $this->assertEqual(count($view->result), 2, 'Make sure the count of items is right.'); - // Group by name to identify the right count. - $results = array(); - foreach ($view->result as $item) { - $results[$item->entity_test_name] = $item->id; - } - $this->assertEqual($results['name1'], $values[0], format_string('Aggregation with @aggregation_function and groupby name: name1 returned the expected amount of results', array('@aggregation_function' => $aggregation_function))); - $this->assertEqual($results['name2'], $values[1], format_string('Aggregation with @aggregation_function and groupby name: name2 returned the expected amount of results', array('@aggregation_function' => $aggregation_function))); - } - - /** - * Helper method that creates some test entities. - */ - protected function setupTestEntities() { - // Create 4 entities with name1 and 3 entities with name2. - $entity_1 = array( - 'name' => 'name1', - ); - - $this->storage->create($entity_1)->save(); - $this->storage->create($entity_1)->save(); - $this->storage->create($entity_1)->save(); - $this->storage->create($entity_1)->save(); - - $entity_2 = array( - 'name' => 'name2', - ); - $this->storage->create($entity_2)->save(); - $this->storage->create($entity_2)->save(); - $this->storage->create($entity_2)->save(); - } - - /** - * Tests the count aggregation function. - */ - public function testGroupByCount() { - $this->groupByTestHelper('count', array(4, 3)); - } - - /** - * Tests the sum aggregation function. - */ - public function testGroupBySum() { - $this->groupByTestHelper('sum', array(10, 18)); - } - - /** - * Tests the average aggregation function. - */ - public function testGroupByAverage() { - $this->groupByTestHelper('avg', array(2.5, 6)); - } - - /** - * Tests the min aggregation function. - */ - public function testGroupByMin() { - $this->groupByTestHelper('min', array(1, 5)); - } - - /** - * Tests the max aggregation function. - */ - public function testGroupByMax() { - $this->groupByTestHelper('max', array(4, 7)); - } - - /** - * Tests aggregation with no specific function. - */ - public function testGroupByNone() { - $this->groupByTestHelper(NULL, array(1, 5)); - } - - /** - * Tests groupby with filters. - */ - public function testGroupByCountOnlyFilters() { - // Check if GROUP BY and HAVING are included when a view - // doesn't display SUM, COUNT, MAX, etc. functions in SELECT statement. - - for ($x = 0; $x < 10; $x++) { - $this->storage->create(array('name' => 'name1'))->save(); - } - - $view = Views::getView('test_group_by_in_filters'); - $this->executeView($view); - - $this->assertTrue(strpos($view->build_info['query'], 'GROUP BY'), 'Make sure that GROUP BY is in the query'); - $this->assertTrue(strpos($view->build_info['query'], 'HAVING'), 'Make sure that HAVING is in the query'); - } - - /** - * Tests grouping on base field. - */ - public function testGroupByBaseField() { - $this->setupTestEntities(); - - $view = Views::getView('test_group_by_count'); - $view->setDisplay(); - // This tests that the GROUP BY portion of the query is properly formatted - // to include the base table to avoid ambiguous field errors. - $view->displayHandlers->get('default')->options['fields']['name']['group_type'] = 'min'; - unset($view->displayHandlers->get('default')->options['fields']['id']['group_type']); - $this->executeView($view); - $this->assertTrue(strpos($view->build_info['query'], 'GROUP BY entity_test.id'), 'GROUP BY field includes the base table name when grouping on the base field.'); - } - - /** - * Tests grouping a field with cardinality > 1. - */ - public function testGroupByFieldWithCardinality() { - $field_storage = FieldStorageConfig::create([ - 'type' => 'integer', - 'field_name' => 'field_test', - 'cardinality' => 4, - 'entity_type' => 'entity_test_mul', - ]); - $field_storage->save(); - $field = FieldConfig::create([ - 'field_name' => 'field_test', - 'entity_type' => 'entity_test_mul', - 'bundle' => 'entity_test_mul', - ]); - $field->save(); - - $entities = []; - $entity = EntityTestMul::create([ - 'field_test' => [1, 1, 1], - ]); - $entity->save(); - $entities[] = $entity; - - $entity = EntityTestMul::create([ - 'field_test' => [2, 2, 2], - ]); - $entity->save(); - $entities[] = $entity; - - $entity = EntityTestMul::create([ - 'field_test' => [2, 2, 2], - ]); - $entity->save(); - $entities[] = $entity; - - $view = Views::getView('test_group_by_count_multicardinality'); - $this->executeView($view); - $this->assertEqual(2, count($view->result)); - - $this->assertEqual('3', $view->getStyle()->getField(0, 'id')); - $this->assertEqual('1', $view->getStyle()->getField(0, 'field_test')); - $this->assertEqual('6', $view->getStyle()->getField(1, 'id')); - $this->assertEqual('2', $view->getStyle()->getField(1, 'field_test')); - - $entities[2]->field_test[0]->value = 3; - $entities[2]->field_test[1]->value = 4; - $entities[2]->field_test[2]->value = 5; - $entities[2]->save(); - - $view = Views::getView('test_group_by_count_multicardinality'); - $this->executeView($view); - $this->assertEqual(5, count($view->result)); - - $this->assertEqual('3', $view->getStyle()->getField(0, 'id')); - $this->assertEqual('1', $view->getStyle()->getField(0, 'field_test')); - $this->assertEqual('3', $view->getStyle()->getField(1, 'id')); - $this->assertEqual('2', $view->getStyle()->getField(1, 'field_test')); - $this->assertEqual('1', $view->getStyle()->getField(2, 'id')); - $this->assertEqual('3', $view->getStyle()->getField(2, 'field_test')); - $this->assertEqual('1', $view->getStyle()->getField(3, 'id')); - $this->assertEqual('4', $view->getStyle()->getField(3, 'field_test')); - $this->assertEqual('1', $view->getStyle()->getField(4, 'id')); - $this->assertEqual('5', $view->getStyle()->getField(4, 'field_test')); - - // Check that translated values are correctly retrieved and are not grouped - // into the original entity. - $translation = $entity->addTranslation('it'); - $translation->field_test = [6, 6, 6]; - $translation->save(); - - $view = Views::getView('test_group_by_count_multicardinality'); - $this->executeView($view); - - $this->assertEqual(6, count($view->result)); - $this->assertEqual('3', $view->getStyle()->getField(5, 'id')); - $this->assertEqual('6', $view->getStyle()->getField(5, 'field_test')); - } - - /** - * Tests groupby with a field not existing on some bundle. - */ - public function testGroupByWithFieldsNotExistingOnBundle() { - $field_storage = FieldStorageConfig::create([ - 'type' => 'integer', - 'field_name' => 'field_test', - 'cardinality' => 4, - 'entity_type' => 'entity_test_mul', - ]); - $field_storage->save(); - $field = FieldConfig::create([ - 'field_name' => 'field_test', - 'entity_type' => 'entity_test_mul', - 'bundle' => 'entity_test_mul', - ]); - $field->save(); - - $entities = []; - $entity = EntityTestMul::create([ - 'field_test' => [1], - 'type' => 'entity_test_mul', - ]); - $entity->save(); - $entities[] = $entity; - - $entity = EntityTestMul::create([ - 'type' => 'entity_test_mul2', - ]); - $entity->save(); - $entities[] = $entity; - - $view = Views::getView('test_group_by_field_not_within_bundle'); - $this->executeView($view); - - $this->assertEqual(2, count($view->result)); - // The first result is coming from entity_test_mul2, so no field could be - // rendered. - $this->assertEqual('', $view->getStyle()->getField(0, 'field_test')); - // The second result is coming from entity_test_mul, so its field value - // could be rendered. - $this->assertEqual('1', $view->getStyle()->getField(1, 'field_test')); - } - -} diff --git a/core/modules/views/src/Tests/RenderCacheIntegrationTest.php b/core/modules/views/src/Tests/RenderCacheIntegrationTest.php deleted file mode 100644 index a5b7cad..0000000 --- a/core/modules/views/src/Tests/RenderCacheIntegrationTest.php +++ /dev/null @@ -1,307 +0,0 @@ -installEntitySchema('entity_test'); - $this->installEntitySchema('user'); - } - - /** - * Tests a field-based view's cache tags when using the "none" cache plugin. - */ - public function testFieldBasedViewCacheTagsWithCachePluginNone() { - $view = Views::getview('entity_test_fields'); - $view->getDisplay()->overrideOption('cache', [ - 'type' => 'none', - ]); - $view->save(); - - $this->assertCacheTagsForFieldBasedView(FALSE); - } - - /** - * Tests a field-based view's cache tags when using the "tag" cache plugin. - */ - public function testFieldBasedViewCacheTagsWithCachePluginTag() { - $view = Views::getview('entity_test_fields'); - $view->getDisplay()->overrideOption('cache', [ - 'type' => 'tag', - ]); - $view->save(); - - $this->assertCacheTagsForFieldBasedView(TRUE); - } - - /** - * Tests a field-based view's cache tags when using the "time" cache plugin. - */ - public function testFieldBasedViewCacheTagsWithCachePluginTime() { - $view = Views::getview('entity_test_fields'); - $view->getDisplay()->overrideOption('cache', [ - 'type' => 'time', - 'options' => [ - 'results_lifespan' => 3600, - 'output_lifespan' => 3600, - ], - ]); - $view->save(); - - $this->assertCacheTagsForFieldBasedView(TRUE); - } - - /** - * Tests cache tags on output & result cache items for a field-based view. - * - * @param bool $do_assert_views_caches - * Whether to check Views' result & output caches. - */ - protected function assertCacheTagsForFieldBasedView($do_assert_views_caches) { - $this->pass('Checking cache tags for field-based view.'); - $view = Views::getview('entity_test_fields'); - - // Empty result (no entities yet). - $this->pass('Test without entities'); - $base_tags = ['config:views.view.entity_test_fields', 'entity_test_list']; - $this->assertViewsCacheTags($view, $base_tags, $do_assert_views_caches, $base_tags); - $this->assertViewsCacheTagsFromStaticRenderArray($view, $base_tags, $do_assert_views_caches); - - - // Non-empty result (1 entity). - /** @var \Drupal\Core\Entity\EntityInterface[] $entities */ - $entities[] = $entity = EntityTest::create(); - $entity->save(); - - $this->pass('Test with entities'); - $tags_with_entity = Cache::mergeTags($base_tags, $entities[0]->getCacheTags()); - $this->assertViewsCacheTags($view, $tags_with_entity, $do_assert_views_caches, $tags_with_entity); - $this->assertViewsCacheTagsFromStaticRenderArray($view, $tags_with_entity, $do_assert_views_caches); - - - // Paged result (more entities than the items-per-page limit). - for ($i = 0; $i < 5; $i++) { - $entities[] = $entity = EntityTest::create(); - $entity->save(); - } - - // Page 1. - $this->pass('Test pager'); - $this->pass('Page 1'); - \Drupal::request()->query->set('page', 0); - $tags_page_1 = Cache::mergeTags($base_tags, $entities[1]->getCacheTags()); - $tags_page_1 = Cache::mergeTags($tags_page_1, $entities[2]->getCacheTags()); - $tags_page_1 = Cache::mergeTags($tags_page_1, $entities[3]->getCacheTags()); - $tags_page_1 = Cache::mergeTags($tags_page_1, $entities[4]->getCacheTags()); - $tags_page_1 = Cache::mergeTags($tags_page_1, $entities[5]->getCacheTags()); - $this->assertViewsCacheTags($view, $tags_page_1, $do_assert_views_caches, $tags_page_1); - $this->assertViewsCacheTagsFromStaticRenderArray($view, $tags_page_1, $do_assert_views_caches); - $view->destroy(); - // Page 2. - $this->pass('Page 2'); - $view->setCurrentPage(1); - \Drupal::request()->query->set('page', 1); - $tags_page_2 = Cache::mergeTags($base_tags, $entities[0]->getCacheTags()); - $this->assertViewsCacheTags($view, $tags_page_2, $do_assert_views_caches, $tags_page_2); - $view->destroy(); - - // Ensure that invalidation works on both pages. - $this->pass('Page invalidations'); - $this->pass('Page 2'); - $view->setCurrentPage(1); - \Drupal::request()->query->set('page', 1); - $entities[0]->name->value = $random_name = $this->randomMachineName(); - $entities[0]->save(); - $build = $this->assertViewsCacheTags($view, $tags_page_2, $do_assert_views_caches, $tags_page_2); - // @todo Static render arrays don't support different pages yet, see - // https://www.drupal.org/node/2500701. - // $this->assertViewsCacheTagsFromStaticRenderArray($view, $tags_page_2, $do_assert_views_caches); - $this->assertTrue(strpos($build['#markup'], $random_name) !== FALSE); - $view->destroy(); - - $this->pass('Page 1'); - $view->setCurrentPage(0); - \Drupal::request()->query->set('page', 0); - $entities[1]->name->value = $random_name = $this->randomMachineName(); - $entities[1]->save(); - $build = $this->assertViewsCacheTags($view, $tags_page_1, $do_assert_views_caches, $tags_page_1); - $this->assertViewsCacheTagsFromStaticRenderArray($view, $tags_page_1, $do_assert_views_caches); - $this->assertTrue(strpos($build['#markup'], $random_name) !== FALSE); - $view->destroy(); - - // Setup arguments to ensure that render caching also varies by them. - $this->pass('Test arguments'); - - // Custom assert for a single result row. - $single_entity_assertions = function(array $build, EntityInterface $entity) { - $this->setRawContent($build['#markup']); - - $result = $this->cssSelect('div.views-row'); - $count = count($result); - $this->assertEqual($count, 1); - - $this->assertEqual((string) $result[0]->div->span, (string) $entity->id()); - }; - - // Execute the view once with a static renderable and one with a full - // prepared render array. - $tags_argument = Cache::mergeTags($base_tags, $entities[0]->getCacheTags()); - $view->setArguments([$entities[0]->id()]); - $build = $this->assertViewsCacheTags($view, $tags_argument, $do_assert_views_caches, $tags_argument); - $single_entity_assertions($build, $entities[0]); - - $view->setArguments([$entities[0]->id()]); - $build = $this->assertViewsCacheTagsFromStaticRenderArray($view, $tags_argument, $do_assert_views_caches); - $single_entity_assertions($build, $entities[0]); - - // Set a different argument and ensure that the result is different. - $tags2_argument = Cache::mergeTags($base_tags, $entities[1]->getCacheTags()); - $view->setArguments([$entities[1]->id()]); - $build = $this->assertViewsCacheTagsFromStaticRenderArray($view, $tags2_argument, $do_assert_views_caches); - $single_entity_assertions($build, $entities[1]); - - $view->destroy(); - } - - /** - * Tests a entity-based view's cache tags when using the "none" cache plugin. - */ - public function testEntityBasedViewCacheTagsWithCachePluginNone() { - $view = Views::getview('entity_test_row'); - $view->getDisplay()->overrideOption('cache', [ - 'type' => 'none', - ]); - $view->save(); - - $this->assertCacheTagsForEntityBasedView(FALSE); - } - - /** - * Tests a entity-based view's cache tags when using the "tag" cache plugin. - */ - public function testEntityBasedViewCacheTagsWithCachePluginTag() { - $view = Views::getview('entity_test_row'); - $view->getDisplay()->overrideOption('cache', [ - 'type' => 'tag', - ]); - $view->save(); - - $this->assertCacheTagsForEntityBasedView(TRUE); - } - - /** - * Tests a entity-based view's cache tags when using the "time" cache plugin. - */ - public function testEntityBasedViewCacheTagsWithCachePluginTime() { - $view = Views::getview('entity_test_row'); - $view->getDisplay()->overrideOption('cache', [ - 'type' => 'time', - 'options' => [ - 'results_lifespan' => 3600, - 'output_lifespan' => 3600, - ], - ]); - $view->save(); - - $this->assertCacheTagsForEntityBasedView(TRUE); - } - - /** - * Tests cache tags on output & result cache items for an entity-based view. - */ - protected function assertCacheTagsForEntityBasedView($do_assert_views_caches) { - $this->pass('Checking cache tags for entity-based view.'); - $view = Views::getview('entity_test_row'); - - // Empty result (no entities yet). - $base_tags = $base_render_tags = ['config:views.view.entity_test_row', 'entity_test_list']; - $this->assertViewsCacheTags($view, $base_tags, $do_assert_views_caches, $base_tags); - $this->assertViewsCacheTagsFromStaticRenderArray($view, $base_tags, $do_assert_views_caches); - - // Non-empty result (1 entity). - $entities[] = $entity = EntityTest::create(); - $entity->save(); - - $result_tags_with_entity = Cache::mergeTags($base_tags, $entities[0]->getCacheTags()); - $render_tags_with_entity = Cache::mergeTags($base_render_tags, $entities[0]->getCacheTags()); - $render_tags_with_entity = Cache::mergeTags($render_tags_with_entity, ['entity_test_view']); - $this->assertViewsCacheTags($view, $result_tags_with_entity, $do_assert_views_caches, $render_tags_with_entity); - $this->assertViewsCacheTagsFromStaticRenderArray($view, $render_tags_with_entity, $do_assert_views_caches); - - - // Paged result (more entities than the items-per-page limit). - for ($i = 0; $i < 5; $i++) { - $entities[] = $entity = EntityTest::create(); - $entity->save(); - } - - $new_entities_cache_tags = Cache::mergeTags($entities[1]->getCacheTags(), $entities[2]->getCacheTags()); - $new_entities_cache_tags = Cache::mergeTags($new_entities_cache_tags, $entities[3]->getCacheTags()); - $new_entities_cache_tags = Cache::mergeTags($new_entities_cache_tags, $entities[4]->getCacheTags()); - $new_entities_cache_tags = Cache::mergeTags($new_entities_cache_tags, $entities[5]->getCacheTags()); - $result_tags_page_1 = Cache::mergeTags($base_tags, $new_entities_cache_tags); - $render_tags_page_1 = Cache::mergeTags($base_render_tags, $new_entities_cache_tags); - $render_tags_page_1 = Cache::mergeTags($render_tags_page_1, ['entity_test_view']); - $this->assertViewsCacheTags($view, $result_tags_page_1, $do_assert_views_caches, $render_tags_page_1); - $this->assertViewsCacheTagsFromStaticRenderArray($view, $render_tags_page_1, $do_assert_views_caches); - } - - /** - * Ensure that the view renderable contains the cache contexts. - */ - public function testBuildRenderableWithCacheContexts() { - $view = View::load('test_view'); - $display =& $view->getDisplay('default'); - $display['cache_metadata']['contexts'] = ['views_test_cache_context']; - $executable = $view->getExecutable(); - - $build = $executable->buildRenderable(); - $this->assertEqual(['views_test_cache_context'], $build['#cache']['contexts']); - } - - /** - * Ensures that saving a view calculates the cache contexts. - */ - public function testViewAddCacheMetadata() { - $view = View::load('test_display'); - $view->save(); - - $this->assertEqual(['languages:' . LanguageInterface::TYPE_CONTENT, 'languages:' . LanguageInterface::TYPE_INTERFACE, 'url.query_args', 'user.node_grants:view', 'user.permissions'], $view->getDisplay('default')['cache_metadata']['contexts']); - } - -} diff --git a/core/modules/views/src/Tests/TokenReplaceTest.php b/core/modules/views/src/Tests/TokenReplaceTest.php deleted file mode 100644 index 149280f..0000000 --- a/core/modules/views/src/Tests/TokenReplaceTest.php +++ /dev/null @@ -1,99 +0,0 @@ -installSchema('system', 'url_alias'); - $this->container->get('router.builder')->rebuild(); - } - - /** - * Tests core token replacements generated from a view. - */ - function testTokenReplacement() { - $token_handler = \Drupal::token(); - $view = Views::getView('test_tokens'); - $view->setDisplay('page_1'); - $this->executeView($view); - - $expected = array( - '[view:label]' => 'Test tokens', - '[view:description]' => 'Test view to token replacement tests.', - '[view:id]' => 'test_tokens', - '[view:title]' => 'Test token page', - '[view:url]' => $view->getUrl(NULL, 'page_1')->setAbsolute(TRUE)->toString(), - '[view:total-rows]' => (string) $view->total_rows, - '[view:base-table]' => 'views_test_data', - '[view:base-field]' => 'id', - '[view:items-per-page]' => '10', - '[view:current-page]' => '1', - '[view:page-count]' => '1', - ); - - $base_bubbleable_metadata = BubbleableMetadata::createFromObject($view->storage); - $metadata_tests = []; - $metadata_tests['[view:label]'] = $base_bubbleable_metadata; - $metadata_tests['[view:description]'] = $base_bubbleable_metadata; - $metadata_tests['[view:id]'] = $base_bubbleable_metadata; - $metadata_tests['[view:title]'] = $base_bubbleable_metadata; - $metadata_tests['[view:url]'] = $base_bubbleable_metadata; - $metadata_tests['[view:total-rows]'] = $base_bubbleable_metadata; - $metadata_tests['[view:base-table]'] = $base_bubbleable_metadata; - $metadata_tests['[view:base-field]'] = $base_bubbleable_metadata; - $metadata_tests['[view:items-per-page]'] = $base_bubbleable_metadata; - $metadata_tests['[view:current-page]'] = $base_bubbleable_metadata; - $metadata_tests['[view:page-count]'] = $base_bubbleable_metadata; - - foreach ($expected as $token => $expected_output) { - $bubbleable_metadata = new BubbleableMetadata(); - $output = $token_handler->replace($token, array('view' => $view), [], $bubbleable_metadata); - $this->assertIdentical($output, $expected_output, format_string('Token %token replaced correctly.', array('%token' => $token))); - $this->assertEqual($bubbleable_metadata, $metadata_tests[$token]); - } - } - - /** - * Tests core token replacements generated from a view without results. - */ - function testTokenReplacementNoResults() { - $token_handler = \Drupal::token(); - $view = Views::getView('test_tokens'); - $view->setDisplay('page_2'); - $this->executeView($view); - - $expected = array( - '[view:page-count]' => '1', - ); - - foreach ($expected as $token => $expected_output) { - $output = $token_handler->replace($token, array('view' => $view)); - $this->assertIdentical($output, $expected_output, format_string('Token %token replaced correctly.', array('%token' => $token))); - } - } - -} diff --git a/core/modules/views/src/Tests/Update/ImageStyleDependencyUpdateTest.php b/core/modules/views/src/Tests/Update/ImageStyleDependencyUpdateTest.php new file mode 100644 index 0000000..be2521c --- /dev/null +++ b/core/modules/views/src/Tests/Update/ImageStyleDependencyUpdateTest.php @@ -0,0 +1,56 @@ +databaseDumpFiles = [ + __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz', + __DIR__ . '/../../../../system/tests/fixtures/update/drupal8.views-image-style-dependency-2649914.php', + ]; + } + + /** + * Tests the updating of views dependencies to image styles. + */ + public function testUpdateImageStyleDependencies() { + $config_dependencies = View::load('foo')->getDependencies()['config']; + + // Checks that 'thumbnail' image style is not a dependency of view 'foo'. + $this->assertFalse(in_array('image.style.thumbnail', $config_dependencies)); + + // We test the case the the field formatter image style doesn't exist. + // Checks that 'nonexistent' image style is not a dependency of view 'foo'. + $this->assertFalse(in_array('image.style.nonexistent', $config_dependencies)); + + // Run updates. + $this->runUpdates(); + + $config_dependencies = View::load('foo')->getDependencies()['config']; + + // Checks that 'thumbnail' image style is a dependency of view 'foo'. + $this->assertTrue(in_array('image.style.thumbnail', $config_dependencies)); + + // The 'nonexistent' style doesn't exist, thus is not a dependency. Checks + // that 'nonexistent' image style is a not dependency of view 'foo'. + $this->assertFalse(in_array('image.style.nonexistent', $config_dependencies)); + } + +} diff --git a/core/modules/views/src/Tests/ViewExecutableTest.php b/core/modules/views/src/Tests/ViewExecutableTest.php deleted file mode 100644 index e4f17b5..0000000 --- a/core/modules/views/src/Tests/ViewExecutableTest.php +++ /dev/null @@ -1,486 +0,0 @@ -installEntitySchema('user'); - $this->installEntitySchema('node'); - $this->installEntitySchema('comment'); - $this->installSchema('comment', array('comment_entity_statistics')); - $this->installConfig(array('system', 'field', 'node', 'comment')); - - entity_create('node_type', array( - 'type' => 'page', - 'name' => 'Page', - ))->save(); - $this->addDefaultCommentField('node', 'page'); - parent::setUpFixtures(); - - $this->installConfig(array('filter')); - } - - /** - * Tests the views.executable container service. - */ - public function testFactoryService() { - $factory = $this->container->get('views.executable'); - $this->assertTrue($factory instanceof ViewExecutableFactory, 'A ViewExecutableFactory instance was returned from the container.'); - $view = entity_load('view', 'test_executable_displays'); - $this->assertTrue($factory->get($view) instanceof ViewExecutable, 'A ViewExecutable instance was returned from the factory.'); - } - - /** - * Tests the initDisplay() and initHandlers() methods. - */ - public function testInitMethods() { - $view = Views::getView('test_destroy'); - $view->initDisplay(); - - $this->assertTrue($view->display_handler instanceof DefaultDisplay, 'Make sure a reference to the current display handler is set.'); - $this->assertTrue($view->displayHandlers->get('default') instanceof DefaultDisplay, 'Make sure a display handler is created for each display.'); - - $view->destroy(); - $view->initHandlers(); - - // Check for all handler types. - $handler_types = array_keys(ViewExecutable::getHandlerTypes()); - foreach ($handler_types as $type) { - // The views_test integration doesn't have relationships. - if ($type == 'relationship') { - continue; - } - $this->assertTrue(count($view->$type), format_string('Make sure a %type instance got instantiated.', array('%type' => $type))); - } - - // initHandlers() should create display handlers automatically as well. - $this->assertTrue($view->display_handler instanceof DefaultDisplay, 'Make sure a reference to the current display handler is set.'); - $this->assertTrue($view->displayHandlers->get('default') instanceof DefaultDisplay, 'Make sure a display handler is created for each display.'); - - $view_hash = spl_object_hash($view); - $display_hash = spl_object_hash($view->display_handler); - - // Test the initStyle() method. - $view->initStyle(); - $this->assertTrue($view->style_plugin instanceof DefaultStyle, 'Make sure a reference to the style plugin is set.'); - // Test the plugin has been inited and view have references to the view and - // display handler. - $this->assertEqual(spl_object_hash($view->style_plugin->view), $view_hash); - $this->assertEqual(spl_object_hash($view->style_plugin->displayHandler), $display_hash); - - // Test the initQuery method(). - $view->initQuery(); - $this->assertTrue($view->query instanceof Sql, 'Make sure a reference to the query is set'); - $this->assertEqual(spl_object_hash($view->query->view), $view_hash); - $this->assertEqual(spl_object_hash($view->query->displayHandler), $display_hash); - - $view->destroy(); - - // Test the plugin get methods. - $display_plugin = $view->getDisplay(); - $this->assertTrue($display_plugin instanceof DefaultDisplay, 'An instance of DefaultDisplay was returned.'); - $this->assertTrue($view->display_handler instanceof DefaultDisplay, 'The display_handler property has been set.'); - $this->assertIdentical($display_plugin, $view->getDisplay(), 'The same display plugin instance was returned.'); - - $style_plugin = $view->getStyle(); - $this->assertTrue($style_plugin instanceof DefaultStyle, 'An instance of DefaultStyle was returned.'); - $this->assertTrue($view->style_plugin instanceof DefaultStyle, 'The style_plugin property has been set.'); - $this->assertIdentical($style_plugin, $view->getStyle(), 'The same style plugin instance was returned.'); - - $pager_plugin = $view->getPager(); - $this->assertTrue($pager_plugin instanceof PagerPluginBase, 'An instance of PagerPluginBase was returned.'); - $this->assertTrue($view->pager instanceof PagerPluginBase, 'The pager property has been set.'); - $this->assertIdentical($pager_plugin, $view->getPager(), 'The same pager plugin instance was returned.'); - - $query_plugin = $view->getQuery(); - $this->assertTrue($query_plugin instanceof QueryPluginBase, 'An instance of QueryPluginBase was returned.'); - $this->assertTrue($view->query instanceof QueryPluginBase, 'The query property has been set.'); - $this->assertIdentical($query_plugin, $view->getQuery(), 'The same query plugin instance was returned.'); - } - - /** - * Tests the generation of the executable object. - */ - public function testConstructing() { - Views::getView('test_destroy'); - } - - /** - * Tests the accessing of values on the object. - */ - public function testProperties() { - $view = Views::getView('test_destroy'); - foreach ($this->executableProperties as $property) { - $this->assertTrue(isset($view->{$property})); - } - - // Per default exposed input should fall back to an empty array. - $this->assertEqual($view->getExposedInput(), []); - } - - /** - * Tests the display related methods and properties. - */ - public function testDisplays() { - $view = Views::getView('test_executable_displays'); - - // Tests Drupal\views\ViewExecutable::initDisplay(). - $view->initDisplay(); - $this->assertTrue($view->displayHandlers instanceof DisplayPluginCollection, 'The displayHandlers property has the right class.'); - // Tests the classes of the instances. - $this->assertTrue($view->displayHandlers->get('default') instanceof DefaultDisplay); - $this->assertTrue($view->displayHandlers->get('page_1') instanceof Page); - $this->assertTrue($view->displayHandlers->get('page_2') instanceof Page); - - // After initializing the default display is the current used display. - $this->assertEqual($view->current_display, 'default'); - $this->assertEqual(spl_object_hash($view->display_handler), spl_object_hash($view->displayHandlers->get('default'))); - - // All handlers should have a reference to the default display. - $this->assertEqual(spl_object_hash($view->displayHandlers->get('page_1')->default_display), spl_object_hash($view->displayHandlers->get('default'))); - $this->assertEqual(spl_object_hash($view->displayHandlers->get('page_2')->default_display), spl_object_hash($view->displayHandlers->get('default'))); - - // Tests Drupal\views\ViewExecutable::setDisplay(). - $view->setDisplay(); - $this->assertEqual($view->current_display, 'default', 'If setDisplay is called with no parameter the default display should be used.'); - $this->assertEqual(spl_object_hash($view->display_handler), spl_object_hash($view->displayHandlers->get('default'))); - - // Set two different valid displays. - $view->setDisplay('page_1'); - $this->assertEqual($view->current_display, 'page_1', 'If setDisplay is called with a valid display id the appropriate display should be used.'); - $this->assertEqual(spl_object_hash($view->display_handler), spl_object_hash($view->displayHandlers->get('page_1'))); - - $view->setDisplay('page_2'); - $this->assertEqual($view->current_display, 'page_2', 'If setDisplay is called with a valid display id the appropriate display should be used.'); - $this->assertEqual(spl_object_hash($view->display_handler), spl_object_hash($view->displayHandlers->get('page_2'))); - - // Destroy the view, so we can start again and test an invalid display. - $view->destroy(); - - $count_before = count($this->assertions); - $view->setDisplay('invalid'); - $count_after = count($this->assertions); - $this->assertTrue($count_after - $count_before, 'Error is triggered while calling the wrong display.'); - - $this->assertEqual($view->current_display, 'default', 'If setDisplay is called with an invalid display id the default display should be used.'); - $this->assertEqual(spl_object_hash($view->display_handler), spl_object_hash($view->displayHandlers->get('default'))); - - // Test the style and row plugins are replaced correctly when setting the - // display. - $view->setDisplay('page_1'); - $view->initStyle(); - $this->assertTrue($view->style_plugin instanceof DefaultStyle); - $this->assertTrue($view->rowPlugin instanceof Fields); - - $view->setDisplay('page_2'); - $view->initStyle(); - $this->assertTrue($view->style_plugin instanceof Grid); - // @todo Change this rowPlugin type too. - $this->assertTrue($view->rowPlugin instanceof Fields); - - // Test the newDisplay() method. - $view = $this->container->get('entity.manager')->getStorage('view')->create(array('id' => 'test_executable_displays')); - $executable = $view->getExecutable(); - - $executable->newDisplay('page'); - $executable->newDisplay('page'); - $executable->newDisplay('display_test'); - - $this->assertTrue($executable->displayHandlers->get('default') instanceof DefaultDisplay); - $this->assertFalse(isset($executable->displayHandlers->get('default')->default_display)); - $this->assertTrue($executable->displayHandlers->get('page_1') instanceof Page); - $this->assertTrue($executable->displayHandlers->get('page_1')->default_display instanceof DefaultDisplay); - $this->assertTrue($executable->displayHandlers->get('page_2') instanceof Page); - $this->assertTrue($executable->displayHandlers->get('page_2')->default_display instanceof DefaultDisplay); - $this->assertTrue($executable->displayHandlers->get('display_test_1') instanceof DisplayTest); - $this->assertTrue($executable->displayHandlers->get('display_test_1')->default_display instanceof DefaultDisplay); - } - - /** - * Tests the setting/getting of properties. - */ - public function testPropertyMethods() { - $view = Views::getView('test_executable_displays'); - - // Test the setAjaxEnabled() method. - $this->assertFalse($view->ajaxEnabled()); - $view->setAjaxEnabled(TRUE); - $this->assertTrue($view->ajaxEnabled()); - - $view->setDisplay(); - // There should be no pager set initially. - $this->assertNull($view->usePager()); - - // Add a pager, initialize, and test. - $view->displayHandlers->get('default')->overrideOption('pager', array( - 'type' => 'full', - 'options' => array('items_per_page' => 10), - )); - $view->initPager(); - $this->assertTrue($view->usePager()); - - // Test setting and getting the offset. - $rand = rand(); - $view->setOffset($rand); - $this->assertEqual($view->getOffset(), $rand); - - // Test the getBaseTable() method. - $expected = array( - 'views_test_data' => TRUE, - '#global' => TRUE, - ); - $this->assertIdentical($view->getBaseTables(), $expected); - - // Test response methods. - $this->assertTrue($view->getResponse() instanceof Response, 'New response object returned.'); - $new_response = new Response(); - $view->setResponse($new_response); - $this->assertIdentical(spl_object_hash($view->getResponse()), spl_object_hash($new_response), 'New response object correctly set.'); - - // Test the getPath() method. - $path = $this->randomMachineName(); - $view->displayHandlers->get('page_1')->overrideOption('path', $path); - $view->setDisplay('page_1'); - $this->assertEqual($view->getPath(), $path); - // Test the override_path property override. - $override_path = $this->randomMachineName(); - $view->override_path = $override_path; - $this->assertEqual($view->getPath(), $override_path); - - // Test the title methods. - $title = $this->randomString(); - $view->setTitle($title); - $this->assertEqual($view->getTitle(), Xss::filterAdmin($title)); - } - - /** - * Tests the deconstructor to be sure that necessary objects are removed. - */ - public function testDestroy() { - $view = Views::getView('test_destroy'); - - $view->preview(); - $view->destroy(); - - $this->assertViewDestroy($view); - } - - /** - * Asserts that expected view properties have been unset by destroy(). - * - * @param \Drupal\views\ViewExecutable $view - */ - protected function assertViewDestroy($view) { - $reflection = new \ReflectionClass($view); - $defaults = $reflection->getDefaultProperties(); - // The storage and user should remain. - unset($defaults['storage'], $defaults['user'], $defaults['request'], $defaults['routeProvider']); - - foreach ($defaults as $property => $default) { - $this->assertIdentical($this->getProtectedProperty($view, $property), $default); - } - } - - /** - * Returns a protected property from a class instance. - * - * @param object $instance - * The class instance to return the property from. - * @param string $property - * The name of the property to return. - * - * @return mixed - * The instance property value. - */ - protected function getProtectedProperty($instance, $property) { - $reflection = new \ReflectionProperty($instance, $property); - $reflection->setAccessible(TRUE); - return $reflection->getValue($instance); - } - - /** - * Tests ViewExecutable::getHandlerTypes(). - */ - public function testGetHandlerTypes() { - $types = ViewExecutable::getHandlerTypes(); - foreach (array('field', 'filter', 'argument', 'sort', 'header', 'footer', 'empty') as $type) { - $this->assertTrue(isset($types[$type])); - // @todo The key on the display should be footers, headers and empties - // or something similar instead of the singular, but so long check for - // this special case. - if (isset($types[$type]['type']) && $types[$type]['type'] == 'area') { - $this->assertEqual($types[$type]['plural'], $type); - } - else { - $this->assertEqual($types[$type]['plural'], $type . 's'); - } - } - } - - /** - * Tests ViewExecutable::getHandlers(). - */ - public function testGetHandlers() { - $view = Views::getView('test_executable_displays'); - $view->setDisplay('page_1'); - - $view->getHandlers('field', 'page_2'); - - // getHandlers() shouldn't change the active display. - $this->assertEqual('page_1', $view->current_display, "The display shouldn't change after getHandlers()"); - } - - /** - * Tests the validation of display handlers. - */ - public function testValidate() { - $view = Views::getView('test_executable_displays'); - $view->setDisplay('page_1'); - - $validate = $view->validate(); - - // Validating a view shouldn't change the active display. - $this->assertEqual('page_1', $view->current_display, "The display should be constant while validating"); - - $count = 0; - foreach ($view->displayHandlers as $id => $display) { - $match = function($value) use ($display) { - return strpos($value, $display->display['display_title']) !== false; - }; - $this->assertTrue(array_filter($validate[$id], $match), format_string('Error message found for @id display', array('@id' => $id))); - $count++; - } - - $this->assertEqual(count($view->displayHandlers), $count, 'Error messages from all handlers merged.'); - - // Test that a deleted display is not included. - $display = &$view->storage->getDisplay('default'); - $display['deleted'] = TRUE; - $validate_deleted = $view->validate(); - - $this->assertNotIdentical($validate, $validate_deleted, 'Master display has not been validated.'); - } - - /** - * Tests that nested loops of the display handlers won't break validation. - */ - public function testValidateNestedLoops() { - $view = View::create(['id' => 'test_validate_nested_loops']); - $executable = $view->getExecutable(); - - $executable->newDisplay('display_test'); - $executable->newDisplay('display_test'); - $errors = $executable->validate(); - $total_error_count = array_reduce($errors, function ($carry, $item) { - $carry += count($item); - - return $carry; - }); - // Assert that there were 9 total errors across 3 displays. - $this->assertIdentical(9, $total_error_count); - $this->assertIdentical(3, count($errors)); - } - - /** - * Tests serialization of the ViewExecutable object. - */ - public function testSerialization() { - $view = Views::getView('test_executable_displays'); - $view->setDisplay('page_1'); - $view->setArguments(['test']); - $view->setCurrentPage(2); - - $serialized = serialize($view); - - // Test the view storage object is not present in the actual serialized - // string. - $this->assertIdentical(strpos($serialized, '"Drupal\views\Entity\View"'), FALSE, 'The Drupal\views\Entity\View class was not found in the serialized string.'); - - /** @var \Drupal\views\ViewExecutable $unserialized */ - $unserialized = unserialize($serialized); - - $this->assertTrue($unserialized instanceof ViewExecutable); - $this->assertIdentical($view->storage->id(), $unserialized->storage->id(), 'The expected storage entity was loaded on the unserialized view.'); - $this->assertIdentical($unserialized->current_display, 'page_1', 'The expected display was set on the unserialized view.'); - $this->assertIdentical($unserialized->args, ['test'], 'The expected argument was set on the unserialized view.'); - $this->assertIdentical($unserialized->getCurrentPage(), 2, 'The expected current page was set on the unserialized view.'); - } - -} diff --git a/core/modules/views/src/Tests/ViewKernelTestBase.php b/core/modules/views/src/Tests/ViewKernelTestBase.php index fe794ea..4c3e4a5 100644 --- a/core/modules/views/src/Tests/ViewKernelTestBase.php +++ b/core/modules/views/src/Tests/ViewKernelTestBase.php @@ -8,7 +8,6 @@ namespace Drupal\views\Tests; use Drupal\Core\Database\Query\SelectInterface; -use Drupal\views\ViewsBundle; use Drupal\simpletest\KernelTestBase; /** @@ -18,7 +17,10 @@ * requires the full web test environment provided by WebTestBase, extend * ViewTestBase instead. * - * @see \Drupal\views\Tests\ViewTestBase + * @deprecated in Drupal 8.0.x, will be removed in Drupal 8.2.x. Use + * \Drupal\Tests\views\Kernel\ViewsKernelTestBase instead. + * + * @see \Drupal\Tests\views\Kernel\ViewsKernelTestBase */ abstract class ViewKernelTestBase extends KernelTestBase { @@ -42,7 +44,7 @@ protected function setUp($import_test_views = TRUE) { parent::setUp(); - $this->installSchema('system', array('router', 'sequences')); + $this->installSchema('system', array('sequences')); $this->setUpFixtures(); if ($import_test_views) { diff --git a/core/modules/views/src/Tests/ViewStorageTest.php b/core/modules/views/src/Tests/ViewStorageTest.php deleted file mode 100644 index 7728b1f..0000000 --- a/core/modules/views/src/Tests/ViewStorageTest.php +++ /dev/null @@ -1,360 +0,0 @@ -entityType = \Drupal::entityManager()->getDefinition('view'); - $this->controller = $this->container->get('entity.manager')->getStorage('view'); - - // Confirm that an info array has been returned. - $this->assertTrue($this->entityType instanceof EntityTypeInterface, 'The View info array is loaded.'); - - // CRUD tests. - $this->loadTests(); - $this->createTests(); - $this->displayTests(); - - // Helper method tests - $this->displayMethodTests(); - } - - /** - * Tests loading configuration entities. - */ - protected function loadTests() { - $view = entity_load('view', 'test_view_storage'); - $data = $this->config('views.view.test_view_storage')->get(); - - // Confirm that an actual view object is loaded and that it returns all of - // expected properties. - $this->assertTrue($view instanceof View, 'Single View instance loaded.'); - foreach ($this->configProperties as $property) { - $this->assertTrue($view->get($property) !== NULL, format_string('Property: @property loaded onto View.', array('@property' => $property))); - } - - // Check the displays have been loaded correctly from config display data. - $expected_displays = array('default', 'block_1', 'page_1'); - $this->assertEqual(array_keys($view->get('display')), $expected_displays, 'The correct display names are present.'); - - // Check each ViewDisplay object and confirm that it has the correct key and - // property values. - foreach ($view->get('display') as $key => $display) { - $this->assertEqual($key, $display['id'], 'The display has the correct ID assigned.'); - - // Get original display data and confirm that the display options array - // exists. - $original_options = $data['display'][$key]; - foreach ($original_options as $orig_key => $value) { - $this->assertIdentical($display[$orig_key], $value, format_string('@key is identical to saved data', array('@key' => $key))); - } - } - - // Make sure that loaded default views get a UUID. - $view = Views::getView('test_view_storage'); - $this->assertTrue($view->storage->uuid()); - } - - /** - * Tests creating configuration entities. - */ - protected function createTests() { - // Create a new View instance with empty values. - $created = $this->controller->create(array()); - - $this->assertTrue($created instanceof View, 'Created object is a View.'); - // Check that the View contains all of the properties. - foreach ($this->configProperties as $property) { - $this->assertTrue(property_exists($created, $property), format_string('Property: @property created on View.', array('@property' => $property))); - } - - // Create a new View instance with config values. - $values = $this->config('views.view.test_view_storage')->get(); - $values['id'] = 'test_view_storage_new'; - unset($values['uuid']); - $created = $this->controller->create($values); - - $this->assertTrue($created instanceof View, 'Created object is a View.'); - // Check that the View contains all of the properties. - $properties = $this->configProperties; - // Remove display from list. - array_pop($properties); - - // Test all properties except displays. - foreach ($properties as $property) { - $this->assertTrue($created->get($property) !== NULL, format_string('Property: @property created on View.', array('@property' => $property))); - $this->assertIdentical($values[$property], $created->get($property), format_string('Property value: @property matches configuration value.', array('@property' => $property))); - } - - // Check the UUID of the loaded View. - $created->save(); - $created_loaded = entity_load('view', 'test_view_storage_new'); - $this->assertIdentical($created->uuid(), $created_loaded->uuid(), 'The created UUID has been saved correctly.'); - } - - /** - * Tests adding, saving, and loading displays on configuration entities. - */ - protected function displayTests() { - // Check whether a display can be added and saved to a View. - $view = entity_load('view', 'test_view_storage_new'); - - $new_id = $view->addDisplay('page', 'Test', 'test'); - $display = $view->get('display'); - - // Ensure the right display_plugin is created/instantiated. - $this->assertEqual($display[$new_id]['display_plugin'], 'page', 'New page display "test" uses the right display plugin.'); - - $executable = $view->getExecutable(); - $executable->initDisplay(); - $this->assertTrue($executable->displayHandlers->get($new_id) instanceof Page, 'New page display "test" uses the right display plugin.'); - - // To save this with a new ID, we should use createDuplicate(). - $view = $view->createDuplicate(); - $view->set('id', 'test_view_storage_new_new2'); - $view->save(); - $values = $this->config('views.view.test_view_storage_new_new2')->get(); - - $this->assertTrue(isset($values['display']['test']) && is_array($values['display']['test']), 'New display was saved.'); - } - - /** - * Tests the display related functions like getDisplaysList(). - */ - protected function displayMethodTests() { - // Enable the system module so the link generator can work using url_alias - // table. - $this->installSchema('system', 'url_alias'); - - $config['display'] = array( - 'page_1' => array( - 'display_options' => array('path' => 'test'), - 'display_plugin' => 'page', - 'id' => 'page_2', - 'display_title' => 'Page 1', - 'position' => 1 - ), - 'feed_1' => array( - 'display_options' => array('path' => 'test.xml'), - 'display_plugin' => 'feed', - 'id' => 'feed', - 'display_title' => 'Feed', - 'position' => 2 - ), - 'page_2' => array( - 'display_options' => array('path' => 'test/%/extra'), - 'display_plugin' => 'page', - 'id' => 'page_2', - 'display_title' => 'Page 2', - 'position' => 3 - ) - ); - $view = $this->controller->create($config); - - // Tests Drupal\views\Entity\View::addDisplay() - $view = $this->controller->create(array()); - $random_title = $this->randomMachineName(); - - $id = $view->addDisplay('page', $random_title); - $this->assertEqual($id, 'page_1', format_string('Make sure the first display (%id_new) has the expected ID (%id)', array('%id_new' => $id, '%id' => 'page_1'))); - $display = $view->get('display'); - $this->assertEqual($display[$id]['display_title'], $random_title); - - $random_title = $this->randomMachineName(); - $id = $view->addDisplay('page', $random_title); - $display = $view->get('display'); - $this->assertEqual($id, 'page_2', format_string('Make sure the second display (%id_new) has the expected ID (%id)', array('%id_new' => $id, '%id' => 'page_2'))); - $this->assertEqual($display[$id]['display_title'], $random_title); - - $id = $view->addDisplay('page'); - $display = $view->get('display'); - $this->assertEqual($display[$id]['display_title'], 'Page 3'); - - // Ensure the 'default' display always has position zero, regardless of when - // it was set relative to other displays. Even if the 'default' display - // exists, adding it again will overwrite it, which is asserted with the new - // title. - $view->addDisplay('default', $random_title); - $displays = $view->get('display'); - $this->assertEqual($displays['default']['display_title'], $random_title, 'Default display is defined with the new title'); - $this->assertEqual($displays['default']['position'], 0, 'Default displays are always in position zero'); - - // Tests Drupal\views\Entity\View::generateDisplayId(). Since - // generateDisplayId() is protected, we have to use reflection to unit-test - // it. - $view = $this->controller->create(array()); - $ref_generate_display_id = new \ReflectionMethod($view, 'generateDisplayId'); - $ref_generate_display_id->setAccessible(TRUE); - $this->assertEqual( - $ref_generate_display_id->invoke($view, 'default'), - 'default', - 'The plugin ID for default is always default.' - ); - $this->assertEqual( - $ref_generate_display_id->invoke($view, 'feed'), - 'feed_1', - 'The generated ID for the first instance of a plugin type should have an suffix of _1.' - ); - $view->addDisplay('feed', 'feed title'); - $this->assertEqual( - $ref_generate_display_id->invoke($view, 'feed'), - 'feed_2', - 'The generated ID for the first instance of a plugin type should have an suffix of _2.' - ); - - // Tests item related methods(). - $view = $this->controller->create(array('base_table' => 'views_test_data')); - $view->addDisplay('default'); - $view = $view->getExecutable(); - - $display_id = 'default'; - $expected_items = array(); - // Tests addHandler with getItem. - // Therefore add one item without any options and one item with some - // options. - $id1 = $view->addHandler($display_id, 'field', 'views_test_data', 'id'); - $item1 = $view->getHandler($display_id, 'field', 'id'); - $expected_items[$id1] = $expected_item = array( - 'id' => 'id', - 'table' => 'views_test_data', - 'field' => 'id', - 'plugin_id' => 'numeric', - ); - $this->assertEqual($item1, $expected_item); - - $options = array( - 'alter' => array( - 'text' => $this->randomMachineName() - ) - ); - $id2 = $view->addHandler($display_id, 'field', 'views_test_data', 'name', $options); - $item2 = $view->getHandler($display_id, 'field', 'name'); - $expected_items[$id2] = $expected_item = array( - 'id' => 'name', - 'table' => 'views_test_data', - 'field' => 'name', - 'plugin_id' => 'standard', - ) + $options; - $this->assertEqual($item2, $expected_item); - - // Tests the expected fields from the previous additions. - $this->assertEqual($view->getHandlers('field', $display_id), $expected_items); - - // Alter an existing item via setItem and check the result via getItem - // and getItems. - $item = array( - 'alter' => array( - 'text' => $this->randomMachineName(), - ) - ) + $item1; - $expected_items[$id1] = $item; - $view->setHandler($display_id, 'field', $id1, $item); - $this->assertEqual($view->getHandler($display_id, 'field', 'id'), $item); - $this->assertEqual($view->getHandlers('field', $display_id), $expected_items); - - // Test removeItem method. - unset($expected_items[$id2]); - $view->removeHandler($display_id, 'field', $id2); - $this->assertEqual($view->getHandlers('field', $display_id), $expected_items); - } - - /** - * Tests the createDuplicate() View method. - */ - public function testCreateDuplicate() { - $view = Views::getView('test_view_storage'); - $copy = $view->storage->createDuplicate(); - - $this->assertTrue($copy instanceof View, 'The copied object is a View.'); - - // Check that the original view and the copy have different UUIDs. - $this->assertNotIdentical($view->storage->uuid(), $copy->uuid(), 'The copied view has a new UUID.'); - - // Check the 'name' (ID) is using the View objects default value (NULL) as it - // gets unset. - $this->assertIdentical($copy->id(), NULL, 'The ID has been reset.'); - - // Check the other properties. - // @todo Create a reusable property on the base test class for these? - $config_properties = array( - 'disabled', - 'description', - 'tag', - 'base_table', - 'label', - 'core', - ); - - foreach ($config_properties as $property) { - $this->assertIdentical($view->storage->get($property), $copy->get($property), format_string('@property property is identical.', array('@property' => $property))); - } - - // Check the displays are the same. - $copy_display = $copy->get('display'); - foreach ($view->storage->get('display') as $id => $display) { - // assertIdentical will not work here. - $this->assertEqual($display, $copy_display[$id], format_string('The @display display has been copied correctly.', array('@display' => $id))); - } - } - -} diff --git a/core/modules/views/src/Tests/ViewTestBase.php b/core/modules/views/src/Tests/ViewTestBase.php index ae67dcf..c7cce87 100644 --- a/core/modules/views/src/Tests/ViewTestBase.php +++ b/core/modules/views/src/Tests/ViewTestBase.php @@ -15,10 +15,10 @@ * Defines a base class for Views testing in the full web test environment. * * Use this base test class if you need to emulate a full Drupal installation. - * When possible, ViewKernelTestBase should be used instead. Both base classes + * When possible, ViewsKernelTestBase should be used instead. Both base classes * include the same methods. * - * @see \Drupal\views\Tests\ViewKernelTestBase + * @see \Drupal\Tests\views\Kernel\ViewsKernelTestBase * @see \Drupal\simpletest\WebTestBase */ abstract class ViewTestBase extends WebTestBase { diff --git a/core/modules/views/src/Tests/ViewTestData.php b/core/modules/views/src/Tests/ViewTestData.php index 536470c..b214d6e 100644 --- a/core/modules/views/src/Tests/ViewTestData.php +++ b/core/modules/views/src/Tests/ViewTestData.php @@ -14,7 +14,7 @@ * * The methods will be used by both views test base classes. * - * @see \Drupal\views\Tests\ViewKernelTestBase. + * @see \Drupal\Tests\views\Kernel\ViewsKernelTestBase. * @see \Drupal\views\Tests\ViewTestBase. */ class ViewTestData { diff --git a/core/modules/views/src/Tests/ViewsFormMultipleTest.php b/core/modules/views/src/Tests/ViewsFormMultipleTest.php new file mode 100644 index 0000000..2baa451 --- /dev/null +++ b/core/modules/views/src/Tests/ViewsFormMultipleTest.php @@ -0,0 +1,65 @@ +enableViewsTestModule(); + } + + /** + * {@inheritdoc} + */ + protected function viewsData() { + $data = parent::viewsData(); + $data['views_test_data']['field_form_button_test']['field'] = [ + 'title' => t('Button test'), + 'help' => t('Adds a test form button.'), + 'id' => 'field_form_button_test', + ]; + return $data; + } + + + /** + * Tests the a page with multiple View forms in it. + */ + public function testViewsFormMultiple() { + // Get the test page. + $this->drupalGet('views_test_form_multiple'); + + $this->assertText('Test base form ID with Views forms and arguments.'); + + // Submit the forms, validate argument returned in message set by handler. + // @note There is not a way to specify a specific index for a submit button. So + // the row index returned is always the last occurrence. + $this->drupalPostForm(NULL, [], t('Test Button'), [], [], 'views-form-test-form-multiple-default-arg2'); + $this->assertText('The test button at row 4 for test_form_multiple (default) View with args: arg2 was submitted.'); + $this->drupalPostForm(NULL, [], t('Test Button'), [], [], 'views-form-test-form-multiple-default-arg1'); + $this->assertText('The test button at row 4 for test_form_multiple (default) View with args: arg1 was submitted.'); + } + +} diff --git a/core/modules/views/src/Tests/ViewsHooksTest.php b/core/modules/views/src/Tests/ViewsHooksTest.php deleted file mode 100644 index 714a3ec..0000000 --- a/core/modules/views/src/Tests/ViewsHooksTest.php +++ /dev/null @@ -1,122 +0,0 @@ - 'all', - 'views_data_alter' => 'alter', - 'views_query_substitutions' => 'view', - 'views_form_substitutions' => 'view', - 'views_analyze' => 'view', - 'views_pre_view' => 'view', - 'views_pre_build' => 'view', - 'views_post_build' => 'view', - 'views_pre_execute' => 'view', - 'views_post_execute' => 'view', - 'views_pre_render' => 'view', - 'views_post_render' => 'view', - 'views_query_alter' => 'view', - 'views_invalidate_cache' => 'all', - ); - - /** - * The module handler to use for invoking hooks. - * - * @var \Drupal\Core\Extension\ModuleHandlerInterface - */ - protected $moduleHandler; - - protected function setUp() { - parent::setUp(); - - $this->moduleHandler = $this->container->get('module_handler'); - } - - /** - * Tests the hooks. - */ - public function testHooks() { - $view = Views::getView('test_view'); - $view->setDisplay(); - - // Test each hook is found in the implementations array and is invoked. - foreach (static::$hooks as $hook => $type) { - $this->assertTrue($this->moduleHandler->implementsHook('views_test_data', $hook), format_string('The hook @hook was registered.', array('@hook' => $hook))); - - if ($hook == 'views_post_render') { - $this->moduleHandler->invoke('views_test_data', $hook, array($view, &$view->display_handler->output, $view->display_handler->getPlugin('cache'))); - continue; - } - - switch ($type) { - case 'view': - $this->moduleHandler->invoke('views_test_data', $hook, array($view)); - break; - - case 'alter': - $data = array(); - $this->moduleHandler->invoke('views_test_data', $hook, array($data)); - break; - - default: - $this->moduleHandler->invoke('views_test_data', $hook); - } - - $this->assertTrue($this->container->get('state')->get('views_hook_test_' . $hook), format_string('The %hook hook was invoked.', array('%hook' => $hook))); - // Reset the module implementations cache, so we ensure that the - // .views.inc file is loaded actively. - $this->moduleHandler->resetImplementations(); - } - } - - /** - * Tests how hook_views_form_substitutions() makes substitutions. - * - * @see views_test_data_views_form_substitutions() - * @see views_pre_render_views_form_views_form() - */ - public function testViewsPreRenderViewsFormViewsForm() { - $element = [ - 'output' => [ - '#plain_text' => '', - ], - '#substitutions' => ['#value' => []], - ]; - $element = \Drupal::service('renderer')->executeInRenderContext(new RenderContext(), function() use ($element) { - return views_pre_render_views_form_views_form($element); - }); - $this->setRawContent((string) $element['output']['#markup']); - $this->assertEscaped('escaped'); - $this->assertRaw('unescaped'); - } - -} diff --git a/core/modules/views/src/Tests/Wizard/BasicTest.php b/core/modules/views/src/Tests/Wizard/BasicTest.php index b87bca6..7b532fd 100644 --- a/core/modules/views/src/Tests/Wizard/BasicTest.php +++ b/core/modules/views/src/Tests/Wizard/BasicTest.php @@ -212,7 +212,7 @@ public function testWizardDefaultValues() { // Make sure the plugin types that should not have empty options don't have. // Test against all values is unit tested. - // @see \Drupal\views\Tests\Plugin\DisplayKernelTest + // @see \Drupal\Tests\views\Kernel\Plugin\DisplayKernelTest $view = Views::getView($random_id); $displays = $view->storage->get('display'); diff --git a/core/modules/views/src/Tests/Wizard/TaggedWithTest.php b/core/modules/views/src/Tests/Wizard/TaggedWithTest.php index a1d4df8..0533fd6 100644 --- a/core/modules/views/src/Tests/Wizard/TaggedWithTest.php +++ b/core/modules/views/src/Tests/Wizard/TaggedWithTest.php @@ -8,7 +8,9 @@ namespace Drupal\views\Tests\Wizard; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\field\Entity\FieldConfig; use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait; +use Drupal\taxonomy\Entity\Vocabulary; /** * Tests the ability of the views wizard to create views filtered by taxonomy. @@ -77,10 +79,10 @@ protected function setUp() { $this->nodeTypeWithoutTags = $this->drupalCreateContentType(); // Create the vocabulary for the tag field. - $this->tagVocabulary = entity_create('taxonomy_vocabulary', array( + $this->tagVocabulary = Vocabulary::create([ 'name' => 'Views testing tags', 'vid' => 'views_testing_tags', - )); + ]); $this->tagVocabulary->save(); // Create the tag field itself. @@ -201,7 +203,7 @@ function testTaggedWithByNodeType() { // If we add an instance of the tagging field to the second node type, the // "tagged with" form element should not appear for it too. - entity_create('field_config', array( + FieldConfig::create([ 'field_name' => $this->tagFieldName, 'entity_type' => 'node', 'bundle' => $this->nodeTypeWithoutTags->id(), @@ -214,7 +216,7 @@ function testTaggedWithByNodeType() { 'auto_create' => TRUE, ), ), - ))->save(); + ])->save(); entity_get_form_display('node', $this->nodeTypeWithoutTags->id(), 'default') ->setComponent($this->tagFieldName, array( 'type' => 'entity_reference_autocomplete_tags', diff --git a/core/modules/views/src/Tests/Wizard/WizardPluginBaseKernelTest.php b/core/modules/views/src/Tests/Wizard/WizardPluginBaseKernelTest.php deleted file mode 100644 index 009536e..0000000 --- a/core/modules/views/src/Tests/Wizard/WizardPluginBaseKernelTest.php +++ /dev/null @@ -1,79 +0,0 @@ -installConfig(array('language')); - - $this->wizard = $this->container->get('plugin.manager.views.wizard')->createInstance('standard:views_test_data', array()); - } - - /** - * Tests the creating of a view. - * - * @see \Drupal\views\Plugin\views\wizard\WizardPluginBase - */ - public function testCreateView() { - $form = array(); - $form_state = new FormState(); - $form = $this->wizard->buildForm($form, $form_state); - $random_id = strtolower($this->randomMachineName()); - $random_label = $this->randomMachineName(); - $random_description = $this->randomMachineName(); - - // Add a new language and mark it as default. - ConfigurableLanguage::createFromLangcode('it')->save(); - $this->config('system.site')->set('default_langcode', 'it')->save(); - - $form_state->setValues([ - 'id' => $random_id, - 'label' => $random_label, - 'description' => $random_description, - 'base_table' => 'views_test_data', - ]); - - $this->wizard->validateView($form, $form_state); - $view = $this->wizard->createView($form, $form_state); - $this->assertTrue($view instanceof ViewUI, 'The created view is a ViewUI object.'); - $this->assertEqual($view->get('id'), $random_id); - $this->assertEqual($view->get('label'), $random_label); - $this->assertEqual($view->get('description'), $random_description); - $this->assertEqual($view->get('base_table'), 'views_test_data'); - $this->assertEqual($view->get('langcode'), 'it'); - } -} - diff --git a/core/modules/views/src/ViewExecutable.php b/core/modules/views/src/ViewExecutable.php index 4ad44fe..716bf76 100644 --- a/core/modules/views/src/ViewExecutable.php +++ b/core/modules/views/src/ViewExecutable.php @@ -1896,10 +1896,6 @@ public function getUrl($args = NULL, $display_id = NULL) { return $this->override_url; } - if (!isset($path)) { - $path = $this->getPath(); - } - $display_handler = $this->displayHandlers->get($display_id ?: $this->current_display)->getRoutedDisplay(); if (!$display_handler instanceof DisplayRouterInterface) { throw new \InvalidArgumentException('You cannot create a URL to a display without routes.'); @@ -1919,6 +1915,9 @@ public function getUrl($args = NULL, $display_id = NULL) { } } } + + $path = $this->getPath(); + // Don't bother working if there's nothing to do: if (empty($path) || (empty($args) && strpos($path, '%') === FALSE)) { return $display_handler->getUrlInfo(); @@ -2460,24 +2459,28 @@ public function serialize() { public function unserialize($serialized) { list($storage, $current_display, $args, $current_page, $exposed_input, $exposed_raw_input, $exposed_data, $dom_id, $executed) = unserialize($serialized); - $this->setRequest(\Drupal::request()); - $this->user = \Drupal::currentUser(); + // There are cases, like in testing, where we don't have a container + // available. + if (\Drupal::hasContainer()) { + $this->setRequest(\Drupal::request()); + $this->user = \Drupal::currentUser(); - $this->storage = \Drupal::entityManager()->getStorage('view')->load($storage); + $this->storage = \Drupal::entityManager()->getStorage('view')->load($storage); - $this->setDisplay($current_display); - $this->setArguments($args); - $this->setCurrentPage($current_page); - $this->setExposedInput($exposed_input); - $this->exposed_data = $exposed_data; - $this->exposed_raw_input = $exposed_raw_input; - $this->dom_id = $dom_id; + $this->setDisplay($current_display); + $this->setArguments($args); + $this->setCurrentPage($current_page); + $this->setExposedInput($exposed_input); + $this->exposed_data = $exposed_data; + $this->exposed_raw_input = $exposed_raw_input; + $this->dom_id = $dom_id; - $this->initHandlers(); + $this->initHandlers(); - // If the display was previously executed, execute it now. - if ($executed) { - $this->execute($this->current_display); + // If the display was previously executed, execute it now. + if ($executed) { + $this->execute($this->current_display); + } } } diff --git a/core/modules/views/tests/fixtures/update/argument-placeholder.php b/core/modules/views/tests/fixtures/update/argument-placeholder.php index 9b7a6d9..525299c 100644 --- a/core/modules/views/tests/fixtures/update/argument-placeholder.php +++ b/core/modules/views/tests/fixtures/update/argument-placeholder.php @@ -1,5 +1,10 @@ insert('config') diff --git a/core/modules/views/tests/fixtures/update/duplicate-field-handler.php b/core/modules/views/tests/fixtures/update/duplicate-field-handler.php index 2b78f12..69aa8e8 100644 --- a/core/modules/views/tests/fixtures/update/duplicate-field-handler.php +++ b/core/modules/views/tests/fixtures/update/duplicate-field-handler.php @@ -1,5 +1,10 @@ insert('config') diff --git a/core/modules/views/tests/modules/views_entity_test/views_entity_test.module b/core/modules/views/tests/modules/views_entity_test/views_entity_test.module index f71a65b..0506710 100644 --- a/core/modules/views/tests/modules/views_entity_test/views_entity_test.module +++ b/core/modules/views/tests/modules/views_entity_test/views_entity_test.module @@ -4,6 +4,7 @@ * @file * Contains main module functionality. */ + use Drupal\Core\Access\AccessResult; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Field\BaseFieldDefinition; @@ -38,7 +39,7 @@ function views_entity_test_entity_field_access($operation, FieldDefinitionInterf if ($field_definition->getName() == 'test_text_access') { if ($items) { if ($items->value == 'no access value') { - return AccessResult::forbidden()->cacheUntilEntityChanges($items->getEntity()); + return AccessResult::forbidden()->addCacheableDependency($items->getEntity()); } } } diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_type_filter.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_type_filter.yml index 745140b..e4d1b93 100644 --- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_type_filter.yml +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_type_filter.yml @@ -34,7 +34,7 @@ display: relationship: none group_type: group admin_label: '' - label: 'Content Type' + label: 'Content type' exclude: false alter: alter_text: false diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_entity_test_rendered.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_entity_test_rendered.yml new file mode 100644 index 0000000..541ee96 --- /dev/null +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_entity_test_rendered.yml @@ -0,0 +1,161 @@ +langcode: en +status: true +dependencies: + module: + - entity_test + - user +id: test_field_entity_test_rendered +label: 'Test Rendered entity test' +module: views +description: '' +tag: '' +base_table: entity_test +base_field: id +core: 8.x +display: + default: + display_plugin: default + id: default + display_title: Master + position: 0 + display_options: + access: + type: none + options: { } + cache: + type: none + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + replica: false + query_comment: '' + query_tags: { } + exposed_form: + type: basic + options: + submit_button: Apply + reset_button: false + reset_button_label: Reset + exposed_sorts_label: 'Sort by' + expose_sort_order: true + sort_asc_label: Asc + sort_desc_label: Desc + pager: + type: full + options: + items_per_page: 10 + offset: 0 + id: 0 + total_pages: null + expose: + items_per_page: false + items_per_page_label: 'Items per page' + items_per_page_options: '5, 10, 25, 50' + items_per_page_options_all: false + items_per_page_options_all_label: '- All -' + offset: false + offset_label: Offset + tags: + previous: '‹ Previous' + next: 'Next ›' + first: '« First' + last: 'Last »' + quantity: 9 + style: + type: default + options: + grouping: { } + row_class: '' + default_row_class: true + uses_fields: false + row: + type: fields + options: + inline: { } + separator: '' + hide_empty: false + default_field_elements: true + fields: + rendered_entity: + id: rendered_entity + table: entity_test + field: rendered_entity + relationship: none + group_type: group + admin_label: '' + label: '' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: false + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + view_mode: foobar + entity_type: entity_test + plugin_id: rendered_entity + filters: { } + sorts: + id: + id: id + table: entity_test + field: id + relationship: none + group_type: group + admin_label: '' + order: ASC + exposed: false + expose: + label: '' + entity_type: entity_test + entity_field: nid + plugin_id: standard + header: { } + footer: { } + empty: { } + relationships: { } + arguments: { } + display_extenders: { } + cache_metadata: + max-age: 0 + contexts: + - 'languages:language_interface' + - url.query_args + - user.permissions + tags: { } diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml new file mode 100644 index 0000000..138517c --- /dev/null +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml @@ -0,0 +1,130 @@ +langcode: en +status: true +dependencies: + module: + - node +id: test_form_multiple +label: '' +module: views +description: '' +tag: '' +base_table: views_test_data +base_field: nid +core: 8.x +display: + default: + display_plugin: default + display_title: Master + id: default + position: 0 + display_options: + access: + type: none + cache: + type: tag + options: { } + query: + type: views_query + options: + disable_sql_rewrite: false + distinct: false + replica: false + query_comment: '' + query_tags: { } + pager: + options: + id: 0 + items_per_page: 10 + offset: 0 + type: full + style: + type: table + options: + grouping: { } + row_class: '' + default_row_class: true + override: true + sticky: false + caption: '' + summary: '' + description: '' + columns: + id: id + field_form_button_test: field_form_button_test + info: + field_form_button_test: + sortable: false + default_sort_order: asc + align: '' + separator: '' + empty_column: false + responsive: '' + default: '-1' + empty_table: false + row: + type: fields + fields: + id: + field: id + id: id + relationship: none + table: views_test_data + plugin_id: numeric + field_form_button_test: + id: field_form_button_test + table: views_test_data + field: field_form_button_test + relationship: none + group_type: group + admin_label: '' + label: 'Field form button test' + exclude: false + alter: + alter_text: false + text: '' + make_link: false + path: '' + absolute: false + external: false + replace_spaces: false + path_case: none + trim_whitespace: false + alt: '' + rel: '' + link_class: '' + prefix: '' + suffix: '' + target: '' + nl2br: false + max_length: 0 + word_boundary: true + ellipsis: true + more_link: false + more_link_text: '' + more_link_path: '' + strip_tags: false + trim: false + preserve_tags: '' + html: false + element_type: '' + element_class: '' + element_label_type: '' + element_label_class: '' + element_label_colon: true + element_wrapper_type: '' + element_wrapper_class: '' + element_default_classes: true + empty: '' + hide_empty: false + empty_zero: false + hide_alter_empty: true + plugin_id: field_form_button_test + arguments: + 'null': + default_action: default + default_argument_type: fixed + field: 'null' + id: 'null' + must_not_be: false + table: views + plugin_id: 'null' diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_summary.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_summary.yml index daf92fa..3784bd9 100644 --- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_summary.yml +++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_summary.yml @@ -75,6 +75,7 @@ display: specify_validation: true plugin_id: string entity_type: entity_test + admin_label: type fields: id: id: id diff --git a/core/modules/views/tests/modules/views_test_data/src/Controller/ViewsTestFormMultipleController.php b/core/modules/views/tests/modules/views_test_data/src/Controller/ViewsTestFormMultipleController.php new file mode 100644 index 0000000..85cb0ad --- /dev/null +++ b/core/modules/views/tests/modules/views_test_data/src/Controller/ViewsTestFormMultipleController.php @@ -0,0 +1,44 @@ + [ + '#prefix' => '
                              ', + '#suffix' => '
                              ', + '#type' => 'view', + '#name' => 'test_form_multiple', + '#display_id' => 'default', + '#arguments' => ['arg1'], + '#embed' => TRUE, + ], + 'view_arg2' => [ + '#prefix' => '
                              ', + '#suffix' => '
                              ', + '#type' => 'view', + '#name' => 'test_form_multiple', + '#display_id' => 'default', + '#arguments' => ['arg2'], + '#embed' => TRUE, + ], + ]; + return $build; + } + +} diff --git a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/field/FieldFormButtonTest.php b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/field/FieldFormButtonTest.php new file mode 100644 index 0000000..95a4a98 --- /dev/null +++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/field/FieldFormButtonTest.php @@ -0,0 +1,92 @@ +options['id'] . '--' . $row->index . '-->'; + } + + /** + * Form constructor for the views form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function viewsForm(&$form, FormStateInterface $form_state) { + // Make sure we do not accidentally cache this form. + $form['#cache']['max-age'] = 0; + // The view is empty, abort. + if (empty($this->view->result)) { + unset($form['actions']); + return; + } + + $form[$this->options['id']]['#tree'] = TRUE; + foreach ($this->view->result as $row_index => $row) { + $form[$this->options['id']][$row_index] = [ + '#type' => 'submit', + '#value' => t('Test Button'), + '#name' => 'test-button-' . $row_index, + '#test_button' => TRUE, + '#row_index' => $row_index, + '#attributes' => ['class' => ['test-button']], + ]; + } + } + + /** + * Submit handler for the views form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + */ + public function viewsFormSubmit(&$form, FormStateInterface $form_state) { + $triggering_element = $form_state->getTriggeringElement(); + if (!empty($triggering_element['#test_button'])) { + $row_index = $triggering_element['#row_index']; + $view_args = !empty($this->view->args) ? implode(', ', $this->view->args) : $this->t('no arguments'); + drupal_set_message($this->t('The test button at row @row_index for @view_id (@display) View with args: @args was submitted.', [ + '@display' => $this->view->current_display, + '@view_id' => $this->view->id(), + '@args' => $view_args, + '@row_index' => $row_index, + ])); + } + } + + /** + * {@inheritdoc} + */ + public function query() { + // Do nothing. + } + +} diff --git a/core/modules/views/tests/modules/views_test_data/views_test_data.module b/core/modules/views/tests/modules/views_test_data/views_test_data.module index 5853fd5..f041651d 100644 --- a/core/modules/views/tests/modules/views_test_data/views_test_data.module +++ b/core/modules/views/tests/modules/views_test_data/views_test_data.module @@ -122,3 +122,10 @@ function views_test_data_test_pre_render_function($element) { $element['#markup'] = 'views_test_data_test_pre_render_function executed'; return $element; } + +/** + * Implements hook_form_BASE_FORM_ID_alter(). + */ +function views_test_data_form_views_form_test_form_multiple_default_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) { + drupal_set_message(t('Test base form ID with Views forms and arguments.')); +} diff --git a/core/modules/views/tests/modules/views_test_data/views_test_data.routing.yml b/core/modules/views/tests/modules/views_test_data/views_test_data.routing.yml index 63d8ac8..cb0ad63 100644 --- a/core/modules/views/tests/modules/views_test_data/views_test_data.routing.yml +++ b/core/modules/views/tests/modules/views_test_data/views_test_data.routing.yml @@ -11,3 +11,11 @@ views_test_data.element_embed: _form: '\Drupal\views_test_data\Form\ViewsTestDataElementEmbedForm' requirements: _access: 'TRUE' + +views_test_data.form_multiple: + path: '/views_test_form_multiple' + defaults: + _title: 'Test Views Form Multiple' + _controller: '\Drupal\views_test_data\Controller\ViewsTestFormMultipleController::testPage' + requirements: + _access: 'TRUE' diff --git a/core/modules/views/tests/src/Kernel/BasicTest.php b/core/modules/views/tests/src/Kernel/BasicTest.php new file mode 100644 index 0000000..9d006d0 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/BasicTest.php @@ -0,0 +1,142 @@ +setDisplay(); + + // Execute the view. + $this->executeView($view); + + // Verify the result. + $this->assertEqual(5, count($view->result), 'The number of returned rows match.'); + $this->assertIdenticalResultset($view, $this->dataSet(), array( + 'views_test_data_name' => 'name', + 'views_test_data_age' => 'age', + )); + } + + /** + * Tests filtering of the result set. + */ + public function testSimpleFiltering() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Add a filter. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'age' => array( + 'operator' => '<', + 'value' => array( + 'value' => '28', + 'min' => '', + 'max' => '', + ), + 'group' => '0', + 'exposed' => FALSE, + 'expose' => array( + 'operator' => FALSE, + 'label' => '', + ), + 'id' => 'age', + 'table' => 'views_test_data', + 'field' => 'age', + 'relationship' => 'none', + ), + )); + + // Execute the view. + $this->executeView($view); + + // Build the expected result. + $dataset = array( + array( + 'id' => 1, + 'name' => 'John', + 'age' => 25, + ), + array( + 'id' => 2, + 'name' => 'George', + 'age' => 27, + ), + array( + 'id' => 4, + 'name' => 'Paul', + 'age' => 26, + ), + ); + + // Verify the result. + $this->assertEqual(3, count($view->result), 'The number of returned rows match.'); + $this->assertIdenticalResultSet($view, $dataset, array( + 'views_test_data_name' => 'name', + 'views_test_data_age' => 'age', + )); + } + + /** + * Tests simple argument. + */ + public function testSimpleArgument() { + // Execute with a view + $view = Views::getView('test_simple_argument'); + $view->setArguments(array(27)); + $this->executeView($view); + + // Build the expected result. + $dataset = array( + array( + 'id' => 2, + 'name' => 'George', + 'age' => 27, + ), + ); + + // Verify the result. + $this->assertEqual(1, count($view->result), 'The number of returned rows match.'); + $this->assertIdenticalResultSet($view, $dataset, array( + 'views_test_data_name' => 'name', + 'views_test_data_age' => 'age', + )); + + // Test "show all" if no argument is present. + $view = Views::getView('test_simple_argument'); + $this->executeView($view); + + // Build the expected result. + $dataset = $this->dataSet(); + + $this->assertEqual(5, count($view->result), 'The number of returned rows match.'); + $this->assertIdenticalResultSet($view, $dataset, array( + 'views_test_data_name' => 'name', + 'views_test_data_age' => 'age', + )); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Entity/RowEntityRenderersTest.php b/core/modules/views/tests/src/Kernel/Entity/RowEntityRenderersTest.php new file mode 100644 index 0000000..120fae2 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Entity/RowEntityRenderersTest.php @@ -0,0 +1,238 @@ +installEntitySchema('node'); + $this->installEntitySchema('user'); + $this->installSchema('node', array('node_access')); + $this->installConfig(array('node', 'language')); + + // The entity.node.canonical route must exist when nodes are rendered. + $this->container->get('router.builder')->rebuild(); + + $this->langcodes = array(\Drupal::languageManager()->getDefaultLanguage()->getId()); + for ($i = 0; $i < 2; $i++) { + $langcode = 'l' . $i; + $this->langcodes[] = $langcode; + ConfigurableLanguage::createFromLangcode($langcode)->save(); + } + + // Make sure we do not try to render non-existing user data. + $node_type = NodeType::create(array('type' => 'test')); + $node_type->setDisplaySubmitted(FALSE); + $node_type->save(); + + $this->values = array(); + $controller = \Drupal::entityManager()->getStorage('node'); + $langcode_index = 0; + + for ($i = 0; $i < count($this->langcodes); $i++) { + // Create a node with a different default language each time. + $default_langcode = $this->langcodes[$langcode_index++]; + $node = $controller->create(array('type' => 'test', 'uid' => 0, 'langcode' => $default_langcode)); + // Ensure the default language is processed first. + $langcodes = array_merge(array($default_langcode), array_diff($this->langcodes, array($default_langcode))); + + foreach ($langcodes as $langcode) { + // Ensure we have a predictable result order. + $this->values[$i][$langcode] = $i . '-' . $langcode . '-' . $this->randomMachineName(); + + if ($langcode != $default_langcode) { + $node->addTranslation($langcode, array('title' => $this->values[$i][$langcode])); + } + else { + $node->setTitle($this->values[$i][$langcode]); + } + + $node->save(); + } + } + } + + /** + * Tests the entity row renderers. + */ + public function testEntityRenderers() { + $this->checkLanguageRenderers('page_1', $this->values); + } + + /** + * Tests the field row renderers. + */ + public function testFieldRenderers() { + $this->checkLanguageRenderers('page_2', $this->values); + } + + /** + * Checks that the language renderer configurations work as expected. + * + * @param string $display + * Name of display to test with. + * @param array $values + * An array of node information which are each an array of node titles + * associated with language keys appropriate for the translation of that + * node. + */ + protected function checkLanguageRenderers($display, $values) { + $expected = array( + $values[0]['en'], + $values[0]['en'], + $values[0]['en'], + $values[1]['en'], + $values[1]['en'], + $values[1]['en'], + $values[2]['en'], + $values[2]['en'], + $values[2]['en'], + ); + $this->assertTranslations($display, '***LANGUAGE_language_content***', $expected, 'The current language renderer behaves as expected.'); + + $expected = array( + $values[0]['en'], + $values[0]['en'], + $values[0]['en'], + $values[1]['l0'], + $values[1]['l0'], + $values[1]['l0'], + $values[2]['l1'], + $values[2]['l1'], + $values[2]['l1'], + ); + $this->assertTranslations($display, '***LANGUAGE_entity_default***', $expected, 'The default language renderer behaves as expected.'); + + $expected = array( + $values[0]['en'], + $values[0]['l0'], + $values[0]['l1'], + $values[1]['en'], + $values[1]['l0'], + $values[1]['l1'], + $values[2]['en'], + $values[2]['l0'], + $values[2]['l1'], + ); + $this->assertTranslations($display, '***LANGUAGE_entity_translation***', $expected, 'The translation language renderer behaves as expected.'); + + $expected = array( + $values[0][$this->langcodes[0]], + $values[0][$this->langcodes[0]], + $values[0][$this->langcodes[0]], + $values[1][$this->langcodes[0]], + $values[1][$this->langcodes[0]], + $values[1][$this->langcodes[0]], + $values[2][$this->langcodes[0]], + $values[2][$this->langcodes[0]], + $values[2][$this->langcodes[0]], + ); + $this->assertTranslations($display, '***LANGUAGE_site_default***', $expected, 'The site default language renderer behaves as expected.'); + + $expected = array( + $values[0]['l0'], + $values[0]['l0'], + $values[0]['l0'], + $values[1]['l0'], + $values[1]['l0'], + $values[1]['l0'], + $values[2]['l0'], + $values[2]['l0'], + $values[2]['l0'], + ); + $this->assertTranslations($display, 'l0', $expected, 'The language specific renderer behaves as expected.'); + } + + /** + * Checks that the view results match the expected values. + * + * @param string $display + * Name of display to test with. + * @param string $renderer_id + * The id of the renderer to be tested. + * @param array $expected + * An array of expected title translation values, one for each result row. + * @param string $message + * (optional) A message to display with the assertion. + * @param string $group + * (optional) The group this message is in. + * + * @return bool + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertTranslations($display, $renderer_id, array $expected, $message = '', $group = 'Other') { + $view = Views::getView('test_entity_row_renderers'); + $view->storage->invalidateCaches(); + $view->setDisplay($display); + $view->getDisplay()->setOption('rendering_language', $renderer_id); + $view->preview(); + + $result = FALSE; + foreach ($expected as $index => $expected_output) { + if (!empty($view->result[$index])) { + $build = $view->rowPlugin->render($view->result[$index]); + $output = \Drupal::service('renderer')->renderRoot($build); + $result = strpos($output, $expected_output) !== FALSE; + if (!$result) { + break; + } + } + else { + $result = FALSE; + break; + } + } + + return $this->assertTrue($result, $message, $group); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Entity/ViewEntityDependenciesTest.php b/core/modules/views/tests/src/Kernel/Entity/ViewEntityDependenciesTest.php new file mode 100644 index 0000000..84e6e33 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Entity/ViewEntityDependenciesTest.php @@ -0,0 +1,183 @@ +installEntitySchema('node'); + $this->installConfig(array('field', 'node')); + + $comment_type = CommentType::create(array( + 'id' => 'comment', + 'label' => 'Comment settings', + 'description' => 'Comment settings', + 'target_entity_type_id' => 'node', + )); + $comment_type->save(); + + $content_type = NodeType::create([ + 'type' => $this->randomMachineName(), + 'name' => $this->randomString(), + ]); + $content_type->save(); + $field_storage = FieldStorageConfig::create(array( + 'field_name' => Unicode::strtolower($this->randomMachineName()), + 'entity_type' => 'node', + 'type' => 'comment', + )); + $field_storage->save(); + FieldConfig::create([ + 'field_storage' => $field_storage, + 'bundle' => $content_type->id(), + 'label' => $this->randomMachineName() . '_label', + 'description' => $this->randomMachineName() . '_description', + 'settings' => array( + 'comment_type' => $comment_type->id(), + ), + ])->save(); + FieldConfig::create([ + 'field_storage' => FieldStorageConfig::loadByName('node', 'body'), + 'bundle' => $content_type->id(), + 'label' => $this->randomMachineName() . '_body', + 'settings' => array('display_summary' => TRUE), + ])->save(); + + ViewTestData::createTestViews(get_class($this), array('views_test_config')); + } + + /** + * Tests the getDependencies method. + */ + public function testGetDependencies() { + $expected = []; + $expected['test_field_get_entity'] = [ + 'module' => [ + 'comment', + 'node', + 'user', + ] + ]; + // Tests dependencies of relationships. + $expected['test_relationship_dependency'] = [ + 'module' => [ + 'comment', + 'node', + 'user', + ] + ]; + $expected['test_plugin_dependencies'] = [ + 'module' => [ + 'comment', + 'views_test_data', + ], + 'content' => [ + 'RowTest', + 'StaticTest', + 'StyleTest', + ] + ]; + + $expected['test_argument_dependency'] = [ + 'config' => [ + 'core.entity_view_mode.node.teaser', + 'field.storage.node.body' + ], + 'content' => [ + 'ArgumentDefaultTest', + 'ArgumentValidatorTest' + ], + 'module' => [ + 'node', + // The argument handler is provided by the search module. + 'search', + 'text', + 'user' + ], + ]; + + foreach ($this::$testViews as $view_id) { + $view = Views::getView($view_id); + + $dependencies = $view->getDependencies(); + $this->assertEqual($expected[$view_id], $dependencies); + $config = $this->config('views.view.' . $view_id); + \Drupal::service('config.storage.sync')->write($view_id, $config->get()); + } + + // Ensure that dependencies are calculated on the display level. + $expected_display['default'] = [ + 'config' => [ + 'core.entity_view_mode.node.teaser', + ], + 'content' => [ + 'ArgumentDefaultTest', + 'ArgumentValidatorTest' + ], + 'module' => [ + 'core', + 'node', + 'search', + 'user', + 'views' + ], + ]; + $expected_display['page'] = [ + 'config' => [ + 'field.storage.node.body' + ], + 'module' => [ + 'core', + 'text', + 'views' + ], + ]; + + $view = Views::getView('test_argument_dependency'); + $view->initDisplay(); + foreach ($view->displayHandlers as $display) { + // Calculate the dependencies each display has. + $this->assertEqual($expected_display[$display->getPluginId()], $display->calculateDependencies()); + } + } + +} diff --git a/core/modules/views/tests/src/Kernel/EventSubscriber/ViewsEntitySchemaSubscriberIntegrationTest.php b/core/modules/views/tests/src/Kernel/EventSubscriber/ViewsEntitySchemaSubscriberIntegrationTest.php new file mode 100644 index 0000000..5893ece --- /dev/null +++ b/core/modules/views/tests/src/Kernel/EventSubscriber/ViewsEntitySchemaSubscriberIntegrationTest.php @@ -0,0 +1,464 @@ +eventDispatcher = $this->container->get('event_dispatcher'); + $this->eventSubscriber = $this->container->get('views.entity_schema_subscriber'); + $this->entityDefinitionUpdateManager = $this->container->get('entity.definition_update_manager'); + $this->entityManager = $this->container->get('entity.manager'); + $this->state = $this->container->get('state'); + + $this->database = $this->container->get('database'); + + // Install every entity type's schema that wasn't installed in the parent + // method. + foreach (array_diff_key($this->entityManager->getDefinitions(), array_flip(array('user', 'entity_test'))) as $entity_type_id => $entity_type) { + $this->installEntitySchema($entity_type_id); + } + } + + /** + * Tests that views are disabled when an entity type is deleted. + */ + public function testDeleteEntityType() { + $entity_storage = $this->entityManager->getStorage('view'); + + $views = $entity_storage->loadMultiple(); + + // Ensure that all test views exists. + $this->assertTrue(isset($views['test_view_entity_test'])); + $this->assertTrue(isset($views['test_view_entity_test_revision'])); + $this->assertTrue(isset($views['test_view_entity_test_data'])); + $this->assertTrue(isset($views['test_view_entity_test_additional_base_field'])); + + $event = new EntityTypeEvent($this->entityManager->getDefinition('entity_test_update')); + $this->eventDispatcher->dispatch(EntityTypeEvents::DELETE, $event); + + // We expect that views which use 'entity_test_update' as base tables are + // disabled. + $views = $entity_storage->loadMultiple(); + + // Ensure that all test views still exists after the deletion of the + // entity type. + $this->assertTrue(isset($views['test_view_entity_test'])); + $this->assertTrue(isset($views['test_view_entity_test_revision'])); + $this->assertTrue(isset($views['test_view_entity_test_data'])); + $this->assertTrue(isset($views['test_view_entity_test_additional_base_field'])); + + // Ensure that they are all disabled. + $this->assertFalse($views['test_view_entity_test']->status()); + $this->assertFalse($views['test_view_entity_test_revision']->status()); + $this->assertFalse($views['test_view_entity_test_data']->status()); + $this->assertFalse($views['test_view_entity_test_additional_base_field']->status()); + } + + /** + * Tests that renaming base tables adapts the views. + */ + public function testBaseTableRename() { + $this->renameBaseTable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + /** @var \Drupal\views\Entity\View $view */ + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test'); + + // Ensure the base table got renamed, so also the views fields. + $this->assertEqual('entity_test_update_new', $view->get('base_table')); + $display = $view->getDisplay('default'); + $this->assertEqual('entity_test_update_new', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_new', $display['display_options']['fields']['name']['table']); + } + + /** + * Tests that renaming data tables adapts the views. + */ + public function testDataTableRename() { + $this->updateEntityTypeToTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test_data'); + $this->assertEqual('entity_test_update', $view->get('base_table')); + $display = $view->getDisplay('default'); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + // Ensure that the data table is used. + $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); + + $this->renameDataTable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + /** @var \Drupal\views\Entity\View $view */ + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test_data'); + + // Ensure the data table got renamed, so also the views fields. + $this->assertEqual('entity_test_update', $view->get('base_table')); + $display = $view->getDisplay('default'); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_data_new', $display['display_options']['fields']['name']['table']); + } + + /** + * Tests that renaming revision tables adapts the views. + */ + public function testRevisionBaseTableRename() { + $this->updateEntityTypeToRevisionable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + /** @var \Drupal\views\Entity\View $view */ + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test_revision'); + $this->assertEqual('entity_test_update_revision', $view->get('base_table')); + $display = $view->getDisplay('default'); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['name']['table']); + + $this->renameRevisionBaseTable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + /** @var \Drupal\views\Entity\View $view */ + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test_revision'); + + // Ensure the base table got renamed, so also the views fields. + $this->assertEqual('entity_test_update_revision_new', $view->get('base_table')); + $display = $view->getDisplay('default'); + $this->assertEqual('entity_test_update_revision_new', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_revision_new', $display['display_options']['fields']['name']['table']); + } + + /** + * Tests that renaming revision tables adapts the views. + */ + public function testRevisionDataTableRename() { + $this->updateEntityTypeToRevisionable(); + // Multiple changes, so we have to invalidate the caches, otherwise + // the second update will revert the first. + $this->entityManager->clearCachedDefinitions(); + $this->updateEntityTypeToTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + /** @var \Drupal\views\Entity\View $view */ + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test_revision'); + $this->assertEqual('entity_test_update_revision', $view->get('base_table')); + $display = $view->getDisplay('default'); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_revision_data', $display['display_options']['fields']['name']['table']); + + $this->renameRevisionDataTable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + /** @var \Drupal\views\Entity\View $view */ + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test_revision'); + + // Ensure the base table got renamed, so also the views fields. + $this->assertEqual('entity_test_update_revision', $view->get('base_table')); + $display = $view->getDisplay('default'); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_revision_data_new', $display['display_options']['fields']['name']['table']); + } + + /** + * Tests that adding data tables adapts the views. + */ + public function testDataTableAddition() { + $this->updateEntityTypeToTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + /** @var \Drupal\views\Entity\View $view */ + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test'); + + // Ensure the data table got renamed, so also the views fields. + $this->assertEqual('entity_test_update', $view->get('base_table')); + $display = $view->getDisplay('default'); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); + } + + /** + * Tests that enabling revisions doesn't do anything. + */ + public function testRevisionEnabling() { + $this->updateEntityTypeToRevisionable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + /** @var \Drupal\views\Entity\View $view */ + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test'); + + // Ensure that nothing happens. + $this->assertEqual('entity_test_update', $view->get('base_table')); + $display = $view->getDisplay('default'); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); + } + + /** + * Tests that removing revision support disables the view. + */ + public function testRevisionDisabling() { + $this->updateEntityTypeToRevisionable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + $this->updateEntityTypeToNotRevisionable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + /** @var \Drupal\views\Entity\View $view */ + $entity_storage = $this->entityManager->getStorage('view'); + $view = $entity_storage->load('test_view_entity_test_revision'); + + $this->assertFalse($view->status()); + } + + /** + * Tests a bunch possible entity definition table updates. + */ + public function testVariousTableUpdates() { + // We want to test the following permutations of entity definition updates: + // base <-> base + translation + // base + translation <-> base + translation + revision + // base + revision <-> base + translation + revision + // base <-> base + revision + // base <-> base + translation + revision + + // base <-> base + translation + $this->updateEntityTypeToTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(); + + $this->assertEqual('entity_test_update', $view->get('base_table')); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); + + $this->updateEntityTypeToNotTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(); + + $this->assertEqual('entity_test_update', $view->get('base_table')); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); + + $this->resetEntityType(); + + // base + translation <-> base + translation + revision + $this->updateEntityTypeToTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(); + + $this->assertEqual('entity_test_update', $view->get('base_table')); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); + + $this->updateEntityTypeToRevisionable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(); + + $this->assertEqual('entity_test_update', $view->get('base_table')); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); + + $this->updateEntityTypeToNotRevisionable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(); + + $this->assertEqual('entity_test_update', $view->get('base_table')); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); + + $this->resetEntityType(); + + // base + revision <-> base + translation + revision + $this->updateEntityTypeToRevisionable(); + list($view, $display) = $this->getUpdatedViewAndDisplay(); + + $this->assertEqual('entity_test_update', $view->get('base_table')); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); + + $this->updateEntityTypeToTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(); + + $this->assertEqual('entity_test_update', $view->get('base_table')); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); + + $this->updateEntityTypeToNotTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(); + + $this->assertEqual('entity_test_update', $view->get('base_table')); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); + + $this->resetEntityType(); + + // base <-> base + revision + $this->updateEntityTypeToRevisionable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(); + + $this->assertEqual('entity_test_update', $view->get('base_table')); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); + + $this->updateEntityTypeToNotRevisionable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(); + + $this->assertEqual('entity_test_update', $view->get('base_table')); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); + + $this->resetEntityType(); + + // base <-> base + translation + revision + $this->updateEntityTypeToRevisionable(); + $this->updateEntityTypeToTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(); + + $this->assertEqual('entity_test_update', $view->get('base_table')); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_data', $display['display_options']['fields']['name']['table']); + + $this->updateEntityTypeToNotRevisionable(); + $this->updateEntityTypeToNotTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(); + + $this->assertEqual('entity_test_update', $view->get('base_table')); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update', $display['display_options']['fields']['name']['table']); + } + + /** + * Tests some possible entity table updates for a revision view. + */ + public function testVariousTableUpdatesForRevisionView() { + // base + revision <-> base + translation + revision + $this->updateEntityTypeToRevisionable(); + // Multiple changes, so we have to invalidate the caches, otherwise + // the second update will revert the first. + $this->entityManager->clearCachedDefinitions(); + + list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE); + + $this->assertEqual('entity_test_update_revision', $view->get('base_table')); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['name']['table']); + + $this->updateEntityTypeToTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE); + + $this->assertEqual('entity_test_update_revision', $view->get('base_table')); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_revision_data', $display['display_options']['fields']['name']['table']); + + $this->updateEntityTypeToNotTranslatable(); + $this->entityDefinitionUpdateManager->applyUpdates(); + list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE); + + $this->assertEqual('entity_test_update_revision', $view->get('base_table')); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']); + $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['name']['table']); + + $this->resetEntityType(); + } + + /** + * Gets a view and its display. + * + * @param bool $revision + * (optional) TRUE if we want to get a revision view. + * + * @return array + * An array with the view as first item, and the display as second. + */ + protected function getUpdatedViewAndDisplay($revision = FALSE) { + $entity_storage = $this->entityManager->getStorage('view'); + /** @var \Drupal\views\Entity\View $view */ + $view = $entity_storage->load($revision ? 'test_view_entity_test_revision' : 'test_view_entity_test'); + $display = $view->getDisplay('default'); + + return [$view, $display]; + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/AreaEntityTest.php b/core/modules/views/tests/src/Kernel/Handler/AreaEntityTest.php new file mode 100644 index 0000000..3067d32 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/AreaEntityTest.php @@ -0,0 +1,202 @@ +installEntitySchema('user'); + $this->installEntitySchema('entity_test'); + $this->installConfig(['entity_test']); + + Block::create([ + 'id' => 'test_block', + 'plugin' => 'system_main_block', + ])->save(); + + parent::setUpFixtures(); + } + + /** + * Tests views data for entity area handlers. + */ + public function testEntityAreaData() { + $data = $this->container->get('views.views_data')->get('views'); + $entity_types = $this->container->get('entity.manager')->getDefinitions(); + + $expected_entities = array_filter($entity_types, function (EntityTypeInterface $entity_type) { + return $entity_type->hasViewBuilderClass(); + }); + + // Test that all expected entity types have data. + foreach (array_keys($expected_entities) as $entity) { + $this->assertTrue(!empty($data['entity_' . $entity]), format_string('Views entity area data found for @entity', array('@entity' => $entity))); + // Test that entity_type is set correctly in the area data. + $this->assertEqual($entity, $data['entity_' . $entity]['area']['entity_type'], format_string('Correct entity_type set for @entity', array('@entity' => $entity))); + } + + $expected_entities = array_filter($entity_types, function (EntityTypeInterface $type) { + return !$type->hasViewBuilderClass(); + }); + + // Test that no configuration entity types have data. + foreach (array_keys($expected_entities) as $entity) { + $this->assertTrue(empty($data['entity_' . $entity]), format_string('Views config entity area data not found for @entity', array('@entity' => $entity))); + } + } + + /** + * Tests the area handler. + */ + public function testEntityArea() { + /** @var \Drupal\Core\Entity\EntityInterface[] $entities */ + $entities = array(); + for ($i = 0; $i < 3; $i++) { + $random_label = $this->randomMachineName(); + $data = array('bundle' => 'entity_test', 'name' => $random_label); + $entity_test = $this->container->get('entity.manager') + ->getStorage('entity_test') + ->create($data); + + $uuid_map[0] = 'aa0c61cb-b7bb-4795-972a-493dabcf529c'; + $uuid_map[1] = '62cef0ff-6f30-4f7a-b9d6-a8ed5a3a6bf3'; + $uuid_map[2] = '3161d6e9-3326-4719-b513-8fa68a731ba2'; + $entity_test->uuid->value = $uuid_map[$i]; + + $entity_test->save(); + $entities[] = $entity_test; + \Drupal::state() + ->set('entity_test_entity_access.view.' . $entity_test->id(), $i != 2); + } + + $this->doTestCalculateDependencies(); + $this->doTestRender($entities); + } + + /** + * Tests rendering the entity area handler. + * + * @param \Drupal\Core\Entity\EntityInterface[] $entities + * The entities. + */ + public function doTestRender($entities) { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = $this->container->get('renderer'); + $view = Views::getView('test_entity_area'); + $preview = $view->preview('default', [$entities[1]->id()]); + $this->setRawContent(\Drupal::service('renderer')->renderRoot($preview)); + $view_class = 'js-view-dom-id-' . $view->dom_id; + $header_xpath = '//div[@class = "' . $view_class . '"]/header[1]'; + $footer_xpath = '//div[@class = "' . $view_class . '"]/footer[1]'; + + $result = $this->xpath($header_xpath); + $this->assertTrue(strpos(trim((string) $result[0]), $entities[0]->label()) !== FALSE, 'The rendered entity appears in the header of the view.'); + $this->assertTrue(strpos(trim((string) $result[0]), 'full') !== FALSE, 'The rendered entity appeared in the right view mode.'); + + $result = $this->xpath($footer_xpath); + $this->assertTrue(strpos(trim((string) $result[0]), $entities[1]->label()) !== FALSE, 'The rendered entity appears in the footer of the view.'); + $this->assertTrue(strpos(trim((string) $result[0]), 'full') !== FALSE, 'The rendered entity appeared in the right view mode.'); + + $preview = $view->preview('default', array($entities[1]->id())); + $this->setRawContent($renderer->renderRoot($preview)); + + $result = $this->xpath($header_xpath); + $this->assertTrue(strpos(trim((string) $result[0]), $entities[0]->label()) !== FALSE, 'The rendered entity appears in the header of the view.'); + $this->assertTrue(strpos(trim((string) $result[0]), 'full') !== FALSE, 'The rendered entity appeared in the right view mode.'); + + $result = $this->xpath($footer_xpath); + $this->assertTrue(strpos(trim((string) $result[0]), $entities[1]->label()) !== FALSE, 'The rendered entity appears in the footer of the view.'); + $this->assertTrue(strpos(trim((string) $result[0]), 'full') !== FALSE, 'The rendered entity appeared in the right view mode.'); + + // Mark entity_test test view_mode as customizable. + $entity_view_mode = \Drupal::entityManager()->getStorage('entity_view_mode')->load('entity_test.test'); + $entity_view_mode->enable(); + $entity_view_mode->save(); + + // Change the view mode of the area handler. + $view = Views::getView('test_entity_area'); + $item = $view->getHandler('default', 'header', 'entity_entity_test'); + $item['view_mode'] = 'test'; + $view->setHandler('default', 'header', 'entity_entity_test', $item); + + $preview = $view->preview('default', array($entities[1]->id())); + $this->setRawContent($renderer->renderRoot($preview)); + $view_class = 'js-view-dom-id-' . $view->dom_id; + $result = $this->xpath('//div[@class = "' . $view_class . '"]/header[1]'); + $this->assertTrue(strpos(trim((string) $result[0]), $entities[0]->label()) !== FALSE, 'The rendered entity appears in the header of the view.'); + $this->assertTrue(strpos(trim((string) $result[0]), 'test') !== FALSE, 'The rendered entity appeared in the right view mode.'); + + // Test entity access. + $view = Views::getView('test_entity_area'); + $preview = $view->preview('default', array($entities[2]->id())); + $this->setRawContent($renderer->renderRoot($preview)); + $view_class = 'js-view-dom-id-' . $view->dom_id; + $result = $this->xpath('//div[@class = "' . $view_class . '"]/footer[1]'); + $this->assertTrue(strpos($result[0], $entities[2]->label()) === FALSE, 'The rendered entity does not appear in the footer of the view.'); + + // Test the available view mode options. + $form = array(); + $form_state = (new FormState()) + ->set('type', 'header'); + $view->display_handler->getHandler('header', 'entity_entity_test')->buildOptionsForm($form, $form_state); + $this->assertTrue(isset($form['view_mode']['#options']['test']), 'Ensure that the test view mode is available.'); + $this->assertTrue(isset($form['view_mode']['#options']['default']), 'Ensure that the default view mode is available.'); + } + + /** + * Tests the calculation of the rendered dependencies. + */ + public function doTestCalculateDependencies() { + $view = View::load('test_entity_area'); + + $dependencies = $view->calculateDependencies()->getDependencies(); + // Ensure that both config and content entity dependencies are calculated. + $this->assertEqual([ + 'config' => ['block.block.test_block'], + 'content' => ['entity_test:entity_test:aa0c61cb-b7bb-4795-972a-493dabcf529c'], + ], $dependencies); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/AreaMessagesTest.php b/core/modules/views/tests/src/Kernel/Handler/AreaMessagesTest.php new file mode 100644 index 0000000..9c30bbe --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/AreaMessagesTest.php @@ -0,0 +1,44 @@ +setDisplay('default'); + $this->executeView($view); + $output = $view->render(); + $output = \Drupal::service('renderer')->renderRoot($output); + $this->setRawContent($output); + $this->assertText('My drupal set message.'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/AreaTextTest.php b/core/modules/views/tests/src/Kernel/Handler/AreaTextTest.php new file mode 100644 index 0000000..28bd977 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/AreaTextTest.php @@ -0,0 +1,75 @@ +installConfig(array('system', 'filter')); + $this->installEntitySchema('user'); + } + + public function testAreaText() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = $this->container->get('renderer'); + $view = Views::getView('test_view'); + $view->setDisplay(); + + // add a text header + $string = $this->randomMachineName(); + $view->displayHandlers->get('default')->overrideOption('header', array( + 'area' => array( + 'id' => 'area', + 'table' => 'views', + 'field' => 'area', + 'content' => array( + 'value' => $string, + ), + ), + )); + + // Execute the view. + $this->executeView($view); + + $view->display_handler->handlers['header']['area']->options['content']['format'] = $this->randomString(); + $build = $view->display_handler->handlers['header']['area']->render(); + $this->assertEqual('', $renderer->renderRoot($build), 'Nonexistent format should return empty markup.'); + + $view->display_handler->handlers['header']['area']->options['content']['format'] = filter_default_format(); + $build = $view->display_handler->handlers['header']['area']->render(); + $this->assertEqual(check_markup($string), $renderer->renderRoot($build), 'Existent format should return something'); + + // Empty results, and it shouldn't be displayed . + $this->assertEqual(array(), $view->display_handler->handlers['header']['area']->render(TRUE), 'No result should lead to no header'); + // Empty results, and it should be displayed. + $view->display_handler->handlers['header']['area']->options['empty'] = TRUE; + $build = $view->display_handler->handlers['header']['area']->render(TRUE); + $this->assertEqual(check_markup($string), $renderer->renderRoot($build), 'No result, but empty enabled lead to a full header'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/AreaTitleTest.php b/core/modules/views/tests/src/Kernel/Handler/AreaTitleTest.php new file mode 100644 index 0000000..2343951 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/AreaTitleTest.php @@ -0,0 +1,61 @@ +setDisplay('default'); + $this->executeView($view); + $view->render(); + $this->assertFalse($view->getTitle(), 'The title area does not override the title if the view is not empty.'); + $view->destroy(); + + $view->setDisplay('default'); + $this->executeView($view); + $view->result = array(); + $view->render(); + $this->assertEqual($view->getTitle(), 'test_title_empty', 'The title area should override the title if the result is empty.'); + $view->destroy(); + + $view->setDisplay('page_1'); + $this->executeView($view); + $view->render(); + $this->assertEqual($view->getTitle(), 'test_title_header', 'The title area on the header should override the title if the result is not empty.'); + $view->destroy(); + + $view->setDisplay('page_1'); + $this->executeView($view); + $view->result = array(); + $view->render(); + $this->assertEqual($view->getTitle(), 'test_title_empty', 'The title area should override the title if the result is empty.'); + $view->destroy(); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/AreaViewTest.php b/core/modules/views/tests/src/Kernel/Handler/AreaViewTest.php new file mode 100644 index 0000000..53d6066 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/AreaViewTest.php @@ -0,0 +1,61 @@ +container->get('renderer'); + $view = Views::getView('test_area_view'); + + // Tests \Drupal\views\Plugin\views\area\View::calculateDependencies(). + $this->assertIdentical(['config' => ['views.view.test_simple_argument']], $view->getDependencies()); + + $this->executeView($view); + $output = $view->render(); + $output = $renderer->renderRoot($output); + $this->assertTrue(strpos($output, 'js-view-dom-id-' . $view->dom_id) !== FALSE, 'The test view is correctly embedded.'); + $view->destroy(); + + $view->setArguments(array(27)); + $this->executeView($view); + $output = $view->render(); + $output = $renderer->renderRoot($output); + $this->assertTrue(strpos($output, 'John') === FALSE, 'The test view is correctly embedded with inherited arguments.'); + $this->assertTrue(strpos($output, 'George') !== FALSE, 'The test view is correctly embedded with inherited arguments.'); + $view->destroy(); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/ArgumentDateTest.php b/core/modules/views/tests/src/Kernel/Handler/ArgumentDateTest.php new file mode 100644 index 0000000..16a8144 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/ArgumentDateTest.php @@ -0,0 +1,323 @@ + 'id', + ); + + /** + * {@inheritdoc} + */ + public function viewsData() { + $data = parent::viewsData(); + + $date_plugins = array( + 'date_fulldate', + 'date_day', + 'date_month', + 'date_week', + 'date_year', + 'date_year_month', + ); + foreach ($date_plugins as $plugin_id) { + $data['views_test_data'][$plugin_id] = $data['views_test_data']['created']; + $data['views_test_data'][$plugin_id]['real field'] = 'created'; + $data['views_test_data'][$plugin_id]['argument']['id'] = $plugin_id; + } + return $data; + } + + /** + * Tests the CreatedFullDate handler. + * + * @see \Drupal\node\Plugin\views\argument\CreatedFullDate + */ + public function testCreatedFullDateHandler() { + $view = Views::getView('test_argument_date'); + $view->setDisplay('default'); + $this->executeView($view, array('20000102')); + $expected = array(); + $expected[] = array('id' => 2); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('default'); + $this->executeView($view, array('20000101')); + $expected = array(); + $expected[] = array('id' => 1); + $expected[] = array('id' => 3); + $expected[] = array('id' => 4); + $expected[] = array('id' => 5); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('default'); + $this->executeView($view, array('20001023')); + $expected = array(); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + } + + /** + * Tests the Day handler. + * + * @see \Drupal\node\Plugin\views\argument\CreatedDay + */ + public function testDayHandler() { + $view = Views::getView('test_argument_date'); + $view->setDisplay('embed_1'); + $this->executeView($view, array('02')); + $expected = array(); + $expected[] = array('id' => 2); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_1'); + $this->executeView($view, array('01')); + $expected = array(); + $expected[] = array('id' => 1); + $expected[] = array('id' => 3); + $expected[] = array('id' => 4); + $expected[] = array('id' => 5); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_1'); + $this->executeView($view, array('23')); + $expected = array(); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + } + + /** + * Tests the Month handler. + * + * @see \Drupal\node\Plugin\views\argument\CreatedMonth + */ + public function testMonthHandler() { + $view = Views::getView('test_argument_date'); + $view->setDisplay('embed_2'); + $this->executeView($view, array('01')); + $expected = array(); + $expected[] = array('id' => 1); + $expected[] = array('id' => 2); + $expected[] = array('id' => 3); + $expected[] = array('id' => 4); + $expected[] = array('id' => 5); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_2'); + $this->executeView($view, array('12')); + $expected = array(); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + } + + /** + * Tests the Week handler. + * + * @see \Drupal\node\Plugin\views\argument\CreatedWeek + */ + public function testWeekHandler() { + $this->container->get('database')->update('views_test_data') + ->fields(array('created' => gmmktime(0, 0, 0, 9, 26, 2008))) + ->condition('id', 1) + ->execute(); + + $this->container->get('database')->update('views_test_data') + ->fields(array('created' => gmmktime(0, 0, 0, 2, 29, 2004))) + ->condition('id', 2) + ->execute(); + + $this->container->get('database')->update('views_test_data') + ->fields(array('created' => gmmktime(0, 0, 0, 1, 1, 2000))) + ->condition('id', 3) + ->execute(); + + $this->container->get('database')->update('views_test_data') + ->fields(array('created' => gmmktime(0, 0, 0, 1, 10, 2000))) + ->condition('id', 4) + ->execute(); + + $this->container->get('database')->update('views_test_data') + ->fields(array('created' => gmmktime(0, 0, 0, 2, 1, 2000))) + ->condition('id', 5) + ->execute(); + + $view = Views::getView('test_argument_date'); + $view->setDisplay('embed_3'); + // Check the week calculation for a leap year. + // @see http://wikipedia.org/wiki/ISO_week_date#Calculation + $this->executeView($view, array('39')); + $expected = array(); + $expected[] = array('id' => 1); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_3'); + // Check the week calculation for the 29th of February in a leap year. + // @see http://wikipedia.org/wiki/ISO_week_date#Calculation + $this->executeView($view, array('09')); + $expected = array(); + $expected[] = array('id' => 2); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_3'); + // The first jan 2000 was still in the last week of the previous year. + $this->executeView($view, array('52')); + $expected = array(); + $expected[] = array('id' => 3); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_3'); + $this->executeView($view, array('02')); + $expected = array(); + $expected[] = array('id' => 4); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_3'); + $this->executeView($view, array('05')); + $expected = array(); + $expected[] = array('id' => 5); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_3'); + $this->executeView($view, array('23')); + $expected = array(); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + } + + /** + * Tests the Year handler. + * + * @see \Drupal\node\Plugin\views\argument\CreatedYear + */ + public function testYearHandler() { + $this->container->get('database')->update('views_test_data') + ->fields(array('created' => gmmktime(0, 0, 0, 1, 1, 2001))) + ->condition('id', 3) + ->execute(); + + $this->container->get('database')->update('views_test_data') + ->fields(array('created' => gmmktime(0, 0, 0, 1, 1, 2002))) + ->condition('id', 4) + ->execute(); + + $this->container->get('database')->update('views_test_data') + ->fields(array('created' => gmmktime(0, 0, 0, 1, 1, 2002))) + ->condition('id', 5) + ->execute(); + + $view = Views::getView('test_argument_date'); + $view->setDisplay('embed_4'); + $this->executeView($view, array('2000')); + $expected = array(); + $expected[] = array('id' => 1); + $expected[] = array('id' => 2); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_4'); + $this->executeView($view, array('2001')); + $expected = array(); + $expected[] = array('id' => 3); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_4'); + $this->executeView($view, array('2002')); + $expected = array(); + $expected[] = array('id' => 4); + $expected[] = array('id' => 5); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_4'); + $this->executeView($view, array('23')); + $expected = array(); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + } + + /** + * Tests the YearMonth handler. + * + * @see \Drupal\node\Plugin\views\argument\CreatedYearMonth + */ + public function testYearMonthHandler() { + $this->container->get('database')->update('views_test_data') + ->fields(array('created' => gmmktime(0, 0, 0, 1, 1, 2001))) + ->condition('id', 3) + ->execute(); + + $this->container->get('database')->update('views_test_data') + ->fields(array('created' => gmmktime(0, 0, 0, 4, 1, 2001))) + ->condition('id', 4) + ->execute(); + + $this->container->get('database')->update('views_test_data') + ->fields(array('created' => gmmktime(0, 0, 0, 4, 1, 2001))) + ->condition('id', 5) + ->execute(); + + $view = Views::getView('test_argument_date'); + $view->setDisplay('embed_5'); + $this->executeView($view, array('200001')); + $expected = array(); + $expected[] = array('id' => 1); + $expected[] = array('id' => 2); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_5'); + $this->executeView($view, array('200101')); + $expected = array(); + $expected[] = array('id' => 3); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_5'); + $this->executeView($view, array('200104')); + $expected = array(); + $expected[] = array('id' => 4); + $expected[] = array('id' => 5); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + $view->destroy(); + + $view->setDisplay('embed_5'); + $this->executeView($view, array('201301')); + $expected = array(); + $this->assertIdenticalResultset($view, $expected, $this->columnMap); + } +} diff --git a/core/modules/views/tests/src/Kernel/Handler/ArgumentNullTest.php b/core/modules/views/tests/src/Kernel/Handler/ArgumentNullTest.php new file mode 100644 index 0000000..ede303b --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/ArgumentNullTest.php @@ -0,0 +1,79 @@ +setDisplay(); + + // Add a null argument. + $view->displayHandlers->get('default')->overrideOption('arguments', array( + 'null' => array( + 'id' => 'null', + 'table' => 'views', + 'field' => 'null', + ), + )); + + $this->executeView($view); + + // Make sure that the argument is not validated yet. + unset($view->argument['null']->argument_validated); + $this->assertTrue($view->argument['null']->validateArgument(26)); + // test must_not_be option. + unset($view->argument['null']->argument_validated); + $view->argument['null']->options['must_not_be'] = TRUE; + $this->assertFalse($view->argument['null']->validateArgument(26), 'must_not_be returns FALSE, if there is an argument'); + unset($view->argument['null']->argument_validated); + $this->assertTrue($view->argument['null']->validateArgument(NULL), 'must_not_be returns TRUE, if there is no argument'); + + // Test execution. + $view->destroy(); + $view->setDisplay(); + + // Add a argument, which has null as handler. + $view->displayHandlers->get('default')->overrideOption('arguments', array( + 'id' => array( + 'id' => 'id', + 'table' => 'views_test_data', + 'field' => 'id', + ), + )); + + $this->executeView($view, array(26)); + + // The argument should be ignored, so every result should return. + $this->assertEqual(5, count($view->result)); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/EntityTestViewsFieldAccessTest.php b/core/modules/views/tests/src/Kernel/Handler/EntityTestViewsFieldAccessTest.php new file mode 100644 index 0000000..a4b0925 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/EntityTestViewsFieldAccessTest.php @@ -0,0 +1,46 @@ +installEntitySchema('entity_test'); + } + + public function testEntityTestFields() { + $entity_test = EntityTest::create([ + 'name' => 'test entity name', + ]); + $entity_test->save(); + + // @todo Expand the test coverage in https://www.drupal.org/node/2464635 + + $this->assertFieldAccess('entity_test', 'id', $entity_test->id()); + $this->assertFieldAccess('entity_test', 'langcode', $entity_test->language()->getName()); + $this->assertFieldAccess('entity_test', 'name', $entity_test->getName()); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldBooleanTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldBooleanTest.php new file mode 100644 index 0000000..2503cae --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FieldBooleanTest.php @@ -0,0 +1,84 @@ +setDisplay(); + + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'age' => array( + 'id' => 'age', + 'table' => 'views_test_data', + 'field' => 'age', + 'relationship' => 'none', + ), + )); + + $this->executeView($view); + + // This is john, which has no age, there are no custom formats defined, yet. + $this->assertEqual(t('No'), $view->field['age']->advancedRender($view->result[0])); + $this->assertEqual(t('Yes'), $view->field['age']->advancedRender($view->result[1])); + + // Reverse the output. + $view->field['age']->options['not'] = TRUE; + $this->assertEqual(t('Yes'), $view->field['age']->advancedRender($view->result[0])); + $this->assertEqual(t('No'), $view->field['age']->advancedRender($view->result[1])); + + unset($view->field['age']->options['not']); + + // Use another output format. + $view->field['age']->options['type'] = 'true-false'; + $this->assertEqual(t('False'), $view->field['age']->advancedRender($view->result[0])); + $this->assertEqual(t('True'), $view->field['age']->advancedRender($view->result[1])); + + // test awesome unicode. + $view->field['age']->options['type'] = 'unicode-yes-no'; + $this->assertEqual('✖', $view->field['age']->advancedRender($view->result[0])); + $this->assertEqual('✔', $view->field['age']->advancedRender($view->result[1])); + + // Set a custom output format. + $view->field['age']->formats['test'] = array(t('Test-True'), t('Test-False')); + $view->field['age']->options['type'] = 'test'; + $this->assertEqual(t('Test-False'), $view->field['age']->advancedRender($view->result[0])); + $this->assertEqual(t('Test-True'), $view->field['age']->advancedRender($view->result[1])); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldCounterTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldCounterTest.php new file mode 100644 index 0000000..fb2d4fc --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FieldCounterTest.php @@ -0,0 +1,96 @@ +setDisplay(); + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'counter' => array( + 'id' => 'counter', + 'table' => 'views', + 'field' => 'counter', + 'relationship' => 'none', + ), + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + ), + )); + $view->preview(); + + $counter = $view->style_plugin->getField(0, 'counter'); + $this->assertEqual($counter, '1', format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 1, '@counter' => $counter))); + $counter = $view->style_plugin->getField(1, 'counter'); + $this->assertEqual($counter, '2', format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 2, '@counter' => $counter))); + $counter = $view->style_plugin->getField(2, 'counter'); + $this->assertEqual($counter, '3', format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 3, '@counter' => $counter))); + $view->destroy(); + $view->storage->invalidateCaches(); + + $view->setDisplay(); + $rand_start = rand(5, 10); + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'counter' => array( + 'id' => 'counter', + 'table' => 'views', + 'field' => 'counter', + 'relationship' => 'none', + 'counter_start' => $rand_start + ), + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + ), + )); + $view->preview(); + + $counter = $view->style_plugin->getField(0, 'counter'); + $expected_number = 0 + $rand_start; + $this->assertEqual($counter, (string) $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); + $counter = $view->style_plugin->getField(1, 'counter'); + $expected_number = 1 + $rand_start; + $this->assertEqual($counter, (string) $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); + $counter = $view->style_plugin->getField(2, 'counter'); + $expected_number = 2 + $rand_start; + $this->assertEqual($counter, (string) $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter))); + } + + // @TODO: Write tests for pager. + function testPager() { + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldCustomTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldCustomTest.php new file mode 100644 index 0000000..c009b6c --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FieldCustomTest.php @@ -0,0 +1,121 @@ +setDisplay(); + + // Alter the text of the field to a random string. + $random = '
                              ' . $this->randomMachineName() . '
                              '; + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'alter' => array( + 'text' => $random, + ), + ), + )); + + $this->executeView($view); + + $this->assertEqual($random, $view->style_plugin->getField(0, 'name')); + } + + /** + * Ensure that custom fields can use tokens. + */ + public function testFieldCustomTokens() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + $view->displayHandlers->get('default')->overrideOption('fields', [ + 'age' => [ + 'id' => 'age', + 'exclude' => TRUE, + 'table' => 'views_test_data', + 'field' => 'age', + ], + 'name' => [ + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'alter' => [ + 'text' => 'Amount of kittens: {{ age }}', + ], + ], + ]); + + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + $preview = $view->preview(); + $output = $renderer->renderRoot($preview); + + $expected_text = 'Amount of kittens: ' . $view->style_plugin->getField(0, 'age'); + $this->assertTrue(strpos((string) $output, $expected_text), 'The views token has been successfully replaced.'); + } + + /** + * Ensure that custom field content is XSS filtered. + */ + public function testCustomFieldXss() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Alter the text of the field to include XSS. + $text = ''; + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'alter' => array( + 'text' => $text, + ), + ), + )); + $this->executeView($view); + $this->assertEqual(Xss::filter($text), $view->style_plugin->getField(0, 'name')); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldDateTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldDateTest.php new file mode 100644 index 0000000..123d4f4 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FieldDateTest.php @@ -0,0 +1,198 @@ + "The destruction date of this record", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => FALSE, + 'default' => 0, + ); + return $schema; + } + + /** + * {@inheritdoc} + */ + public function viewsData() { + $data = parent::viewsData(); + $data['views_test_data']['created']['field']['id'] = 'date'; + $data['views_test_data']['destroyed'] = array( + 'title' => 'Destroyed', + 'help' => 'Date in future this will be destroyed.', + 'field' => array('id' => 'date'), + 'argument' => array('id' => 'date'), + 'filter' => array('id' => 'date'), + 'sort' => array('id' => 'date'), + ); + return $data; + } + + /** + * {@inheritdoc} + */ + public function dataSet() { + $datas = parent::dataSet(); + foreach ($datas as $i => $data) { + $datas[$i]['destroyed'] = gmmktime(0, 0, 0, 1, 1, 2050); + } + return $datas; + } + + /** + * Sets up functional test of the views date field. + */ + public function testFieldDate() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'created' => array( + 'id' => 'created', + 'table' => 'views_test_data', + 'field' => 'created', + 'relationship' => 'none', + // ISO 8601 format, see http://php.net/manual/function.date.php + 'custom_date_format' => 'c', + ), + 'destroyed' => array( + 'id' => 'destroyed', + 'table' => 'views_test_data', + 'field' => 'destroyed', + 'relationship' => 'none', + 'custom_date_format' => 'c', + ), + )); + $time = gmmktime(0, 0, 0, 1, 1, 2000); + + $this->executeView($view); + + $timezones = array( + NULL, + 'UTC', + 'America/New_York', + ); + + // Check each date/time in various timezones. + foreach ($timezones as $timezone) { + $dates = array( + 'short' => format_date($time, 'short', '', $timezone), + 'medium' => format_date($time, 'medium', '', $timezone), + 'long' => format_date($time, 'long', '', $timezone), + 'custom' => format_date($time, 'custom', 'c', $timezone), + 'fallback' => format_date($time, 'fallback', '', $timezone), + 'html_date' => format_date($time, 'html_date', '', $timezone), + 'html_datetime' => format_date($time, 'html_datetime', '', $timezone), + 'html_month' => format_date($time, 'html_month', '', $timezone), + 'html_time' => format_date($time, 'html_time', '', $timezone), + 'html_week' => format_date($time, 'html_week', '', $timezone), + 'html_year' => format_date($time, 'html_year', '', $timezone), + 'html_yearless_date' => format_date($time, 'html_yearless_date', '', $timezone), + ); + $this->assertRenderedDatesEqual($view, $dates, $timezone); + } + + // Check times in the past. + $time_since = $this->container->get('date.formatter')->formatTimeDiffSince($time); + $intervals = array( + 'raw time ago' => $time_since, + 'time ago' => t('%time ago', array('%time' => $time_since)), + 'raw time span' => $time_since, + 'inverse time span' => "-$time_since", + 'time span' => t('%time ago', array('%time' => $time_since)), + ); + $this->assertRenderedDatesEqual($view, $intervals); + + // Check times in the future. + $time = gmmktime(0, 0, 0, 1, 1, 2050); + $formatted = $this->container->get('date.formatter')->formatTimeDiffUntil($time); + $intervals = array( + 'raw time span' => "-$formatted", + 'time span' => t('%time hence', array( + '%time' => $formatted, + )), + ); + $this->assertRenderedFutureDatesEqual($view, $intervals); + } + + /** + * Asserts properly formatted display against 'created' field in view. + * + * @param mixed $view + * View to be tested. + * @param array $map + * Data map. + * @param null $timezone + * Optional timezone. + */ + protected function assertRenderedDatesEqual($view, $map, $timezone = NULL) { + foreach ($map as $date_format => $expected_result) { + $view->field['created']->options['date_format'] = $date_format; + $t_args = array( + '%value' => $expected_result, + '%format' => $date_format, + ); + if (isset($timezone)) { + $t_args['%timezone'] = $timezone; + $message = t('Value %value in %format format for timezone %timezone matches.', $t_args); + $view->field['created']->options['timezone'] = $timezone; + } + else { + $message = t('Value %value in %format format matches.', $t_args); + } + $actual_result = $view->field['created']->advancedRender($view->result[0]); + $this->assertEqual($expected_result, $actual_result, $message); + } + } + + /** + * Asserts properly formatted display against 'destroyed' field in view. + * + * @param mixed $view + * View to be tested. + * @param array $map + * Data map. + */ + protected function assertRenderedFutureDatesEqual($view, $map) { + foreach ($map as $format => $result) { + $view->field['destroyed']->options['date_format'] = $format; + $view_result = $view->field['destroyed']->advancedRender($view->result[0]); + $t_args = array( + '%value' => $result, + '%format' => $format, + '%actual' => $view_result, + ); + $message = t('Value %value in %format matches %actual', $t_args); + $this->assertEqual($view_result, $result, $message); + } + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldEntityLinkTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldEntityLinkTest.php new file mode 100644 index 0000000..4df2d59 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FieldEntityLinkTest.php @@ -0,0 +1,131 @@ +installEntitySchema('user'); + $this->installEntitySchema('entity_test'); + $this->installConfig(['user']); + + // Create some test entities. + for ($i = 0; $i < 5; $i++) { + EntityTest::create(['name' => $this->randomString()])->save(); + } + + // Create and admin user. + $this->adminUser = $this->createUser(['view test entity'], FALSE, TRUE); + + Role::load(AccountInterface::ANONYMOUS_ROLE) + ->grantPermission('view test entity') + ->save(); + } + + /** + * Tests entity link fields. + */ + public function testEntityLink() { + // Anonymous users cannot see edit/delete links. + $expected_results = ['canonical' => TRUE, 'edit-form' => FALSE, 'delete-form' => FALSE]; + $this->doTestEntityLink(\Drupal::currentUser(), $expected_results); + + // Admin users cannot see all links. + $expected_results = ['canonical' => TRUE, 'edit-form' => TRUE, 'delete-form' => TRUE]; + $this->doTestEntityLink($this->adminUser, $expected_results); + } + + /** + * Tests whether entity links behave as expected. + * + * @param \Drupal\Core\Session\AccountInterface $account + * The user account to be used to run the test; + * @param bool[] $expected_results + * An associative array of expected results keyed by link template name. + */ + protected function doTestEntityLink(AccountInterface $account, $expected_results) { + \Drupal::currentUser()->setAccount($account); + + $view = Views::getView('test_entity_test_link'); + $view->preview(); + + $info = [ + 'canonical' => [ + 'label' => 'View entity test', + 'field_id' => 'view_entity_test', + 'destination' => FALSE, + ], + 'edit-form' => [ + 'label' => 'Edit entity test', + 'field_id' => 'edit_entity_test', + 'destination' => TRUE, + ], + 'delete-form' => [ + 'label' => 'Delete entity test', + 'field_id' => 'delete_entity_test', + 'destination' => TRUE, + ], + ]; + + $index = 0; + foreach (EntityTest::loadMultiple() as $entity) { + foreach ($expected_results as $template => $expected_result) { + $expected_link = ''; + if ($expected_result) { + $path = $entity->url($template); + $destination = $info[$template]['destination'] ? '?destination=/' : ''; + $expected_link = '' . $info[$template]['label'] . ''; + } + $link = $view->style_plugin->getField($index, $info[$template]['field_id']); + $this->assertEqual($link, $expected_link); + } + $index++; + } + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldFieldAccessTestBase.php b/core/modules/views/tests/src/Kernel/Handler/FieldFieldAccessTestBase.php new file mode 100644 index 0000000..4cdf592 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FieldFieldAccessTestBase.php @@ -0,0 +1,144 @@ +installEntitySchema('user'); + + $role_with_access = Role::create([ + 'id' => 'with_access', + 'permissions' => ['view test entity field'], + ]); + $role_with_access->save(); + $role_without_access = Role::create([ + 'id' => 'without_access', + 'permissions' => [], + ]); + $role_without_access->save(); + + $this->userWithAccess = User::create([ + 'name' => $this->randomMachineName(), + 'roles' => [$role_with_access->id()], + ]); + $this->userWithAccess->save(); + $this->userWithoutAccess = User::create([ + 'name' => $this->randomMachineName(), + 'roles' => [$role_without_access->id()], + ]); + $this->userWithoutAccess->save(); + } + + /** + * Checks views field access for a given entity type and field name. + * + * To use this method, set up an entity of type $entity_type_id, with field + * $field_name. Create an entity instance that contains content $field_content + * in that field. + * + * This method will check that a user with permission can see the content in a + * view, and a user without access permission on that field cannot. + * + * @param string $entity_type_id + * The entity type ID. + * @param string $field_name + * The field name. + * @param string $field_content + * The expected field content. + */ + protected function assertFieldAccess($entity_type_id, $field_name, $field_content) { + \Drupal::state()->set('views_field_access_test-field', $field_name); + + $entity_type = \Drupal::entityManager()->getDefinition($entity_type_id); + $view_id = $this->randomMachineName(); + $data_table = $entity_type->getDataTable(); + // Use the data table as long as the field is not 'uuid'. This is the only + // column that can only be obtained from the base table. + $base_table = ($data_table && ($field_name !== 'uuid')) ? $data_table : $entity_type->getBaseTable(); + $entity = View::create([ + 'id' => $view_id, + 'base_table' => $base_table, + 'display' => [ + 'default' => [ + 'display_plugin' => 'default', + 'id' => 'default', + 'display_options' => [ + 'fields' => [ + $field_name => [ + 'table' => $base_table, + 'field' => $field_name, + 'id' => $field_name, + 'plugin_id' => 'field', + ], + ], + ], + ], + ], + ]); + $entity->save(); + + /** @var \Drupal\Core\Session\AccountSwitcherInterface $account_switcher */ + $account_switcher = \Drupal::service('account_switcher'); + + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + + $account_switcher->switchTo($this->userWithAccess); + $executable = Views::getView($view_id); + $build = $executable->preview(); + $this->setRawContent($renderer->renderRoot($build)); + + $this->assertText($field_content); + $this->assertTrue(isset($executable->field[$field_name])); + + $account_switcher->switchTo($this->userWithoutAccess); + $executable = Views::getView($view_id); + $build = $executable->preview(); + $this->setRawContent($renderer->renderRoot($build)); + + $this->assertNoText($field_content); + $this->assertFalse(isset($executable->field[$field_name])); + + \Drupal::state()->delete('views_field_access_test-field'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php new file mode 100644 index 0000000..aa5197f --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FieldFieldTest.php @@ -0,0 +1,554 @@ +installEntitySchema('entity_test'); + $this->installEntitySchema('user'); + $this->installEntitySchema('entity_test_rev'); + + // Bypass any field access. + $this->adminUser = User::create(['name' => $this->randomString()]); + $this->adminUser->save(); + $this->container->get('current_user')->setAccount($this->adminUser); + + $this->testUsers = []; + for ($i = 0; $i < 5; $i++) { + $this->testUsers[$i] = User::create([ + 'name' => 'test ' . $i, + 'timezone' => User::getAllowedTimezones()[$i], + 'created' => REQUEST_TIME - rand(0, 3600) + ]); + $this->testUsers[$i]->save(); + } + + // Setup a field storage and field, but also change the views data for the + // entity_test entity type. + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'field_test', + 'type' => 'integer', + 'entity_type' => 'entity_test', + ]); + $field_storage->save(); + + $field = FieldConfig::create([ + 'field_name' => 'field_test', + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + ]); + $field->save(); + + $field_storage_multiple = FieldStorageConfig::create([ + 'field_name' => 'field_test_multiple', + 'type' => 'integer', + 'entity_type' => 'entity_test', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + ]); + $field_storage_multiple->save(); + + $field_multiple = FieldConfig::create([ + 'field_name' => 'field_test_multiple', + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + ]); + $field_multiple->save(); + + $random_number = (string) 30856; + $random_number_multiple = (string) 1370359990; + for ($i = 0; $i < 5; $i++) { + $this->entities[$i] = $entity = EntityTest::create([ + 'bundle' => 'entity_test', + 'name' => 'test ' . $i, + 'field_test' => $random_number[$i], + 'field_test_multiple' => [$random_number_multiple[$i * 2], $random_number_multiple[$i * 2 + 1]], + 'user_id' => $this->testUsers[$i]->id(), + ]); + $entity->save(); + } + + // Setup some test data for entities with revisions. + // We are testing both base field revisions and field config revisions. + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'field_test', + 'type' => 'integer', + 'entity_type' => 'entity_test_rev', + ]); + $field_storage->save(); + + $field = FieldConfig::create([ + 'field_name' => 'field_test', + 'entity_type' => 'entity_test_rev', + 'bundle' => 'entity_test_rev', + ]); + $field->save(); + + $field_storage_multiple = FieldStorageConfig::create([ + 'field_name' => 'field_test_multiple', + 'type' => 'integer', + 'entity_type' => 'entity_test_rev', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + ]); + $field_storage_multiple->save(); + + $field_multiple = FieldConfig::create([ + 'field_name' => 'field_test_multiple', + 'entity_type' => 'entity_test_rev', + 'bundle' => 'entity_test_rev', + ]); + $field_multiple->save(); + + $this->entityRevision = []; + $this->entityRevision[0] = $entity = EntityTestRev::create([ + 'name' => 'base value', + 'field_test' => 1, + 'field_test_multiple' => [1, 3, 7], + 'user_id' => $this->testUsers[0]->id(), + ]); + $entity->save(); + $original_entity = clone $entity; + + $entity = clone $original_entity; + $entity->setNewRevision(TRUE); + $entity->name->value = 'revision value1'; + $entity->field_test->value = 2; + $entity->field_test_multiple[0]->value = 0; + $entity->field_test_multiple[1]->value = 3; + $entity->field_test_multiple[2]->value = 5; + $entity->user_id->target_id = $this->testUsers[1]->id(); + $entity->save(); + $this->entityRevision[1] = $entity; + + $entity = clone $original_entity; + $entity->setNewRevision(TRUE); + $entity->name->value = 'revision value2'; + $entity->field_test->value = 3; + $entity->field_test_multiple[0]->value = 9; + $entity->field_test_multiple[1]->value = 9; + $entity->field_test_multiple[2]->value = 9; + $entity->user_id->target_id = $this->testUsers[2]->id(); + $entity->save(); + $this->entityRevision[2] = $entity; + + $this->entityRevision[3] = $entity = EntityTestRev::create([ + 'name' => 'next entity value', + 'field_test' => 4, + 'field_test_multiple' => [2, 9, 9], + 'user_id' => $this->testUsers[3]->id(), + ]); + $entity->save(); + + \Drupal::state()->set('entity_test.views_data', [ + 'entity_test' => [ + 'id' => [ + 'field' => [ + 'id' => 'field', + ], + ], + ], + 'entity_test_rev_revision' => [ + 'id' => [ + 'field' => [ + 'id' => 'field', + ], + ], + ], + ]); + + Views::viewsData()->clear(); + } + + /** + * Tests the result of a view with base fields and configurable fields. + */ + public function testSimpleExecute() { + $executable = Views::getView('test_field_field_test'); + $executable->execute(); + + $this->assertTrue($executable->field['id'] instanceof Field); + $this->assertTrue($executable->field['field_test'] instanceof Field); + + $this->assertIdenticalResultset($executable, + [ + ['id' => 1, 'field_test' => 3], + ['id' => 2, 'field_test' => 0], + ['id' => 3, 'field_test' => 8], + ['id' => 4, 'field_test' => 5], + ['id' => 5, 'field_test' => 6], + ], + ['id' => 'id', 'field_test' => 'field_test'] + ); + } + + /** + * Tests the output of a view with base fields and configurable fields. + */ + public function testSimpleRender() { + $executable = Views::getView('test_field_field_test'); + $executable->execute(); + + $this->assertEqual('1', $executable->getStyle()->getField(0, 'id')); + $this->assertEqual('3', $executable->getStyle()->getField(0, 'field_test')); + $this->assertEqual('2', $executable->getStyle()->getField(1, 'id')); + // @todo Switch this assertion to assertIdentical('', ...) when + // https://www.drupal.org/node/2488006 gets fixed. + $this->assertEqual('0', $executable->getStyle()->getField(1, 'field_test')); + $this->assertEqual('3', $executable->getStyle()->getField(2, 'id')); + $this->assertEqual('8', $executable->getStyle()->getField(2, 'field_test')); + $this->assertEqual('4', $executable->getStyle()->getField(3, 'id')); + $this->assertEqual('5', $executable->getStyle()->getField(3, 'field_test')); + $this->assertEqual('5', $executable->getStyle()->getField(4, 'id')); + $this->assertEqual('6', $executable->getStyle()->getField(4, 'field_test')); + } + + /** + * Tests that formatter's #attached assets are correctly preserved. + * + * @see \Drupal\views_test_formatter\Plugin\Field\FieldFormatter\AttachmentTestFormatter::viewElements() + */ + public function testAttachedRender() { + $executable = Views::getView('test_field_field_attachment_test'); + $executable->execute(); + + // Check that the attachments added by AttachmentTestFormatter have been + // preserved in the render array. + $render = $executable->display_handler->render(); + $expected_attachments = [ + 'library' => [ + 'views/views.module' + ] + ]; + foreach ($this->entities as $entity) { + $expected_attachments['library'][] = 'foo/fake_library'; + $expected_attachments['drupalSettings']['AttachmentIntegerFormatter'][$entity->id()] = $entity->id(); + } + $this->assertEqual($expected_attachments, $render['#attached']); + } + + /** + * Tests the result of a view with complex field configuration. + * + * A complex field configuration contains multiple times the same field, with + * different delta limit / offset. + */ + public function testFieldAlias() { + $executable = Views::getView('test_field_alias_test'); + $executable->execute(); + + $this->assertTrue($executable->field['id'] instanceof Field); + $this->assertTrue($executable->field['name'] instanceof Field); + $this->assertTrue($executable->field['name_alias'] instanceof Field); + + $this->assertIdenticalResultset($executable, + [ + ['id' => 1, 'name' => 'test 0', 'name_alias' => 'test 0'], + ['id' => 2, 'name' => 'test 1', 'name_alias' => 'test 1'], + ['id' => 3, 'name' => 'test 2', 'name_alias' => 'test 2'], + ['id' => 4, 'name' => 'test 3', 'name_alias' => 'test 3'], + ['id' => 5, 'name' => 'test 4', 'name_alias' => 'test 4'], + ], + ['id' => 'id', 'name' => 'name', 'name_alias' => 'name_alias'] + ); + } + + /** + * Tests the result of a view with complex field configuration. + * + * A complex field configuration contains multiple times the same field, with + * different delta limit / offset. + */ + public function testFieldAliasRender() { + $executable = Views::getView('test_field_alias_test'); + $executable->execute(); + + for ($i = 0; $i < 5; $i++) { + $this->assertEqual((string) ($i + 1), $executable->getStyle()->getField($i, 'id')); + $this->assertEqual('test ' . $i, $executable->getStyle()->getField($i, 'name')); + $entity = EntityTest::load($i + 1); + $this->assertEqual('test ' . $i . '', (string) $executable->getStyle()->getField($i, 'name_alias')); + } + } + + /** + * Tests the result of a view with complex field configuration. + * + * A complex field configuration contains multiple times the same field, with + * different delta limit / offset. + */ + public function testComplexExecute() { + $executable = Views::getView('test_field_field_complex_test'); + $executable->execute(); + + $timezones = []; + foreach ($this->testUsers as $user) { + $timezones[] = $user->getTimeZone(); + } + + $this->assertTrue($executable->field['field_test_multiple'] instanceof Field); + $this->assertTrue($executable->field['field_test_multiple_1'] instanceof Field); + $this->assertTrue($executable->field['field_test_multiple_2'] instanceof Field); + $this->assertTrue($executable->field['timezone'] instanceof Field); + + $this->assertIdenticalResultset($executable, + [ + ['timezone' => $timezones[0], 'field_test_multiple' => [1, 3], 'field_test_multiple_1' => [1, 3], 'field_test_multiple_2' => [1, 3]], + ['timezone' => $timezones[1], 'field_test_multiple' => [7, 0], 'field_test_multiple_1' => [7, 0], 'field_test_multiple_2' => [7, 0]], + ['timezone' => $timezones[2], 'field_test_multiple' => [3, 5], 'field_test_multiple_1' => [3, 5], 'field_test_multiple_2' => [3, 5]], + ['timezone' => $timezones[3], 'field_test_multiple' => [9, 9], 'field_test_multiple_1' => [9, 9], 'field_test_multiple_2' => [9, 9]], + ['timezone' => $timezones[4], 'field_test_multiple' => [9, 0], 'field_test_multiple_1' => [9, 0], 'field_test_multiple_2' => [9, 0]], + ], + ['timezone' => 'timezone', 'field_test_multiple' => 'field_test_multiple', 'field_test_multiple_1' => 'field_test_multiple_1', 'field_test_multiple_2' => 'field_test_multiple_2'] + ); + } + + /** + * Tests the output of a view with complex field configuration. + */ + public function testComplexRender() { + $executable = Views::getView('test_field_field_complex_test'); + $executable->execute(); + $date_formatter = \Drupal::service('date.formatter'); + + $this->assertEqual($this->testUsers[0]->getTimeZone(), $executable->getStyle()->getField(0, 'timezone')); + $this->assertEqual("1, 3", $executable->getStyle()->getField(0, 'field_test_multiple')); + $this->assertEqual("1", $executable->getStyle()->getField(0, 'field_test_multiple_1')); + $this->assertEqual("3", $executable->getStyle()->getField(0, 'field_test_multiple_2')); + $this->assertEqual($date_formatter->format($this->testUsers[0]->getCreatedTime(), 'custom', 'Y'), $executable->getStyle()->getField(0, 'created')); + $this->assertEqual($date_formatter->format($this->testUsers[0]->getCreatedTime(), 'custom', 'H:i:s'), $executable->getStyle()->getField(0, 'created_1')); + $this->assertEqual($date_formatter->format($this->testUsers[0]->getCreatedTime(), 'fallback'), $executable->getStyle()->getField(0, 'created_2')); + + $this->assertEqual($this->testUsers[1]->getTimeZone(), $executable->getStyle()->getField(1, 'timezone')); + $this->assertEqual("7, 0", $executable->getStyle()->getField(1, 'field_test_multiple')); + $this->assertEqual("7", $executable->getStyle()->getField(1, 'field_test_multiple_1')); + $this->assertEqual("0", $executable->getStyle()->getField(1, 'field_test_multiple_2')); + $this->assertEqual($date_formatter->format($this->testUsers[1]->getCreatedTime(), 'custom', 'Y'), $executable->getStyle()->getField(1, 'created')); + $this->assertEqual($date_formatter->format($this->testUsers[1]->getCreatedTime(), 'custom', 'H:i:s'), $executable->getStyle()->getField(1, 'created_1')); + $this->assertEqual($date_formatter->format($this->testUsers[1]->getCreatedTime(), 'fallback'), $executable->getStyle()->getField(1, 'created_2')); + + $this->assertEqual($this->testUsers[2]->getTimeZone(), $executable->getStyle()->getField(2, 'timezone')); + $this->assertEqual("3, 5", $executable->getStyle()->getField(2, 'field_test_multiple')); + $this->assertEqual("3", $executable->getStyle()->getField(2, 'field_test_multiple_1')); + $this->assertEqual("5", $executable->getStyle()->getField(2, 'field_test_multiple_2')); + $this->assertEqual($date_formatter->format($this->testUsers[2]->getCreatedTime(), 'custom', 'Y'), $executable->getStyle()->getField(2, 'created')); + $this->assertEqual($date_formatter->format($this->testUsers[2]->getCreatedTime(), 'custom', 'H:i:s'), $executable->getStyle()->getField(2, 'created_1')); + $this->assertEqual($date_formatter->format($this->testUsers[2]->getCreatedTime(), 'fallback'), $executable->getStyle()->getField(2, 'created_2')); + + $this->assertEqual($this->testUsers[3]->getTimeZone(), $executable->getStyle()->getField(3, 'timezone')); + $this->assertEqual("9, 9", $executable->getStyle()->getField(3, 'field_test_multiple')); + $this->assertEqual("9", $executable->getStyle()->getField(3, 'field_test_multiple_1')); + $this->assertEqual("9", $executable->getStyle()->getField(3, 'field_test_multiple_2')); + $this->assertEqual($date_formatter->format($this->testUsers[3]->getCreatedTime(), 'custom', 'Y'), $executable->getStyle()->getField(3, 'created')); + $this->assertEqual($date_formatter->format($this->testUsers[3]->getCreatedTime(), 'custom', 'H:i:s'), $executable->getStyle()->getField(3, 'created_1')); + $this->assertEqual($date_formatter->format($this->testUsers[3]->getCreatedTime(), 'fallback'), $executable->getStyle()->getField(3, 'created_2')); + + $this->assertEqual($this->testUsers[4]->getTimeZone(), $executable->getStyle()->getField(4, 'timezone')); + $this->assertEqual("9, 0", $executable->getStyle()->getField(4, 'field_test_multiple')); + $this->assertEqual("9", $executable->getStyle()->getField(4, 'field_test_multiple_1')); + $this->assertEqual("0", $executable->getStyle()->getField(4, 'field_test_multiple_2')); + $this->assertEqual($date_formatter->format($this->testUsers[4]->getCreatedTime(), 'custom', 'Y'), $executable->getStyle()->getField(4, 'created')); + $this->assertEqual($date_formatter->format($this->testUsers[4]->getCreatedTime(), 'custom', 'H:i:s'), $executable->getStyle()->getField(4, 'created_1')); + $this->assertEqual($date_formatter->format($this->testUsers[4]->getCreatedTime(), 'fallback'), $executable->getStyle()->getField(4, 'created_2')); + } + + /** + * Tests the revision result. + */ + public function testRevisionExecute() { + $executable = Views::getView('test_field_field_revision_test'); + $executable->execute(); + + $this->assertTrue($executable->field['name'] instanceof Field); + $this->assertTrue($executable->field['field_test'] instanceof Field); + + $this->assertIdenticalResultset($executable, + [ + ['id' => 1, 'field_test' => 1, 'revision_id' => 1, 'name' => 'base value'], + ['id' => 1, 'field_test' => 2, 'revision_id' => 2, 'name' => 'revision value1'], + ['id' => 1, 'field_test' => 3, 'revision_id' => 3, 'name' => 'revision value2'], + ['id' => 2, 'field_test' => 4, 'revision_id' => 4, 'name' => 'next entity value'], + ], + ['entity_test_rev_revision_id' => 'id', 'revision_id' => 'revision_id', 'name' => 'name', 'field_test' => 'field_test'] + ); + } + + /** + * Tests the output of a revision view with base and configurable fields. + */ + public function testRevisionRender() { + $executable = Views::getView('test_field_field_revision_test'); + $executable->execute(); + + $this->assertEqual('1', $executable->getStyle()->getField(0, 'id')); + $this->assertEqual('1', $executable->getStyle()->getField(0, 'revision_id')); + $this->assertEqual('1', $executable->getStyle()->getField(0, 'field_test')); + $this->assertEqual('base value', $executable->getStyle()->getField(0, 'name')); + + $this->assertEqual('1', $executable->getStyle()->getField(1, 'id')); + $this->assertEqual('2', $executable->getStyle()->getField(1, 'revision_id')); + $this->assertEqual('2', $executable->getStyle()->getField(1, 'field_test')); + $this->assertEqual('revision value1', $executable->getStyle()->getField(1, 'name')); + + $this->assertEqual('1', $executable->getStyle()->getField(2, 'id')); + $this->assertEqual('3', $executable->getStyle()->getField(2, 'revision_id')); + $this->assertEqual('3', $executable->getStyle()->getField(2, 'field_test')); + $this->assertEqual('revision value2', $executable->getStyle()->getField(2, 'name')); + + $this->assertEqual('2', $executable->getStyle()->getField(3, 'id')); + $this->assertEqual('4', $executable->getStyle()->getField(3, 'revision_id')); + $this->assertEqual('4', $executable->getStyle()->getField(3, 'field_test')); + $this->assertEqual('next entity value', $executable->getStyle()->getField(3, 'name')); + } + + /** + * Tests the result set of a complex revision view. + */ + public function testRevisionComplexExecute() { + $executable = Views::getView('test_field_field_revision_complex_test'); + $executable->execute(); + + $timezones = []; + foreach ($this->testUsers as $user) { + $timezones[] = $user->getTimeZone(); + } + + $this->assertTrue($executable->field['id'] instanceof Field); + $this->assertTrue($executable->field['revision_id'] instanceof Field); + $this->assertTrue($executable->field['timezone'] instanceof Field); + $this->assertTrue($executable->field['field_test_multiple'] instanceof Field); + $this->assertTrue($executable->field['field_test_multiple_1'] instanceof Field); + $this->assertTrue($executable->field['field_test_multiple_2'] instanceof Field); + + $this->assertIdenticalResultset($executable, + [ + ['id' => 1, 'field_test' => 1, 'revision_id' => 1, 'uid' => $this->testUsers[0]->id(), 'timezone' => $timezones[0], 'field_test_multiple' => [1, 3, 7], 'field_test_multiple_1' => [1, 3, 7], 'field_test_multiple_2' => [1, 3, 7]], + ['id' => 1, 'field_test' => 2, 'revision_id' => 2, 'uid' => $this->testUsers[1]->id(), 'timezone' => $timezones[1], 'field_test_multiple' => [0, 3, 5], 'field_test_multiple_1' => [0, 3, 5], 'field_test_multiple_2' => [0, 3, 5]], + ['id' => 1, 'field_test' => 3, 'revision_id' => 3, 'uid' => $this->testUsers[2]->id(), 'timezone' => $timezones[2], 'field_test_multiple' => [9, 9, 9], 'field_test_multiple_1' => [9, 9, 9], 'field_test_multiple_2' => [9, 9, 9]], + ['id' => 2, 'field_test' => 4, 'revision_id' => 4, 'uid' => $this->testUsers[3]->id(), 'timezone' => $timezones[3], 'field_test_multiple' => [2, 9, 9], 'field_test_multiple_1' => [2, 9, 9], 'field_test_multiple_2' => [2, 9, 9]], + ], + ['entity_test_rev_revision_id' => 'id', 'revision_id' => 'revision_id', 'users_field_data_entity_test_rev_revision_uid' => 'uid', 'timezone' => 'timezone', 'field_test_multiple' => 'field_test_multiple', 'field_test_multiple_1' => 'field_test_multiple_1', 'field_test_multiple_2' => 'field_test_multiple_2'] + ); + } + + /** + * Tests the output of a revision view with base fields and configurable fields. + */ + public function testRevisionComplexRender() { + $executable = Views::getView('test_field_field_revision_complex_test'); + $executable->execute(); + + $this->assertEqual('1', $executable->getStyle()->getField(0, 'id')); + $this->assertEqual('1', $executable->getStyle()->getField(0, 'revision_id')); + $this->assertEqual($this->testUsers[0]->getTimeZone(), $executable->getStyle()->getField(0, 'timezone')); + $this->assertEqual('1, 3, 7', $executable->getStyle()->getField(0, 'field_test_multiple')); + $this->assertEqual('1', $executable->getStyle()->getField(0, 'field_test_multiple_1')); + $this->assertEqual('3, 7', $executable->getStyle()->getField(0, 'field_test_multiple_2')); + + $this->assertEqual('1', $executable->getStyle()->getField(1, 'id')); + $this->assertEqual('2', $executable->getStyle()->getField(1, 'revision_id')); + $this->assertEqual($this->testUsers[1]->getTimeZone(), $executable->getStyle()->getField(1, 'timezone')); + $this->assertEqual('0, 3, 5', $executable->getStyle()->getField(1, 'field_test_multiple')); + $this->assertEqual('0', $executable->getStyle()->getField(1, 'field_test_multiple_1')); + $this->assertEqual('3, 5', $executable->getStyle()->getField(1, 'field_test_multiple_2')); + + $this->assertEqual('1', $executable->getStyle()->getField(2, 'id')); + $this->assertEqual('3', $executable->getStyle()->getField(2, 'revision_id')); + $this->assertEqual($this->testUsers[2]->getTimeZone(), $executable->getStyle()->getField(2, 'timezone')); + $this->assertEqual('9, 9, 9', $executable->getStyle()->getField(2, 'field_test_multiple')); + $this->assertEqual('9', $executable->getStyle()->getField(2, 'field_test_multiple_1')); + $this->assertEqual('9, 9', $executable->getStyle()->getField(2, 'field_test_multiple_2')); + + $this->assertEqual('2', $executable->getStyle()->getField(3, 'id')); + $this->assertEqual('4', $executable->getStyle()->getField(3, 'revision_id')); + $this->assertEqual($this->testUsers[3]->getTimeZone(), $executable->getStyle()->getField(3, 'timezone')); + $this->assertEqual('2, 9, 9', $executable->getStyle()->getField(3, 'field_test_multiple')); + $this->assertEqual('2', $executable->getStyle()->getField(3, 'field_test_multiple_1')); + $this->assertEqual('9, 9', $executable->getStyle()->getField(3, 'field_test_multiple_2')); + } + + /** + * Tests that a field not available for every bundle is rendered as empty. + */ + public function testMissingBundleFieldRender() { + // Create a new bundle not having the test field attached. + $bundle = $this->randomMachineName(); + entity_test_create_bundle($bundle); + + $entity = EntityTest::create([ + 'type' => $bundle, + 'name' => $this->randomString(), + 'user_id' => $this->testUsers[0]->id(), + ]); + $entity->save(); + + $executable = Views::getView('test_field_field_test'); + $executable->execute(); + + $this->assertEqual('', $executable->getStyle()->getField(6, 'field_test')); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldFileSizeTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldFileSizeTest.php new file mode 100644 index 0000000..6ce0a66 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FieldFileSizeTest.php @@ -0,0 +1,72 @@ +setDisplay(); + + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'age' => array( + 'id' => 'age', + 'table' => 'views_test_data', + 'field' => 'age', + ), + )); + + $this->executeView($view); + + // Test with the formatted option. + $this->assertEqual($view->field['age']->advancedRender($view->result[0]), ''); + $this->assertEqual($view->field['age']->advancedRender($view->result[1]), '10 bytes'); + $this->assertEqual($view->field['age']->advancedRender($view->result[2]), '1000 bytes'); + $this->assertEqual($view->field['age']->advancedRender($view->result[3]), '9.77 KB'); + // Test with the bytes option. + $view->field['age']->options['file_size_display'] = 'bytes'; + $this->assertEqual($view->field['age']->advancedRender($view->result[0]), ''); + $this->assertEqual($view->field['age']->advancedRender($view->result[1]), '10'); + $this->assertEqual($view->field['age']->advancedRender($view->result[2]), '1000'); + $this->assertEqual($view->field['age']->advancedRender($view->result[3]), '10000'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldKernelTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldKernelTest.php new file mode 100644 index 0000000..c5bfb5d --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FieldKernelTest.php @@ -0,0 +1,797 @@ + 'name', + ); + + /** + * {@inheritdoc} + */ + protected function viewsData() { + $data = parent::viewsData(); + $data['views_test_data']['job']['field']['id'] = 'test_field'; + $data['views_test_data']['job']['field']['click sortable'] = FALSE; + $data['views_test_data']['id']['field']['click sortable'] = TRUE; + return $data; + } + + /** + * Tests that the render function is called. + */ + public function testRender() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + + $view = Views::getView('test_field_tokens'); + $this->executeView($view); + + $random_text = $this->randomMachineName(); + $view->field['job']->setTestValue($random_text); + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['job']->theme($view->result[0]); + }); + $this->assertEqual($output, $random_text, 'Make sure the render method rendered the manual set value.'); + } + + /** + * Tests all things related to the query. + */ + public function testQuery() { + // Tests adding additional fields to the query. + $view = Views::getView('test_view'); + $view->initHandlers(); + + $id_field = $view->field['id']; + $id_field->additional_fields['job'] = 'job'; + // Choose also a field alias key which doesn't match to the table field. + $id_field->additional_fields['created_test'] = array('table' => 'views_test_data', 'field' => 'created'); + $view->build(); + + // Make sure the field aliases have the expected value. + $this->assertEqual($id_field->aliases['job'], 'views_test_data_job'); + $this->assertEqual($id_field->aliases['created_test'], 'views_test_data_created'); + + $this->executeView($view); + // Tests the getValue method with and without a field aliases. + foreach ($this->dataSet() as $key => $row) { + $id = $key + 1; + $result = $view->result[$key]; + $this->assertEqual($id_field->getValue($result), $id); + $this->assertEqual($id_field->getValue($result, 'job'), $row['job']); + $this->assertEqual($id_field->getValue($result, 'created_test'), $row['created']); + } + } + + /** + * Asserts that a string is part of another string. + * + * @param string $haystack + * The value to search in. + * @param string $needle + * The value to search for. + * @param string $message + * (optional) A message to display with the assertion. Do not translate + * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed + * variables in the message text, not t(). If left blank, a default message + * will be displayed. + * @param string $group + * (optional) The group this message is in, which is displayed in a column + * in test output. Use 'Debug' to indicate this is debugging output. Do not + * translate this string. Defaults to 'Other'; most tests do not override + * this default. + * + * @return bool + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertSubString($haystack, $needle, $message = '', $group = 'Other') { + return $this->assertTrue(strpos($haystack, $needle) !== FALSE, $message, $group); + } + + /** + * Asserts that a string is not part of another string. + * + * @param string $haystack + * The value to search in. + * @param string $needle + * The value to search for. + * @param string $message + * (optional) A message to display with the assertion. Do not translate + * messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed + * variables in the message text, not t(). If left blank, a default message + * will be displayed. + * @param string $group + * (optional) The group this message is in, which is displayed in a column + * in test output. Use 'Debug' to indicate this is debugging output. Do not + * translate this string. Defaults to 'Other'; most tests do not override + * this default. + * + * @return bool + * TRUE if the assertion succeeded, FALSE otherwise. + */ + protected function assertNotSubString($haystack, $needle, $message = '', $group = 'Other') { + return $this->assertTrue(strpos($haystack, $needle) === FALSE, $message, $group); + } + + /** + * Tests general rewriting of the output. + */ + public function testRewrite() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + + $view = Views::getView('test_view'); + $view->initHandlers(); + $this->executeView($view); + $row = $view->result[0]; + $id_field = $view->field['id']; + + // Don't check the rewrite checkbox, so the text shouldn't appear. + $id_field->options['alter']['text'] = $random_text = $this->randomMachineName(); + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) { + return $id_field->theme($row); + }); + $this->assertNotSubString($output, $random_text); + + $id_field->options['alter']['alter_text'] = TRUE; + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) { + return $id_field->theme($row); + }); + $this->assertSubString($output, $random_text); + } + + /** + * Tests the arguments tokens on field level. + */ + public function testArgumentTokens() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + + $view = Views::getView('test_field_argument_tokens'); + $this->executeView($view, ['{{ { "#pre_render": ["views_test_data_test_pre_render_function"]} }}']); + + $name_field_0 = $view->field['name']; + + // Test the old style tokens. + $name_field_0->options['alter']['alter_text'] = TRUE; + $name_field_0->options['alter']['text'] = '%1 !1'; + + $row = $view->result[0]; + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_0, $row) { + return $name_field_0->advancedRender($row); + }); + + $this->assertFalse(strpos((string) $output, 'views_test_data_test_pre_render_function executed') !== FALSE, 'Ensure that the pre_render function was not executed'); + $this->assertEqual('%1 !1', (string) $output, "Ensure that old style placeholders aren't replaced"); + + // This time use new style tokens but ensure that we still don't allow + // arbitrary code execution. + $name_field_0->options['alter']['alter_text'] = TRUE; + $name_field_0->options['alter']['text'] = '{{ arguments.null }} {{ raw_arguments.null }}'; + + $row = $view->result[0]; + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_0, $row) { + return $name_field_0->advancedRender($row); + }); + + $this->assertFalse(strpos((string) $output, 'views_test_data_test_pre_render_function executed') !== FALSE, 'Ensure that the pre_render function was not executed'); + $this->assertEqual('{{ { "#pre_render": ["views_test_data_test_pre_render_function"]} }} {{ { "#pre_render": ["views_test_data_test_pre_render_function"]} }}', (string) $output, 'Ensure that new style placeholders are replaced'); + } + + /** + * Tests the field tokens, row level and field level. + */ + public function testFieldTokens() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + + $view = Views::getView('test_field_tokens'); + $this->executeView($view); + $name_field_0 = $view->field['name']; + $name_field_1 = $view->field['name_1']; + $name_field_2 = $view->field['name_2']; + $row = $view->result[0]; + + $name_field_0->options['alter']['alter_text'] = TRUE; + $name_field_0->options['alter']['text'] = '{{ name }}'; + + $name_field_1->options['alter']['alter_text'] = TRUE; + $name_field_1->options['alter']['text'] = '{{ name_1 }} {{ name }}'; + + $name_field_2->options['alter']['alter_text'] = TRUE; + $name_field_2->options['alter']['text'] = '{% if name_2|length > 3 %}{{ name_2 }} {{ name_1 }}{% endif %}'; + + foreach ($view->result as $row) { + $expected_output_0 = $row->views_test_data_name; + $expected_output_1 = "$row->views_test_data_name $row->views_test_data_name"; + $expected_output_2 = "$row->views_test_data_name $row->views_test_data_name $row->views_test_data_name"; + + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_0, $row) { + return $name_field_0->advancedRender($row); + }); + $this->assertEqual($output, $expected_output_0, format_string('Test token replacement: "@token" gave "@output"', [ + '@token' => $name_field_0->options['alter']['text'], + '@output' => $output, + ])); + + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_1, $row) { + return $name_field_1->advancedRender($row); + }); + $this->assertEqual($output, $expected_output_1, format_string('Test token replacement: "@token" gave "@output"', [ + '@token' => $name_field_1->options['alter']['text'], + '@output' => $output, + ])); + + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_2, $row) { + return $name_field_2->advancedRender($row); + }); + $this->assertEqual($output, $expected_output_2, format_string('Test token replacement: "@token" gave "@output"', [ + '@token' => $name_field_2->options['alter']['text'], + '@output' => $output, + ])); + } + + $job_field = $view->field['job']; + $job_field->options['alter']['alter_text'] = TRUE; + $job_field->options['alter']['text'] = '{{ job }}'; + + $random_text = $this->randomMachineName(); + $job_field->setTestValue($random_text); + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) { + return $job_field->advancedRender($row); + }); + $this->assertSubString($output, $random_text, format_string('Make sure the self token (@token => @value) appears in the output (@output)', [ + '@value' => $random_text, + '@output' => $output, + '@token' => $job_field->options['alter']['text'], + ])); + + // Verify the token format used in D7 and earlier does not get substituted. + $old_token = '[job]'; + $job_field->options['alter']['text'] = $old_token; + $random_text = $this->randomMachineName(); + $job_field->setTestValue($random_text); + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) { + return $job_field->advancedRender($row); + }); + $this->assertEqual($output, $old_token, format_string('Make sure the old token style (@token => @value) is not changed in the output (@output)', [ + '@value' => $random_text, + '@output' => $output, + '@token' => $job_field->options['alter']['text'], + ])); + + // Verify HTML tags are allowed in rewrite templates while token + // replacements are escaped. + $job_field->options['alter']['text'] = '

                              {{ job }}

                              '; + $random_text = $this->randomMachineName(); + $job_field->setTestValue('' . $random_text . ''); + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) { + return $job_field->advancedRender($row); + }); + $this->assertEqual($output, '

                              <span>' . $random_text . '</span>

                              ', 'Valid tags are allowed in rewrite templates and token replacements.'); + + // Verify '; + $job_field->options['alter']['text'] = $rewrite_template; + $random_text = $this->randomMachineName(); + $job_field->setTestValue($random_text); + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) { + return $job_field->advancedRender($row); + }); + $this->assertNotSubString($output, ''; + $job_field->options['alter']['text'] = $rewrite_template; + $random_text = $this->randomMachineName(); + $job_field->setTestValue($random_text); + $output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) { + return $job_field->advancedRender($row); + }); + $this->assertEqual($output, $random_text, format_string('Make sure a script tag in the template (@template) is removed, leaving only the replaced token in the output (@output)', [ + '@output' => $output, + '@template' => $rewrite_template, + ])); + } + + /** + * Tests the exclude setting. + */ + public function testExclude() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = $this->container->get('renderer'); + $view = Views::getView('test_field_output'); + $view->initHandlers(); + // Hide the field and see whether it's rendered. + $view->field['name']->options['exclude'] = TRUE; + + $output = $view->preview(); + $output = $renderer->renderRoot($output); + foreach ($this->dataSet() as $entry) { + $this->assertNotSubString($output, $entry['name']); + } + + // Show and check the field. + $view->field['name']->options['exclude'] = FALSE; + + $output = $view->preview(); + $output = $renderer->renderRoot($output); + foreach ($this->dataSet() as $entry) { + $this->assertSubString($output, $entry['name']); + } + } + + /** + * Tests everything related to empty output of a field. + */ + function testEmpty() { + $this->_testHideIfEmpty(); + $this->_testEmptyText(); + } + + /** + * Tests the hide if empty functionality. + * + * This tests alters the result to get easier and less coupled results. It is + * important that assertIdentical() is used in this test since in PHP 0 == ''. + */ + function _testHideIfEmpty() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + + $view = Views::getView('test_view'); + $view->initDisplay(); + $this->executeView($view); + + $column_map_reversed = array_flip($this->columnMap); + $view->row_index = 0; + $random_name = $this->randomMachineName(); + $random_value = $this->randomMachineName(); + + // Test when results are not rewritten and empty values are not hidden. + $view->field['name']->options['hide_alter_empty'] = FALSE; + $view->field['name']->options['hide_empty'] = FALSE; + $view->field['name']->options['empty_zero'] = FALSE; + + // Test a valid string. + $view->result[0]->{$column_map_reversed['name']} = $random_name; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $random_name, 'By default, a string should not be treated as empty.'); + + // Test an empty string. + $view->result[0]->{$column_map_reversed['name']} = ""; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical($render, "", 'By default, "" should not be treated as empty.'); + + // Test zero as an integer. + $view->result[0]->{$column_map_reversed['name']} = 0; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, '0', 'By default, 0 should not be treated as empty.'); + + // Test zero as a string. + $view->result[0]->{$column_map_reversed['name']} = "0"; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, "0", 'By default, "0" should not be treated as empty.'); + + // Test when results are not rewritten and non-zero empty values are hidden. + $view->field['name']->options['hide_alter_empty'] = TRUE; + $view->field['name']->options['hide_empty'] = TRUE; + $view->field['name']->options['empty_zero'] = FALSE; + + // Test a valid string. + $view->result[0]->{$column_map_reversed['name']} = $random_name; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $random_name, 'If hide_empty is checked, a string should not be treated as empty.'); + + // Test an empty string. + $view->result[0]->{$column_map_reversed['name']} = ""; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical($render, "", 'If hide_empty is checked, "" should be treated as empty.'); + + // Test zero as an integer. + $view->result[0]->{$column_map_reversed['name']} = 0; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, '0', 'If hide_empty is checked, but not empty_zero, 0 should not be treated as empty.'); + + // Test zero as a string. + $view->result[0]->{$column_map_reversed['name']} = "0"; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, "0", 'If hide_empty is checked, but not empty_zero, "0" should not be treated as empty.'); + + // Test when results are not rewritten and all empty values are hidden. + $view->field['name']->options['hide_alter_empty'] = TRUE; + $view->field['name']->options['hide_empty'] = TRUE; + $view->field['name']->options['empty_zero'] = TRUE; + + // Test zero as an integer. + $view->result[0]->{$column_map_reversed['name']} = 0; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical($render, "", 'If hide_empty and empty_zero are checked, 0 should be treated as empty.'); + + // Test zero as a string. + $view->result[0]->{$column_map_reversed['name']} = "0"; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical($render, "", 'If hide_empty and empty_zero are checked, "0" should be treated as empty.'); + + // Test when results are rewritten to a valid string and non-zero empty + // results are hidden. + $view->field['name']->options['hide_alter_empty'] = FALSE; + $view->field['name']->options['hide_empty'] = TRUE; + $view->field['name']->options['empty_zero'] = FALSE; + $view->field['name']->options['alter']['alter_text'] = TRUE; + $view->field['name']->options['alter']['text'] = $random_name; + + // Test a valid string. + $view->result[0]->{$column_map_reversed['name']} = $random_value; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $random_name, 'If the rewritten string is not empty, it should not be treated as empty.'); + + // Test an empty string. + $view->result[0]->{$column_map_reversed['name']} = ""; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $random_name, 'If the rewritten string is not empty, "" should not be treated as empty.'); + + // Test zero as an integer. + $view->result[0]->{$column_map_reversed['name']} = 0; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $random_name, 'If the rewritten string is not empty, 0 should not be treated as empty.'); + + // Test zero as a string. + $view->result[0]->{$column_map_reversed['name']} = "0"; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $random_name, 'If the rewritten string is not empty, "0" should not be treated as empty.'); + + // Test when results are rewritten to an empty string and non-zero empty results are hidden. + $view->field['name']->options['hide_alter_empty'] = TRUE; + $view->field['name']->options['hide_empty'] = TRUE; + $view->field['name']->options['empty_zero'] = FALSE; + $view->field['name']->options['alter']['alter_text'] = TRUE; + $view->field['name']->options['alter']['text'] = ""; + + // Test a valid string. + $view->result[0]->{$column_map_reversed['name']} = $random_name; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $random_name, 'If the rewritten string is empty, it should not be treated as empty.'); + + // Test an empty string. + $view->result[0]->{$column_map_reversed['name']} = ""; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical($render, "", 'If the rewritten string is empty, "" should be treated as empty.'); + + // Test zero as an integer. + $view->result[0]->{$column_map_reversed['name']} = 0; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, '0', 'If the rewritten string is empty, 0 should not be treated as empty.'); + + // Test zero as a string. + $view->result[0]->{$column_map_reversed['name']} = "0"; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, "0", 'If the rewritten string is empty, "0" should not be treated as empty.'); + + // Test when results are rewritten to zero as a string and non-zero empty + // results are hidden. + $view->field['name']->options['hide_alter_empty'] = FALSE; + $view->field['name']->options['hide_empty'] = TRUE; + $view->field['name']->options['empty_zero'] = FALSE; + $view->field['name']->options['alter']['alter_text'] = TRUE; + $view->field['name']->options['alter']['text'] = "0"; + + // Test a valid string. + $view->result[0]->{$column_map_reversed['name']} = $random_name; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, "0", 'If the rewritten string is zero and empty_zero is not checked, the string rewritten as 0 should not be treated as empty.'); + + // Test an empty string. + $view->result[0]->{$column_map_reversed['name']} = ""; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, "0", 'If the rewritten string is zero and empty_zero is not checked, "" rewritten as 0 should not be treated as empty.'); + + // Test zero as an integer. + $view->result[0]->{$column_map_reversed['name']} = 0; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, "0", 'If the rewritten string is zero and empty_zero is not checked, 0 should not be treated as empty.'); + + // Test zero as a string. + $view->result[0]->{$column_map_reversed['name']} = "0"; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, "0", 'If the rewritten string is zero and empty_zero is not checked, "0" should not be treated as empty.'); + + // Test when results are rewritten to a valid string and non-zero empty + // results are hidden. + $view->field['name']->options['hide_alter_empty'] = TRUE; + $view->field['name']->options['hide_empty'] = TRUE; + $view->field['name']->options['empty_zero'] = FALSE; + $view->field['name']->options['alter']['alter_text'] = TRUE; + $view->field['name']->options['alter']['text'] = $random_value; + + // Test a valid string. + $view->result[0]->{$column_map_reversed['name']} = $random_name; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $random_value, 'If the original and rewritten strings are valid, it should not be treated as empty.'); + + // Test an empty string. + $view->result[0]->{$column_map_reversed['name']} = ""; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical($render, "", 'If either the original or rewritten string is invalid, "" should be treated as empty.'); + + // Test zero as an integer. + $view->result[0]->{$column_map_reversed['name']} = 0; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $random_value, 'If the original and rewritten strings are valid, 0 should not be treated as empty.'); + + // Test zero as a string. + $view->result[0]->{$column_map_reversed['name']} = "0"; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $random_value, 'If the original and rewritten strings are valid, "0" should not be treated as empty.'); + + // Test when results are rewritten to zero as a string and all empty + // original values and results are hidden. + $view->field['name']->options['hide_alter_empty'] = TRUE; + $view->field['name']->options['hide_empty'] = TRUE; + $view->field['name']->options['empty_zero'] = TRUE; + $view->field['name']->options['alter']['alter_text'] = TRUE; + $view->field['name']->options['alter']['text'] = "0"; + + // Test a valid string. + $view->result[0]->{$column_map_reversed['name']} = $random_name; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, "", 'If the rewritten string is zero, it should be treated as empty.'); + + // Test an empty string. + $view->result[0]->{$column_map_reversed['name']} = ""; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical($render, "", 'If the rewritten string is zero, "" should be treated as empty.'); + + // Test zero as an integer. + $view->result[0]->{$column_map_reversed['name']} = 0; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical($render, "", 'If the rewritten string is zero, 0 should not be treated as empty.'); + + // Test zero as a string. + $view->result[0]->{$column_map_reversed['name']} = "0"; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical($render, "", 'If the rewritten string is zero, "0" should not be treated as empty.'); + } + + /** + * Tests the usage of the empty text. + */ + function _testEmptyText() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + + $view = Views::getView('test_view'); + $view->initDisplay(); + $this->executeView($view); + + $column_map_reversed = array_flip($this->columnMap); + $view->row_index = 0; + + $empty_text = $view->field['name']->options['empty'] = $this->randomMachineName(); + $view->result[0]->{$column_map_reversed['name']} = ""; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $empty_text, 'If a field is empty, the empty text should be used for the output.'); + + $view->result[0]->{$column_map_reversed['name']} = "0"; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, "0", 'If a field is 0 and empty_zero is not checked, the empty text should not be used for the output.'); + + $view->result[0]->{$column_map_reversed['name']} = "0"; + $view->field['name']->options['empty_zero'] = TRUE; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $empty_text, 'If a field is 0 and empty_zero is checked, the empty text should be used for the output.'); + + $view->result[0]->{$column_map_reversed['name']} = ""; + $view->field['name']->options['alter']['alter_text'] = TRUE; + $alter_text = $view->field['name']->options['alter']['text'] = $this->randomMachineName(); + $view->field['name']->options['hide_alter_empty'] = FALSE; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $alter_text, 'If a field is empty, some rewrite text exists, but hide_alter_empty is not checked, render the rewrite text.'); + + $view->field['name']->options['hide_alter_empty'] = TRUE; + $render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) { + return $view->field['name']->advancedRender($view->result[0]); + }); + $this->assertIdentical((string) $render, $empty_text, 'If a field is empty, some rewrite text exists, and hide_alter_empty is checked, use the empty text.'); + } + + /** + * Tests views_handler_field::isValueEmpty(). + */ + function testIsValueEmpty() { + $view = Views::getView('test_view'); + $view->initHandlers(); + $field = $view->field['name']; + + $this->assertFalse($field->isValueEmpty("not empty", TRUE), 'A normal string is not empty.'); + $this->assertTrue($field->isValueEmpty("not empty", TRUE, FALSE), 'A normal string which skips empty() can be seen as empty.'); + + $this->assertTrue($field->isValueEmpty("", TRUE), '"" is considered as empty.'); + + $this->assertTrue($field->isValueEmpty('0', TRUE), '"0" is considered as empty if empty_zero is TRUE.'); + $this->assertTrue($field->isValueEmpty(0, TRUE), '0 is considered as empty if empty_zero is TRUE.'); + $this->assertFalse($field->isValueEmpty('0', FALSE), '"0" is considered not as empty if empty_zero is FALSE.'); + $this->assertFalse($field->isValueEmpty(0, FALSE), '0 is considered not as empty if empty_zero is FALSE.'); + + $this->assertTrue($field->isValueEmpty(NULL, TRUE, TRUE), 'Null should be always seen as empty, regardless of no_skip_empty.'); + $this->assertTrue($field->isValueEmpty(NULL, TRUE, FALSE), 'Null should be always seen as empty, regardless of no_skip_empty.'); + } + + /** + * Tests whether the filters are click sortable as expected. + */ + public function testClickSortable() { + // Test that clickSortable is TRUE by default. + $item = array( + 'table' => 'views_test_data', + 'field' => 'name', + ); + $plugin = $this->container->get('plugin.manager.views.field')->getHandler($item); + $this->assertTrue($plugin->clickSortable(), 'TRUE as a default value is correct.'); + + // Test that clickSortable is TRUE by when set TRUE in the data. + $item['field'] = 'id'; + $plugin = $this->container->get('plugin.manager.views.field')->getHandler($item); + $this->assertTrue($plugin->clickSortable(), 'TRUE as a views data value is correct.'); + + // Test that clickSortable is FALSE by when set FALSE in the data. + $item['field'] = 'job'; + $plugin = $this->container->get('plugin.manager.views.field')->getHandler($item); + $this->assertFalse($plugin->clickSortable(), 'FALSE as a views data value is correct.'); + } + + /** + * Tests the trimText method. + */ + public function testTrimText() { + // Test unicode. See https://www.drupal.org/node/513396#comment-2839416. + $text = array( + 'Tuy nhiên, những hi vọng', + 'Giả sử chúng tôi có 3 Apple', + 'siêu nhỏ này là bộ xử lý', + 'Di động của nhà sản xuất Phần Lan', + 'khoảng cách từ đại lí đến', + 'của hãng bao gồm ba dòng', + 'сд асд асд ас', + 'асд асд асд ас' + ); + // Just test maxlength without word boundary. + $alter = array( + 'max_length' => 10, + ); + $expect = array( + 'Tuy nhiên,', + 'Giả sử chú', + 'siêu nhỏ n', + 'Di động củ', + 'khoảng các', + 'của hãng b', + 'сд асд асд', + 'асд асд ас', + ); + + foreach ($text as $key => $line) { + $result_text = FieldPluginBase::trimText($alter, $line); + $this->assertEqual($result_text, $expect[$key]); + } + + // Test also word_boundary + $alter['word_boundary'] = TRUE; + $expect = array( + 'Tuy nhiên', + 'Giả sử', + 'siêu nhỏ', + 'Di động', + 'khoảng', + 'của hãng', + 'сд асд', + 'асд асд', + ); + + foreach ($text as $key => $line) { + $result_text = FieldPluginBase::trimText($alter, $line); + $this->assertEqual($result_text, $expect[$key]); + } + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldRenderedEntityTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldRenderedEntityTest.php new file mode 100644 index 0000000..88dca35 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FieldRenderedEntityTest.php @@ -0,0 +1,200 @@ +installEntitySchema('user'); + $this->installEntitySchema('entity_test'); + $this->installConfig(['entity_test']); + + EntityViewMode::create([ + 'id' => 'entity_test.foobar', + 'targetEntityType' => 'entity_test', + 'status' => TRUE, + 'enabled' => TRUE, + 'label' => 'My view mode', + ])->save(); + + $display = EntityViewDisplay::create([ + 'targetEntityType' => 'entity_test', + 'bundle' => 'entity_test', + 'mode' => 'foobar', + 'label' => 'My view mode', + 'status' => TRUE, + ]); + $display->save(); + + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'test_field', + 'entity_type' => 'entity_test', + 'type' => 'string', + ]); + $field_storage->save(); + + $field_config = FieldConfig::create([ + 'field_name' => 'test_field', + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + ]); + $field_config->save(); + + // Create some test entities. + for ($i = 1; $i <= 3; $i++) { + EntityTest::create([ + 'name' => "Article title $i", + 'test_field' => "Test $i", + ])->save(); + } + + $this->user = User::create([ + 'name' => 'test user', + ]); + $this->user->save(); + + parent::setUpFixtures(); + } + + /** + * Tests the default rendered entity output. + */ + public function testRenderedEntityWithoutField() { + \Drupal::currentUser()->setAccount($this->user); + + EntityViewDisplay::load('entity_test.entity_test.foobar') + ->removeComponent('test_field') + ->save(); + + $view = Views::getView('test_field_entity_test_rendered'); + $build = [ + '#type' => 'view', + '#name' => 'test_field_entity_test_rendered', + '#view' => $view, + '#display_id' => 'default', + ]; + $renderer = \Drupal::service('renderer'); + $renderer->renderPlain($build); + for ($i = 1; $i <= 3; $i++) { + $view_field = $view->style_plugin->getField($i - 1, 'rendered_entity'); + $search_result = strpos($view_field, "Test $i") !== FALSE; + $this->assertFalse($search_result, "The text 'Test $i' not found in the view."); + } + + $this->assertConfigDependencies($view->storage); + $this->assertCacheabilityMetadata($build); + } + + /** + * Ensures that the expected cacheability metadata is applied. + * + * @param array $build + * The render array + */ + protected function assertCacheabilityMetadata($build) { + $this->assertEqual([ + 'config:core.entity_view_display.entity_test.entity_test.foobar', + 'config:views.view.test_field_entity_test_rendered', + 'entity_test:1', + 'entity_test:2', + 'entity_test:3', + 'entity_test_list', + 'entity_test_view', + ], $build['#cache']['tags']); + + $this->assertEqual([ + 'entity_test_view_grants', + 'languages:language_interface', + 'theme', + 'url.query_args', + 'user.permissions', + ], $build['#cache']['contexts']); + } + + /** + * Ensures that the config dependencies are calculated the right way. + * + * @param \Drupal\views\Entity\View $storage + */ + protected function assertConfigDependencies(View $storage) { + $storage->calculateDependencies(); + $this->assertEqual([ + 'config' => ['core.entity_view_mode.entity_test.foobar'], + 'module' => ['entity_test'], + ], $storage->getDependencies()); + } + + /** + * Tests the rendered entity output with the test field configured to show. + */ + public function testRenderedEntityWithField() { + \Drupal::currentUser()->setAccount($this->user); + + // Show the test_field on the entity_test.entity_test.foobar view display. + EntityViewDisplay::load('entity_test.entity_test.foobar')->setComponent('test_field', ['type' => 'string', 'label' => 'above'])->save(); + + $view = Views::getView('test_field_entity_test_rendered'); + $build = [ + '#type' => 'view', + '#name' => 'test_field_entity_test_rendered', + '#view' => $view, + '#display_id' => 'default', + ]; + + $renderer = \Drupal::service('renderer'); + $renderer->renderPlain($build); + for ($i = 1; $i <= 3; $i++) { + $view_field = $view->style_plugin->getField($i - 1, 'rendered_entity'); + $search_result = strpos($view_field, "Test $i") !== FALSE; + $this->assertTrue($search_result, "The text 'Test $i' found in the view."); + } + + $this->assertConfigDependencies($view->storage); + $this->assertCacheabilityMetadata($build); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FieldUrlTest.php b/core/modules/views/tests/src/Kernel/Handler/FieldUrlTest.php new file mode 100644 index 0000000..494d939 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FieldUrlTest.php @@ -0,0 +1,72 @@ +setDisplay(); + + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'display_as_link' => FALSE, + ), + )); + + $this->executeView($view); + + $this->assertEqual('John', $view->field['name']->advancedRender($view->result[0])); + + // Make the url a link. + $view->destroy(); + $view->setDisplay(); + + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + ), + )); + + $this->executeView($view); + + $this->assertEqual(\Drupal::l('John', Url::fromUri('base:John'))->getGeneratedLink(), $view->field['name']->advancedRender($view->result[0])); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FilterBooleanOperatorStringTest.php b/core/modules/views/tests/src/Kernel/Handler/FilterBooleanOperatorStringTest.php new file mode 100644 index 0000000..a334677 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FilterBooleanOperatorStringTest.php @@ -0,0 +1,221 @@ + 'id', + ); + + /** + * {@inheritdoc} + */ + protected function schemaDefinition() { + $schema = parent::schemaDefinition(); + + $schema['views_test_data']['fields']['status'] = array( + 'description' => 'The status of this record', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ); + + return $schema; + } + + /** + * {@inheritdoc} + */ + protected function viewsData() { + $views_data = parent::viewsData(); + + $views_data['views_test_data']['status']['filter']['id'] = 'boolean_string'; + + return $views_data; + } + + /** + * {@inheritdoc} + */ + protected function dataSet() { + $data = parent::dataSet(); + + foreach ($data as &$row) { + if ($row['status']) { + $row['status'] = 'Enabled'; + } + else { + $row['status'] = ''; + } + } + + return $data; + } + + /** + * Tests the BooleanOperatorString filter. + */ + public function testFilterBooleanOperatorString() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Add a the status boolean filter. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'status' => array( + 'id' => 'status', + 'field' => 'status', + 'table' => 'views_test_data', + 'value' => 0, + ), + )); + $this->executeView($view); + + $expected_result = array( + array('id' => 2), + array('id' => 4), + ); + + $this->assertEqual(2, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + + $view->destroy(); + $view->setDisplay(); + + // Add the status boolean filter. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'status' => array( + 'id' => 'status', + 'field' => 'status', + 'table' => 'views_test_data', + 'value' => 1, + ), + )); + $this->executeView($view); + + $expected_result = array( + array('id' => 1), + array('id' => 3), + array('id' => 5), + ); + + $this->assertEqual(3, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + } + + /** + * Tests the Boolean filter with grouped exposed form enabled. + */ + public function testFilterGroupedExposed() { + $filters = $this->getGroupedExposedFilters(); + $view = Views::getView('test_view'); + + $view->setExposedInput(array('status' => 1)); + $view->setDisplay(); + $view->displayHandlers->get('default')->overrideOption('filters', $filters); + + $this->executeView($view); + + $expected_result = array( + array('id' => 1), + array('id' => 3), + array('id' => 5), + ); + + $this->assertEqual(3, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + $view->destroy(); + + $view->setExposedInput(array('status' => 2)); + $view->setDisplay(); + $view->displayHandlers->get('default')->overrideOption('filters', $filters); + + $this->executeView($view); + + $expected_result = array( + array('id' => 2), + array('id' => 4), + ); + + $this->assertEqual(2, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + } + + /** + * Provides grouped exposed filter configuration. + * + * @return array + * Returns the filter configuration for exposed filters. + */ + protected function getGroupedExposedFilters() { + $filters = array( + 'status' => array( + 'id' => 'status', + 'table' => 'views_test_data', + 'field' => 'status', + 'relationship' => 'none', + 'exposed' => TRUE, + 'expose' => array( + 'operator' => 'status_op', + 'label' => 'status', + 'identifier' => 'status', + ), + 'is_grouped' => TRUE, + 'group_info' => array( + 'label' => 'status', + 'identifier' => 'status', + 'default_group' => 'All', + 'group_items' => array( + 1 => array( + 'title' => 'Active', + 'operator' => '=', + 'value' => '1', + ), + 2 => array( + 'title' => 'Blocked', + 'operator' => '=', + 'value' => '0', + ), + ), + ), + ), + ); + return $filters; + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FilterBooleanOperatorTest.php b/core/modules/views/tests/src/Kernel/Handler/FilterBooleanOperatorTest.php new file mode 100644 index 0000000..1f58fe9 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FilterBooleanOperatorTest.php @@ -0,0 +1,223 @@ + 'id', + ); + + /** + * Tests the BooleanOperator filter. + */ + public function testFilterBooleanOperator() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Add a the status boolean filter. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'status' => array( + 'id' => 'status', + 'field' => 'status', + 'table' => 'views_test_data', + 'value' => 0, + ), + )); + $this->executeView($view); + + $expected_result = array( + array('id' => 2), + array('id' => 4), + ); + + $this->assertEqual(2, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + + $view->destroy(); + $view->setDisplay(); + + // Add the status boolean filter. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'status' => array( + 'id' => 'status', + 'field' => 'status', + 'table' => 'views_test_data', + 'value' => 1, + ), + )); + $this->executeView($view); + + $expected_result = array( + array('id' => 1), + array('id' => 3), + array('id' => 5), + ); + + $this->assertEqual(3, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + + $view->destroy(); + $view->setDisplay(); + + // Testing the same scenario but using the reverse status and operation. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'status' => array( + 'id' => 'status', + 'field' => 'status', + 'table' => 'views_test_data', + 'value' => 0, + 'operator' => '!=', + ), + )); + $this->executeView($view); + + $expected_result = array( + array('id' => 1), + array('id' => 3), + array('id' => 5), + ); + + $this->assertEqual(3, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + } + + /** + * Tests the boolean filter with grouped exposed form enabled. + */ + public function testFilterGroupedExposed() { + $filters = $this->getGroupedExposedFilters(); + $view = Views::getView('test_view'); + + $view->setExposedInput(array('status' => 1)); + $view->setDisplay(); + $view->displayHandlers->get('default')->overrideOption('filters', $filters); + + $this->executeView($view); + + $expected_result = array( + array('id' => 1), + array('id' => 3), + array('id' => 5), + ); + + $this->assertEqual(3, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + $view->destroy(); + + $view->setExposedInput(array('status' => 2)); + $view->setDisplay(); + $view->displayHandlers->get('default')->overrideOption('filters', $filters); + + $this->executeView($view); + + $expected_result = array( + array('id' => 2), + array('id' => 4), + ); + + $this->assertEqual(2, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + + $view->destroy(); + + // Expecting the same results as for ['status' => 1]. + $view->setExposedInput(['status' => 3]); + $view->setDisplay(); + $view->displayHandlers->get('default')->overrideOption('filters', $filters); + + $this->executeView($view); + + $expected_result = array( + array('id' => 1), + array('id' => 3), + array('id' => 5), + ); + + $this->assertEqual(3, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + } + + /** + * Provides grouped exposed filter configuration. + * + * @return array + */ + protected function getGroupedExposedFilters() { + $filters = array( + 'status' => array( + 'id' => 'status', + 'table' => 'views_test_data', + 'field' => 'status', + 'relationship' => 'none', + 'exposed' => TRUE, + 'expose' => array( + 'operator' => 'status_op', + 'label' => 'status', + 'identifier' => 'status', + ), + 'is_grouped' => TRUE, + 'group_info' => array( + 'label' => 'status', + 'identifier' => 'status', + 'default_group' => 'All', + 'group_items' => array( + 1 => array( + 'title' => 'Active', + 'operator' => '=', + 'value' => '1', + ), + 2 => array( + 'title' => 'Blocked', + 'operator' => '=', + 'value' => '0', + ), + // This group should return the same results as group 1, because it + // is the negation of group 2. + 3 => array( + 'title' => 'Active (reverse)', + 'operator' => '!=', + 'value' => '0', + ), + ), + ), + ), + ); + return $filters; + } + +} + diff --git a/core/modules/views/tests/src/Kernel/Handler/FilterCombineTest.php b/core/modules/views/tests/src/Kernel/Handler/FilterCombineTest.php new file mode 100644 index 0000000..66b3a8b --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FilterCombineTest.php @@ -0,0 +1,161 @@ + 'name', + 'views_test_data_job' => 'job', + ); + + public function testFilterCombineContains() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + $fields = $view->displayHandlers->get('default')->getOption('fields'); + $view->displayHandlers->get('default')->overrideOption('fields', $fields + array( + 'job' => array( + 'id' => 'job', + 'table' => 'views_test_data', + 'field' => 'job', + 'relationship' => 'none', + ), + )); + + // Change the filtering. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'age' => array( + 'id' => 'combine', + 'table' => 'views', + 'field' => 'combine', + 'relationship' => 'none', + 'operator' => 'contains', + 'fields' => array( + 'name', + 'job', + ), + 'value' => 'ing', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + 'job' => 'Singer', + ), + array( + 'name' => 'George', + 'job' => 'Singer', + ), + array( + 'name' => 'Ringo', + 'job' => 'Drummer', + ), + array( + 'name' => 'Ginger', + 'job' => NULL, + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + /** + * Tests if the filter can handle removed fields. + * + * Tests the combined filter handler when a field overwrite is done + * and fields set in the combine filter are removed from the display + * but not from the combined filter settings. + */ + public function testFilterCombineContainsFieldsOverwritten() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + $fields = $view->displayHandlers->get('default')->getOption('fields'); + $view->displayHandlers->get('default')->overrideOption('fields', $fields + array( + 'job' => array( + 'id' => 'job', + 'table' => 'views_test_data', + 'field' => 'job', + 'relationship' => 'none', + ), + )); + + // Change the filtering. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'age' => array( + 'id' => 'combine', + 'table' => 'views', + 'field' => 'combine', + 'relationship' => 'none', + 'operator' => 'contains', + 'fields' => array( + 'name', + 'job', + // Add a dummy field to the combined fields to simulate + // a removed or deleted field. + 'dummy', + ), + 'value' => 'ing', + ), + )); + + $this->executeView($view); + // Make sure this view will not get displayed. + $this->assertTrue($view->build_info['fail'], "View build has been marked as failed."); + // Make sure this view does not pass validation with the right error. + $errors = $view->validate(); + $this->assertEqual(reset($errors['default']), t('Field %field set in %filter is not set in this display.', array('%field' => 'dummy', '%filter' => 'Global: Combine fields filter'))); + } + + /** + * Additional data to test the NULL issue. + */ + protected function dataSet() { + $data_set = parent::dataSet(); + $data_set[] = array( + 'name' => 'Ginger', + 'age' => 25, + 'job' => NULL, + 'created' => gmmktime(0, 0, 0, 1, 2, 2000), + 'status' => 1, + ); + return $data_set; + } + + /** + * Allow {views_test_data}.job to be NULL. + */ + protected function schemaDefinition() { + $schema = parent::schemaDefinition(); + unset($schema['views_test_data']['fields']['job']['not null']); + return $schema; + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FilterEqualityTest.php b/core/modules/views/tests/src/Kernel/Handler/FilterEqualityTest.php new file mode 100644 index 0000000..cd97231 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FilterEqualityTest.php @@ -0,0 +1,193 @@ + 'name', + ); + + function viewsData() { + $data = parent::viewsData(); + $data['views_test_data']['name']['filter']['id'] = 'equality'; + return $data; + } + + function testEqual() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'operator' => '=', + 'value' => 'Ringo', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'Ringo', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + public function testEqualGroupedExposed() { + $filters = $this->getGroupedExposedFilters(); + $view = Views::getView('test_view'); + $view->newDisplay('page', 'Page', 'page_1'); + + // Filter: Name, Operator: =, Value: Ringo + $filters['name']['group_info']['default_group'] = 1; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'Ringo', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testNotEqual() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'operator' => '!=', + 'value' => 'Ringo', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + ), + array( + 'name' => 'George', + ), + array( + 'name' => 'Paul', + ), + array( + 'name' => 'Meredith', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + public function testEqualGroupedNotExposed() { + $filters = $this->getGroupedExposedFilters(); + $view = Views::getView('test_view'); + $view->newDisplay('page', 'Page', 'page_1'); + + // Filter: Name, Operator: !=, Value: Ringo + $filters['name']['group_info']['default_group'] = 2; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + ), + array( + 'name' => 'George', + ), + array( + 'name' => 'Paul', + ), + array( + 'name' => 'Meredith', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + + protected function getGroupedExposedFilters() { + $filters = array( + 'name' => array( + 'id' => 'name', + 'plugin_id' => 'equality', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'group' => 1, + 'exposed' => TRUE, + 'expose' => array( + 'operator' => 'name_op', + 'label' => 'name', + 'identifier' => 'name', + ), + 'is_grouped' => TRUE, + 'group_info' => array( + 'label' => 'name', + 'identifier' => 'name', + 'default_group' => 'All', + 'group_items' => array( + 1 => array( + 'title' => 'Name is equal to Ringo', + 'operator' => '=', + 'value' => 'Ringo', + ), + 2 => array( + 'title' => 'Name is not equal to Ringo', + 'operator' => '!=', + 'value' => 'Ringo', + ), + ), + ), + ), + ); + return $filters; + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FilterInOperatorTest.php b/core/modules/views/tests/src/Kernel/Handler/FilterInOperatorTest.php new file mode 100644 index 0000000..92c2984 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FilterInOperatorTest.php @@ -0,0 +1,203 @@ + 'name', + 'views_test_data_age' => 'age', + ); + + function viewsData() { + $data = parent::viewsData(); + $data['views_test_data']['age']['filter']['id'] = 'in_operator'; + return $data; + } + + public function testFilterInOperatorSimple() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Add a in_operator ordering. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'age' => array( + 'id' => 'age', + 'field' => 'age', + 'table' => 'views_test_data', + 'value' => array(26, 30), + 'operator' => 'in', + ), + )); + + $this->executeView($view); + + $expected_result = array( + array( + 'name' => 'Paul', + 'age' => 26, + ), + array( + 'name' => 'Meredith', + 'age' => 30, + ), + ); + + $this->assertEqual(2, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + + $view->destroy(); + $view->setDisplay(); + + // Add a in_operator ordering. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'age' => array( + 'id' => 'age', + 'field' => 'age', + 'table' => 'views_test_data', + 'value' => array(26, 30), + 'operator' => 'not in', + ), + )); + + $this->executeView($view); + + $expected_result = array( + array( + 'name' => 'John', + 'age' => 25, + ), + array( + 'name' => 'George', + 'age' => 27, + ), + array( + 'name' => 'Ringo', + 'age' => 28, + ), + ); + + $this->assertEqual(3, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + } + + public function testFilterInOperatorGroupedExposedSimple() { + $filters = $this->getGroupedExposedFilters(); + $view = Views::getView('test_view'); + + // Filter: Age, Operator: in, Value: 26, 30 + $filters['age']['group_info']['default_group'] = 1; + $view->setDisplay(); + $view->displayHandlers->get('default')->overrideOption('filters', $filters); + + $this->executeView($view); + + $expected_result = array( + array( + 'name' => 'Paul', + 'age' => 26, + ), + array( + 'name' => 'Meredith', + 'age' => 30, + ), + ); + + $this->assertEqual(2, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + } + + public function testFilterNotInOperatorGroupedExposedSimple() { + $filters = $this->getGroupedExposedFilters(); + $view = Views::getView('test_view'); + + // Filter: Age, Operator: in, Value: 26, 30 + $filters['age']['group_info']['default_group'] = 2; + $view->setDisplay(); + $view->displayHandlers->get('default')->overrideOption('filters', $filters); + + $this->executeView($view); + + $expected_result = array( + array( + 'name' => 'John', + 'age' => 25, + ), + array( + 'name' => 'George', + 'age' => 27, + ), + array( + 'name' => 'Ringo', + 'age' => 28, + ), + ); + + $this->assertEqual(3, count($view->result)); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + } + + protected function getGroupedExposedFilters() { + $filters = array( + 'age' => array( + 'id' => 'age', + 'table' => 'views_test_data', + 'field' => 'age', + 'relationship' => 'none', + 'exposed' => TRUE, + 'expose' => array( + 'operator' => 'age_op', + 'label' => 'age', + 'identifier' => 'age', + ), + 'is_grouped' => TRUE, + 'group_info' => array( + 'label' => 'age', + 'identifier' => 'age', + 'default_group' => 'All', + 'group_items' => array( + 1 => array( + 'title' => 'Age is one of 26, 30', + 'operator' => 'in', + 'value' => array(26, 30), + ), + 2 => array( + 'title' => 'Age is not one of 26, 30', + 'operator' => 'not in', + 'value' => array(26, 30), + ), + ), + ), + ), + ); + return $filters; + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FilterNumericTest.php b/core/modules/views/tests/src/Kernel/Handler/FilterNumericTest.php new file mode 100644 index 0000000..40ab4f9 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FilterNumericTest.php @@ -0,0 +1,430 @@ + 'name', + 'views_test_data_age' => 'age', + ); + + function viewsData() { + $data = parent::viewsData(); + $data['views_test_data']['age']['filter']['allow empty'] = TRUE; + $data['views_test_data']['id']['filter']['allow empty'] = FALSE; + + return $data; + } + + public function testFilterNumericSimple() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'age' => array( + 'id' => 'age', + 'table' => 'views_test_data', + 'field' => 'age', + 'relationship' => 'none', + 'operator' => '=', + 'value' => array('value' => 28), + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'Ringo', + 'age' => 28, + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + public function testFilterNumericExposedGroupedSimple() { + $filters = $this->getGroupedExposedFilters(); + $view = Views::getView('test_view'); + $view->newDisplay('page', 'Page', 'page_1'); + + // Filter: Age, Operator: =, Value: 28 + $filters['age']['group_info']['default_group'] = 1; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'Ringo', + 'age' => 28, + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + public function testFilterNumericBetween() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'age' => array( + 'id' => 'age', + 'table' => 'views_test_data', + 'field' => 'age', + 'relationship' => 'none', + 'operator' => 'between', + 'value' => array( + 'min' => 26, + 'max' => 29, + ), + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'George', + 'age' => 27, + ), + array( + 'name' => 'Ringo', + 'age' => 28, + ), + array( + 'name' => 'Paul', + 'age' => 26, + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + + // test not between + $view->destroy(); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'age' => array( + 'id' => 'age', + 'table' => 'views_test_data', + 'field' => 'age', + 'relationship' => 'none', + 'operator' => 'not between', + 'value' => array( + 'min' => 26, + 'max' => 29, + ), + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + 'age' => 25, + ), + array( + 'name' => 'Paul', + 'age' => 26, + ), + array( + 'name' => 'Meredith', + 'age' => 30, + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + public function testFilterNumericExposedGroupedBetween() { + $filters = $this->getGroupedExposedFilters(); + $view = Views::getView('test_view'); + $view->newDisplay('page', 'Page', 'page_1'); + + // Filter: Age, Operator: between, Value: 26 and 29 + $filters['age']['group_info']['default_group'] = 2; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'George', + 'age' => 27, + ), + array( + 'name' => 'Ringo', + 'age' => 28, + ), + array( + 'name' => 'Paul', + 'age' => 26, + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + public function testFilterNumericExposedGroupedNotBetween() { + $filters = $this->getGroupedExposedFilters(); + $view = Views::getView('test_view'); + $view->newDisplay('page', 'Page', 'page_1'); + + // Filter: Age, Operator: between, Value: 26 and 29 + $filters['age']['group_info']['default_group'] = 3; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + 'age' => 25, + ), + array( + 'name' => 'Paul', + 'age' => 26, + ), + array( + 'name' => 'Meredith', + 'age' => 30, + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + public function testFilterNumericEmpty() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'age' => array( + 'id' => 'age', + 'table' => 'views_test_data', + 'field' => 'age', + 'relationship' => 'none', + 'operator' => 'empty', + ), + )); + + $this->executeView($view); + $resultset = array( + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + + $view->destroy(); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'age' => array( + 'id' => 'age', + 'table' => 'views_test_data', + 'field' => 'age', + 'relationship' => 'none', + 'operator' => 'not empty', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + 'age' => 25, + ), + array( + 'name' => 'George', + 'age' => 27, + ), + array( + 'name' => 'Ringo', + 'age' => 28, + ), + array( + 'name' => 'Paul', + 'age' => 26, + ), + array( + 'name' => 'Meredith', + 'age' => 30, + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + public function testFilterNumericExposedGroupedEmpty() { + $filters = $this->getGroupedExposedFilters(); + $view = Views::getView('test_view'); + $view->newDisplay('page', 'Page', 'page_1'); + + // Filter: Age, Operator: empty, Value: + $filters['age']['group_info']['default_group'] = 4; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + $resultset = array( + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + public function testFilterNumericExposedGroupedNotEmpty() { + $filters = $this->getGroupedExposedFilters(); + $view = Views::getView('test_view'); + $view->newDisplay('page', 'Page', 'page_1'); + + // Filter: Age, Operator: empty, Value: + $filters['age']['group_info']['default_group'] = 5; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + 'age' => 25, + ), + array( + 'name' => 'George', + 'age' => 27, + ), + array( + 'name' => 'Ringo', + 'age' => 28, + ), + array( + 'name' => 'Paul', + 'age' => 26, + ), + array( + 'name' => 'Meredith', + 'age' => 30, + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + public function testAllowEmpty() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'id' => array( + 'id' => 'id', + 'table' => 'views_test_data', + 'field' => 'id', + 'relationship' => 'none', + ), + 'age' => array( + 'id' => 'age', + 'table' => 'views_test_data', + 'field' => 'age', + 'relationship' => 'none', + ), + )); + + $view->initHandlers(); + + $id_operators = $view->filter['id']->operators(); + $age_operators = $view->filter['age']->operators(); + + $this->assertFalse(isset($id_operators['empty'])); + $this->assertFalse(isset($id_operators['not empty'])); + $this->assertTrue(isset($age_operators['empty'])); + $this->assertTrue(isset($age_operators['not empty'])); + } + + protected function getGroupedExposedFilters() { + $filters = array( + 'age' => array( + 'id' => 'age', + 'plugin_id' => 'numeric', + 'table' => 'views_test_data', + 'field' => 'age', + 'relationship' => 'none', + 'exposed' => TRUE, + 'expose' => array( + 'operator' => 'age_op', + 'label' => 'age', + 'identifier' => 'age', + ), + 'is_grouped' => TRUE, + 'group_info' => array( + 'label' => 'age', + 'identifier' => 'age', + 'default_group' => 'All', + 'group_items' => array( + 1 => array( + 'title' => 'Age is 28', + 'operator' => '=', + 'value' => array('value' => 28), + ), + 2 => array( + 'title' => 'Age is between 26 and 29', + 'operator' => 'between', + 'value' => array( + 'min' => 26, + 'max' => 29, + ), + ), + 3 => array( + 'title' => 'Age is not between 26 and 29', + 'operator' => 'not between', + 'value' => array( + 'min' => 26, + 'max' => 29, + ), + ), + 4 => array( + 'title' => 'Age is empty', + 'operator' => 'empty', + ), + 5 => array( + 'title' => 'Age is not empty', + 'operator' => 'not empty', + ), + ), + ), + ), + ); + return $filters; + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/FilterStringTest.php b/core/modules/views/tests/src/Kernel/Handler/FilterStringTest.php new file mode 100644 index 0000000..27ca0db --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/FilterStringTest.php @@ -0,0 +1,863 @@ + 'name', + ); + + function viewsData() { + $data = parent::viewsData(); + $data['views_test_data']['name']['filter']['allow empty'] = TRUE; + $data['views_test_data']['job']['filter']['allow empty'] = FALSE; + $data['views_test_data']['description'] = $data['views_test_data']['name']; + + return $data; + } + + protected function schemaDefinition() { + $schema = parent::schemaDefinition(); + $schema['views_test_data']['fields']['description'] = array( + 'description' => "A person's description", + 'type' => 'text', + 'not null' => FALSE, + 'size' => 'big', + ); + + return $schema; + } + + /** + * An extended test dataset. + */ + protected function dataSet() { + $dataset = parent::dataSet(); + $dataset[0]['description'] = 'John Winston Ono Lennon, MBE (9 October 1940 – 8 December 1980) was an English musician and singer-songwriter who rose to worldwide fame as one of the founding members of The Beatles, one of the most commercially successful and critically acclaimed acts in the history of popular music. Along with fellow Beatle Paul McCartney, he formed one of the most successful songwriting partnerships of the 20th century.'; + $dataset[1]['description'] = 'George Harrison,[1] MBE (25 February 1943 – 29 November 2001)[2] was an English rock guitarist, singer-songwriter, actor and film producer who achieved international fame as lead guitarist of The Beatles.'; + $dataset[2]['description'] = 'Richard Starkey, MBE (born 7 July 1940), better known by his stage name Ringo Starr, is an English musician, singer-songwriter, and actor who gained worldwide fame as the drummer for The Beatles.'; + $dataset[3]['description'] = 'Sir James Paul McCartney, MBE (born 18 June 1942) is an English musician, singer-songwriter and composer. Formerly of The Beatles (1960–1970) and Wings (1971–1981), McCartney is the most commercially successful songwriter in the history of popular music, according to Guinness World Records.[1]'; + $dataset[4]['description'] = NULL; + + return $dataset; + } + + /** + * Build and return a Page view of the views_test_data table. + * + * @return view + */ + protected function getBasicPageView() { + $view = Views::getView('test_view'); + + // In order to test exposed filters, we have to disable + // the exposed forms cache. + \Drupal::service('views.exposed_form_cache')->reset(); + + $view->newDisplay('page', 'Page', 'page_1'); + return $view; + } + + function testFilterStringEqual() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'operator' => '=', + 'value' => 'Ringo', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'Ringo', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringGroupedExposedEqual() { + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Name, Operator: =, Value: Ringo + $filters['name']['group_info']['default_group'] = 1; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + + $resultset = array( + array( + 'name' => 'Ringo', + ), + ); + + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringNotEqual() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'operator' => '!=', + 'value' => 'Ringo', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + ), + array( + 'name' => 'George', + ), + array( + 'name' => 'Paul', + ), + array( + 'name' => 'Meredith', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringGroupedExposedNotEqual() { + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Name, Operator: !=, Value: Ringo + $filters['name']['group_info']['default_group'] = '2'; + + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + + $resultset = array( + array( + 'name' => 'John', + ), + array( + 'name' => 'George', + ), + array( + 'name' => 'Paul', + ), + array( + 'name' => 'Meredith', + ), + ); + + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringContains() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'operator' => 'contains', + 'value' => 'ing', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'Ringo', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + + function testFilterStringGroupedExposedContains() { + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Name, Operator: contains, Value: ing + $filters['name']['group_info']['default_group'] = '3'; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + + $resultset = array( + array( + 'name' => 'Ringo', + ), + ); + + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + + function testFilterStringWord() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'description' => array( + 'id' => 'description', + 'table' => 'views_test_data', + 'field' => 'description', + 'relationship' => 'none', + 'operator' => 'word', + 'value' => 'actor', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'George', + ), + array( + 'name' => 'Ringo', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + $view->destroy(); + + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'description' => array( + 'id' => 'description', + 'table' => 'views_test_data', + 'field' => 'description', + 'relationship' => 'none', + 'operator' => 'allwords', + 'value' => 'Richard Starkey', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'Ringo', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + + function testFilterStringGroupedExposedWord() { + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Name, Operator: contains, Value: ing + $filters['name']['group_info']['default_group'] = '3'; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + + $resultset = array( + array( + 'name' => 'Ringo', + ), + ); + + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + $view->destroy(); + + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Description, Operator: contains, Value: actor + $filters['description']['group_info']['default_group'] = '1'; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'George', + ), + array( + 'name' => 'Ringo', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringStarts() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'description' => array( + 'id' => 'description', + 'table' => 'views_test_data', + 'field' => 'description', + 'relationship' => 'none', + 'operator' => 'starts', + 'value' => 'George', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'George', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringGroupedExposedStarts() { + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Name, Operator: starts, Value: George + $filters['description']['group_info']['default_group'] = 2; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + + $resultset = array( + array( + 'name' => 'George', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringNotStarts() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'description' => array( + 'id' => 'description', + 'table' => 'views_test_data', + 'field' => 'description', + 'relationship' => 'none', + 'operator' => 'not_starts', + 'value' => 'George', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + ), + array( + 'name' => 'Ringo', + ), + array( + 'name' => 'Paul', + ), + // There is no Meredith returned because his description is empty + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringGroupedExposedNotStarts() { + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Name, Operator: not_starts, Value: George + $filters['description']['group_info']['default_group'] = 3; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + + $resultset = array( + array( + 'name' => 'John', + ), + array( + 'name' => 'Ringo', + ), + array( + 'name' => 'Paul', + ), + // There is no Meredith returned because his description is empty + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringEnds() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'description' => array( + 'id' => 'description', + 'table' => 'views_test_data', + 'field' => 'description', + 'relationship' => 'none', + 'operator' => 'ends', + 'value' => 'Beatles.', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'George', + ), + array( + 'name' => 'Ringo', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringGroupedExposedEnds() { + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Description, Operator: ends, Value: Beatles + $filters['description']['group_info']['default_group'] = 4; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + + $resultset = array( + array( + 'name' => 'George', + ), + array( + 'name' => 'Ringo', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringNotEnds() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'description' => array( + 'id' => 'description', + 'table' => 'views_test_data', + 'field' => 'description', + 'relationship' => 'none', + 'operator' => 'not_ends', + 'value' => 'Beatles.', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + ), + array( + 'name' => 'Paul', + ), + // There is no Meredith returned because his description is empty + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringGroupedExposedNotEnds() { + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Description, Operator: not_ends, Value: Beatles + $filters['description']['group_info']['default_group'] = 5; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + + $resultset = array( + array( + 'name' => 'John', + ), + array( + 'name' => 'Paul', + ), + // There is no Meredith returned because his description is empty + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringNot() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'description' => array( + 'id' => 'description', + 'table' => 'views_test_data', + 'field' => 'description', + 'relationship' => 'none', + 'operator' => 'not', + 'value' => 'Beatles.', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + ), + array( + 'name' => 'Paul', + ), + // There is no Meredith returned because his description is empty + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + + function testFilterStringGroupedExposedNot() { + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Description, Operator: not (does not contains), Value: Beatles + $filters['description']['group_info']['default_group'] = 6; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + + $resultset = array( + array( + 'name' => 'John', + ), + array( + 'name' => 'Paul', + ), + // There is no Meredith returned because his description is empty + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + + } + + function testFilterStringShorter() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'operator' => 'shorterthan', + 'value' => 5, + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + ), + array( + 'name' => 'Paul', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringGroupedExposedShorter() { + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Name, Operator: shorterthan, Value: 5 + $filters['name']['group_info']['default_group'] = 4; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'John', + ), + array( + 'name' => 'Paul', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringLonger() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'operator' => 'longerthan', + 'value' => 7, + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'Meredith', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringGroupedExposedLonger() { + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Name, Operator: longerthan, Value: 4 + $filters['name']['group_info']['default_group'] = 5; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'Meredith', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + + function testFilterStringEmpty() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the filtering + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'description' => array( + 'id' => 'description', + 'table' => 'views_test_data', + 'field' => 'description', + 'relationship' => 'none', + 'operator' => 'empty', + ), + )); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'Meredith', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + function testFilterStringGroupedExposedEmpty() { + $filters = $this->getGroupedExposedFilters(); + $view = $this->getBasicPageView(); + + // Filter: Description, Operator: empty, Value: + $filters['description']['group_info']['default_group'] = 7; + $view->setDisplay('page_1'); + $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); + + $this->executeView($view); + $resultset = array( + array( + 'name' => 'Meredith', + ), + ); + $this->assertIdenticalResultset($view, $resultset, $this->columnMap); + } + + protected function getGroupedExposedFilters() { + $filters = array( + 'name' => array( + 'id' => 'name', + 'plugin_id' => 'string', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + 'exposed' => TRUE, + 'expose' => array( + 'operator' => 'name_op', + 'label' => 'name', + 'identifier' => 'name', + ), + 'is_grouped' => TRUE, + 'group_info' => array( + 'label' => 'name', + 'identifier' => 'name', + 'default_group' => 'All', + 'group_items' => array( + 1 => array( + 'title' => 'Is Ringo', + 'operator' => '=', + 'value' => 'Ringo', + ), + 2 => array( + 'title' => 'Is not Ringo', + 'operator' => '!=', + 'value' => 'Ringo', + ), + 3 => array( + 'title' => 'Contains ing', + 'operator' => 'contains', + 'value' => 'ing', + ), + 4 => array( + 'title' => 'Shorter than 5 letters', + 'operator' => 'shorterthan', + 'value' => 5, + ), + 5 => array( + 'title' => 'Longer than 7 letters', + 'operator' => 'longerthan', + 'value' => 7, + ), + ), + ), + ), + 'description' => array( + 'id' => 'description', + 'plugin_id' => 'string', + 'table' => 'views_test_data', + 'field' => 'description', + 'relationship' => 'none', + 'exposed' => TRUE, + 'expose' => array( + 'operator' => 'description_op', + 'label' => 'description', + 'identifier' => 'description', + ), + 'is_grouped' => TRUE, + 'group_info' => array( + 'label' => 'description', + 'identifier' => 'description', + 'default_group' => 'All', + 'group_items' => array( + 1 => array( + 'title' => 'Contains the word: Actor', + 'operator' => 'word', + 'value' => 'actor', + ), + 2 => array( + 'title' => 'Starts with George', + 'operator' => 'starts', + 'value' => 'George', + ), + 3 => array( + 'title' => 'Not Starts with: George', + 'operator' => 'not_starts', + 'value' => 'George', + ), + 4 => array( + 'title' => 'Ends with: Beatles', + 'operator' => 'ends', + 'value' => 'Beatles.', + ), + 5 => array( + 'title' => 'Not Ends with: Beatles', + 'operator' => 'not_ends', + 'value' => 'Beatles.', + ), + 6 => array( + 'title' => 'Does not contain: Beatles', + 'operator' => 'not', + 'value' => 'Beatles.', + ), + 7 => array( + 'title' => 'Empty description', + 'operator' => 'empty', + 'value' => '', + ), + ), + ), + ), + ); + return $filters; + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/HandlerAliasTest.php b/core/modules/views/tests/src/Kernel/Handler/HandlerAliasTest.php new file mode 100644 index 0000000..368bec5 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/HandlerAliasTest.php @@ -0,0 +1,89 @@ +installEntitySchema('user'); + } + + /** + * {@inheritdoc} + */ + protected function viewsData() { + $data = parent::viewsData(); + // User the existing test_filter plugin. + $data['views_test_data_alias']['table']['real table'] = 'views_test_data'; + $data['views_test_data_alias']['name_alias']['filter']['id'] = 'test_filter'; + $data['views_test_data_alias']['name_alias']['filter']['real field'] = 'name'; + + return $data; + } + + public function testPluginAliases() { + $view = Views::getView('test_filter'); + $view->initDisplay(); + + // Change the filtering. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'test_filter' => array( + 'id' => 'test_filter', + 'table' => 'views_test_data_alias', + 'field' => 'name_alias', + 'operator' => '=', + 'value' => 'John', + 'group' => 0, + ), + )); + + $this->executeView($view); + + $filter = $view->filter['test_filter']; + + // Check the definition values are present. + $this->assertIdentical($filter->definition['real table'], 'views_test_data'); + $this->assertIdentical($filter->definition['real field'], 'name'); + + $this->assertIdentical($filter->table, 'views_test_data'); + $this->assertIdentical($filter->realField, 'name'); + + // Test an existing user uid field. + $view = Views::getView('test_alias'); + $view->initDisplay(); + $this->executeView($view); + + $filter = $view->filter['uid_raw']; + + $this->assertIdentical($filter->definition['real field'], 'uid'); + + $this->assertIdentical($filter->field, 'uid_raw'); + $this->assertIdentical($filter->table, 'users_field_data'); + $this->assertIdentical($filter->realField, 'uid'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/SortDateTest.php b/core/modules/views/tests/src/Kernel/Handler/SortDateTest.php new file mode 100644 index 0000000..da13b81 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/SortDateTest.php @@ -0,0 +1,208 @@ + 'John'), + array('name' => 'Paul'), + array('name' => 'Meredith'), + array('name' => 'Ringo'), + array('name' => 'George'), + ); + break; + case 'minute': + $expected = array( + array('name' => 'John'), + array('name' => 'Paul'), + array('name' => 'Ringo'), + array('name' => 'Meredith'), + array('name' => 'George'), + ); + break; + case 'hour': + $expected = array( + array('name' => 'John'), + array('name' => 'Ringo'), + array('name' => 'Paul'), + array('name' => 'Meredith'), + array('name' => 'George'), + ); + break; + case 'day': + $expected = array( + array('name' => 'John'), + array('name' => 'Ringo'), + array('name' => 'Paul'), + array('name' => 'Meredith'), + array('name' => 'George'), + ); + break; + case 'month': + $expected = array( + array('name' => 'John'), + array('name' => 'George'), + array('name' => 'Ringo'), + array('name' => 'Paul'), + array('name' => 'Meredith'), + ); + break; + case 'year': + $expected = array( + array('name' => 'John'), + array('name' => 'George'), + array('name' => 'Ringo'), + array('name' => 'Paul'), + array('name' => 'Meredith'), + ); + break; + } + } + else { + switch ($granularity) { + case 'second': + $expected = array( + array('name' => 'George'), + array('name' => 'Ringo'), + array('name' => 'Meredith'), + array('name' => 'Paul'), + array('name' => 'John'), + ); + break; + case 'minute': + $expected = array( + array('name' => 'George'), + array('name' => 'Ringo'), + array('name' => 'Meredith'), + array('name' => 'Paul'), + array('name' => 'John'), + ); + break; + case 'hour': + $expected = array( + array('name' => 'George'), + array('name' => 'Ringo'), + array('name' => 'Paul'), + array('name' => 'Meredith'), + array('name' => 'John'), + ); + break; + case 'day': + $expected = array( + array('name' => 'George'), + array('name' => 'John'), + array('name' => 'Ringo'), + array('name' => 'Paul'), + array('name' => 'Meredith'), + ); + break; + case 'month': + $expected = array( + array('name' => 'John'), + array('name' => 'George'), + array('name' => 'Ringo'), + array('name' => 'Paul'), + array('name' => 'Meredith'), + ); + break; + case 'year': + $expected = array( + array('name' => 'John'), + array('name' => 'George'), + array('name' => 'Ringo'), + array('name' => 'Paul'), + array('name' => 'Meredith'), + ); + break; + } + } + + return $expected; + } + + /** + * Tests numeric ordering of the result set. + */ + public function testDateOrdering() { + foreach (array('second', 'minute', 'hour', 'day', 'month', 'year') as $granularity) { + foreach (array(FALSE, TRUE) as $reverse) { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the fields. + $view->displayHandlers->get('default')->overrideOption('fields', array( + 'name' => array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + ), + 'created' => array( + 'id' => 'created', + 'table' => 'views_test_data', + 'field' => 'created', + 'relationship' => 'none', + ), + )); + + // Change the ordering + $view->displayHandlers->get('default')->overrideOption('sorts', array( + 'created' => array( + 'id' => 'created', + 'table' => 'views_test_data', + 'field' => 'created', + 'relationship' => 'none', + 'granularity' => $granularity, + 'order' => $reverse ? 'DESC' : 'ASC', + ), + 'id' => array( + 'id' => 'id', + 'table' => 'views_test_data', + 'field' => 'id', + 'relationship' => 'none', + 'order' => 'ASC', + ), + )); + + // Execute the view. + $this->executeView($view); + + // Verify the result. + $this->assertEqual(count($this->dataSet()), count($view->result), 'The number of returned rows match.'); + $this->assertIdenticalResultset($view, $this->expectedResultSet($granularity, $reverse), array( + 'views_test_data_name' => 'name', + ), SafeMarkup::format('Result is returned correctly when ordering by granularity @granularity, @reverse.', array('@granularity' => $granularity, '@reverse' => $reverse ? 'reverse' : 'forward'))); + $view->destroy(); + unset($view); + } + } + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/SortRandomTest.php b/core/modules/views/tests/src/Kernel/Handler/SortRandomTest.php new file mode 100644 index 0000000..cc72fab --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/SortRandomTest.php @@ -0,0 +1,144 @@ + 'name_' . $i, + 'age' => $i, + 'job' => 'job_' . $i, + 'created' => rand(0, time()), + 'status' => 1, + ); + } + return $data; + } + + /** + * Return a basic view with random ordering. + */ + protected function getBasicRandomView() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Add a random ordering. + $view->displayHandlers->get('default')->overrideOption('sorts', array( + 'random' => array( + 'id' => 'random', + 'field' => 'random', + 'table' => 'views', + ), + )); + + return $view; + } + + /** + * Tests random ordering of the result set. + * + * @see DatabaseSelectTestCase::testRandomOrder() + */ + public function testRandomOrdering() { + // Execute a basic view first. + $view = Views::getView('test_view'); + $this->executeView($view); + + // Verify the result. + $this->assertEqual(count($this->dataSet()), count($view->result), 'The number of returned rows match.'); + $this->assertIdenticalResultset($view, $this->dataSet(), array( + 'views_test_data_name' => 'name', + 'views_test_data_age' => 'age', + )); + + // Execute a random view, we expect the result set to be different. + $view_random = $this->getBasicRandomView(); + $this->executeView($view_random); + $this->assertEqual(count($this->dataSet()), count($view_random->result), 'The number of returned rows match.'); + $this->assertNotIdenticalResultset($view_random, $view->result, array( + 'views_test_data_name' => 'views_test_data_name', + 'views_test_data_age' => 'views_test_data_name', + )); + + // Execute a second random view, we expect the result set to be different again. + $view_random_2 = $this->getBasicRandomView(); + $this->executeView($view_random_2); + $this->assertEqual(count($this->dataSet()), count($view_random_2->result), 'The number of returned rows match.'); + $this->assertNotIdenticalResultset($view_random, $view->result, array( + 'views_test_data_name' => 'views_test_data_name', + 'views_test_data_age' => 'views_test_data_name', + )); + } + + /** + * Tests random ordering with tags based caching. + * + * The random sorting should opt out of caching by defining a max age of 0. + * At the same time, the row render caching still works. + */ + public function testRandomOrderingWithRenderCaching() { + $view_random = $this->getBasicRandomView(); + + $display = &$view_random->storage->getDisplay('default'); + $display['display_options']['cache'] = [ + 'type' => 'tag', + ]; + + $view_random->storage->save(); + + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + /** @var \Drupal\Core\Render\RenderCacheInterface $render_cache */ + $render_cache = \Drupal::service('render_cache'); + + $original = $build = DisplayPluginBase::buildBasicRenderable($view_random->id(), 'default'); + $result = $renderer->renderPlain($build); + + $original['#cache'] += ['contexts' => []]; + $original['#cache']['contexts'] = Cache::mergeContexts($original['#cache']['contexts'], $this->container->getParameter('renderer.config')['required_cache_contexts']); + + $this->assertFalse($render_cache->get($original), 'Ensure there is no render cache entry.'); + + $build = DisplayPluginBase::buildBasicRenderable($view_random->id(), 'default'); + $result2 = $renderer->renderPlain($build); + + // Ensure that the random ordering works and don't produce the same result. + $this->assertNotEqual($result, $result2); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Handler/SortTest.php b/core/modules/views/tests/src/Kernel/Handler/SortTest.php new file mode 100644 index 0000000..bec86c6 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Handler/SortTest.php @@ -0,0 +1,133 @@ +setDisplay(); + + // Change the ordering + $view->displayHandlers->get('default')->overrideOption('sorts', array( + 'age' => array( + 'order' => 'ASC', + 'id' => 'age', + 'table' => 'views_test_data', + 'field' => 'age', + 'relationship' => 'none', + ), + )); + + // Execute the view. + $this->executeView($view); + + // Verify the result. + $this->assertEqual(count($this->dataSet()), count($view->result), 'The number of returned rows match.'); + $this->assertIdenticalResultset($view, $this->orderResultSet($this->dataSet(), 'age'), array( + 'views_test_data_name' => 'name', + 'views_test_data_age' => 'age', + )); + + $view->destroy(); + $view->setDisplay(); + + // Reverse the ordering + $view->displayHandlers->get('default')->overrideOption('sorts', array( + 'age' => array( + 'order' => 'DESC', + 'id' => 'age', + 'table' => 'views_test_data', + 'field' => 'age', + 'relationship' => 'none', + ), + )); + + // Execute the view. + $this->executeView($view); + + // Verify the result. + $this->assertEqual(count($this->dataSet()), count($view->result), 'The number of returned rows match.'); + $this->assertIdenticalResultset($view, $this->orderResultSet($this->dataSet(), 'age', TRUE), array( + 'views_test_data_name' => 'name', + 'views_test_data_age' => 'age', + )); + } + + /** + * Tests string ordering of the result set. + */ + public function testStringOrdering() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Change the ordering + $view->displayHandlers->get('default')->overrideOption('sorts', array( + 'name' => array( + 'order' => 'ASC', + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + ), + )); + + // Execute the view. + $this->executeView($view); + + // Verify the result. + $this->assertEqual(count($this->dataSet()), count($view->result), 'The number of returned rows match.'); + $this->assertIdenticalResultset($view, $this->orderResultSet($this->dataSet(), 'name'), array( + 'views_test_data_name' => 'name', + 'views_test_data_age' => 'age', + )); + + $view->destroy(); + $view->setDisplay(); + + // Reverse the ordering + $view->displayHandlers->get('default')->overrideOption('sorts', array( + 'name' => array( + 'order' => 'DESC', + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'relationship' => 'none', + ), + )); + + // Execute the view. + $this->executeView($view); + + // Verify the result. + $this->assertEqual(count($this->dataSet()), count($view->result), 'The number of returned rows match.'); + $this->assertIdenticalResultset($view, $this->orderResultSet($this->dataSet(), 'name', TRUE), array( + 'views_test_data_name' => 'name', + 'views_test_data_age' => 'age', + )); + } + +} diff --git a/core/modules/views/tests/src/Kernel/ModuleTest.php b/core/modules/views/tests/src/Kernel/ModuleTest.php new file mode 100644 index 0000000..a77fe6b --- /dev/null +++ b/core/modules/views/tests/src/Kernel/ModuleTest.php @@ -0,0 +1,374 @@ + $this->randomMachineName(), + 'field' => $this->randomMachineName(), + ); + $handler = $this->container->get('plugin.manager.views.' . $type)->getHandler($item); + $this->assertEqual('Drupal\views\Plugin\views\\' . $type . '\Broken', get_class($handler), new FormattableMarkup('Make sure that a broken handler of type: @type is created.', ['@type' => $type])); + } + + $views_data = $this->viewsData(); + $test_tables = array('views_test_data' => array('id', 'name')); + foreach ($test_tables as $table => $fields) { + foreach ($fields as $field) { + $data = $views_data[$table][$field]; + $item = array( + 'table' => $table, + 'field' => $field, + ); + foreach ($data as $id => $field_data) { + if (!in_array($id, array('title', 'help'))) { + $handler = $this->container->get('plugin.manager.views.' . $id)->getHandler($item); + $this->assertInstanceHandler($handler, $table, $field, $id); + } + } + } + } + + // Test the override handler feature. + $item = array( + 'table' => 'views_test_data', + 'field' => 'job', + ); + $handler = $this->container->get('plugin.manager.views.filter')->getHandler($item, 'standard'); + $this->assertTrue($handler instanceof Standard); + + // @todo Reinstate these tests when the debug() in views_get_handler() is + // restored. + return; + + // Test non-existent tables/fields. + set_error_handler(array($this, 'customErrorHandler')); + $item = array( + 'table' => 'views_test_data', + 'field' => 'field_invalid', + ); + $this->container->get('plugin.manager.views.field')->getHandler($item); + $this->assertTrue(strpos($this->lastErrorMessage, format_string("Missing handler: @table @field @type", array('@table' => 'views_test_data', '@field' => 'field_invalid', '@type' => 'field'))) !== FALSE, 'An invalid field name throws a debug message.'); + unset($this->lastErrorMessage); + + $item = array( + 'table' => 'table_invalid', + 'field' => 'id', + ); + $this->container->get('plugin.manager.views.filter')->getHandler($item); + $this->assertEqual(strpos($this->lastErrorMessage, format_string("Missing handler: @table @field @type", array('@table' => 'table_invalid', '@field' => 'id', '@type' => 'filter'))) !== FALSE, 'An invalid table name throws a debug message.'); + unset($this->lastErrorMessage); + + $item = array( + 'table' => 'table_invalid', + 'field' => 'id', + ); + $this->container->get('plugin.manager.views.filter')->getHandler($item); + $this->assertEqual(strpos($this->lastErrorMessage, format_string("Missing handler: @table @field @type", array('@table' => 'table_invalid', '@field' => 'id', '@type' => 'filter'))) !== FALSE, 'An invalid table name throws a debug message.'); + unset($this->lastErrorMessage); + + restore_error_handler(); + } + + /** + * Defines an error handler which is used in the test. + * + * @param int $error_level + * The level of the error raised. + * @param string $message + * The error message. + * @param string $filename + * The filename that the error was raised in. + * @param int $line + * The line number the error was raised at. + * @param array $context + * An array that points to the active symbol table at the point the error + * occurred. + * + * Because this is registered in set_error_handler(), it has to be public. + * @see set_error_handler() + */ + public function customErrorHandler($error_level, $message, $filename, $line, $context) { + $this->lastErrorMessage = $message; + } + + /** + * Tests the load wrapper/helper functions. + */ + public function testLoadFunctions() { + $this->enableModules(array('text', 'node')); + $this->installConfig(array('node')); + $storage = $this->container->get('entity.manager')->getStorage('view'); + + // Test views_view_is_enabled/disabled. + $archive = $storage->load('archive'); + $this->assertTrue(views_view_is_disabled($archive), 'views_view_is_disabled works as expected.'); + // Enable the view and check this. + $archive->enable(); + $this->assertTrue(views_view_is_enabled($archive), ' views_view_is_enabled works as expected.'); + + // We can store this now, as we have enabled/disabled above. + $all_views = $storage->loadMultiple(); + + // Test Views::getAllViews(). + ksort($all_views); + $this->assertEquals(array_keys($all_views), array_keys(Views::getAllViews()), 'Views::getAllViews works as expected.'); + + // Test Views::getEnabledViews(). + $expected_enabled = array_filter($all_views, function($view) { + return views_view_is_enabled($view); + }); + $this->assertEquals(array_keys($expected_enabled), array_keys(Views::getEnabledViews()), 'Expected enabled views returned.'); + + // Test Views::getDisabledViews(). + $expected_disabled = array_filter($all_views, function($view) { + return views_view_is_disabled($view); + }); + $this->assertEquals(array_keys($expected_disabled), array_keys(Views::getDisabledViews()), 'Expected disabled views returned.'); + + // Test Views::getViewsAsOptions(). + // Test the $views_only parameter. + $this->assertIdentical(array_keys($all_views), array_keys(Views::getViewsAsOptions(TRUE)), 'Expected option keys for all views were returned.'); + $expected_options = array(); + foreach ($all_views as $id => $view) { + $expected_options[$id] = $view->label(); + } + $this->assertIdentical($expected_options, $this->castSafeStrings(Views::getViewsAsOptions(TRUE)), 'Expected options array was returned.'); + + // Test the default. + $this->assertIdentical($this->formatViewOptions($all_views), $this->castSafeStrings(Views::getViewsAsOptions()), 'Expected options array for all views was returned.'); + // Test enabled views. + $this->assertIdentical($this->formatViewOptions($expected_enabled), $this->castSafeStrings(Views::getViewsAsOptions(FALSE, 'enabled')), 'Expected enabled options array was returned.'); + // Test disabled views. + $this->assertIdentical($this->formatViewOptions($expected_disabled), $this->castSafeStrings(Views::getViewsAsOptions(FALSE, 'disabled')), 'Expected disabled options array was returned.'); + + // Test the sort parameter. + $all_views_sorted = $all_views; + ksort($all_views_sorted); + $this->assertIdentical(array_keys($all_views_sorted), array_keys(Views::getViewsAsOptions(TRUE, 'all', NULL, FALSE, TRUE)), 'All view id keys returned in expected sort order'); + + // Test $exclude_view parameter. + $this->assertFalse(array_key_exists('archive', Views::getViewsAsOptions(TRUE, 'all', 'archive')), 'View excluded from options based on name'); + $this->assertFalse(array_key_exists('archive:default', Views::getViewsAsOptions(FALSE, 'all', 'archive:default')), 'View display excluded from options based on name'); + $this->assertFalse(array_key_exists('archive', Views::getViewsAsOptions(TRUE, 'all', $archive->getExecutable())), 'View excluded from options based on object'); + + // Test the $opt_group parameter. + $expected_opt_groups = array(); + foreach ($all_views as $view) { + foreach ($view->get('display') as $display) { + $expected_opt_groups[$view->id()][$view->id() . ':' . $display['id']] = (string) t('@view : @display', array('@view' => $view->id(), '@display' => $display['id'])); + } + } + $this->assertIdentical($expected_opt_groups, $this->castSafeStrings(Views::getViewsAsOptions(FALSE, 'all', NULL, TRUE)), 'Expected option array for an option group returned.'); + } + + /** + * Tests view enable and disable procedural wrapper functions. + */ + function testStatusFunctions() { + $view = Views::getView('test_view_status')->storage; + + $this->assertFalse($view->status(), 'The view status is disabled.'); + + views_enable_view($view); + $this->assertTrue($view->status(), 'A view has been enabled.'); + $this->assertEqual($view->status(), views_view_is_enabled($view), 'views_view_is_enabled is correct.'); + + views_disable_view($view); + $this->assertFalse($view->status(), 'A view has been disabled.'); + $this->assertEqual(!$view->status(), views_view_is_disabled($view), 'views_view_is_disabled is correct.'); + } + + /** + * Tests the \Drupal\views\Views::fetchPluginNames() method. + */ + public function testViewsFetchPluginNames() { + // All style plugins should be returned, as we have not specified a type. + $plugins = Views::fetchPluginNames('style'); + $definitions = $this->container->get('plugin.manager.views.style')->getDefinitions(); + $expected = array(); + foreach ($definitions as $id =>$definition) { + $expected[$id] = $definition['title']; + } + asort($expected); + $this->assertIdentical(array_keys($plugins), array_keys($expected)); + + // Test using the 'test' style plugin type only returns the test_style and + // mapping_test plugins. + $plugins = Views::fetchPluginNames('style', 'test'); + $this->assertIdentical(array_keys($plugins), array('mapping_test', 'test_style', 'test_template_style')); + + // Test a non existent style plugin type returns no plugins. + $plugins = Views::fetchPluginNames('style', $this->randomString()); + $this->assertIdentical($plugins, array()); + } + + /** + * Tests the \Drupal\views\Views::pluginList() method. + */ + public function testViewsPluginList() { + $plugin_list = Views::pluginList(); + // Only plugins used by 'test_view' should be in the plugin list. + foreach (array('display:default', 'pager:none') as $key) { + list($plugin_type, $plugin_id) = explode(':', $key); + $plugin_def = $this->container->get("plugin.manager.views.$plugin_type")->getDefinition($plugin_id); + + $this->assertTrue(isset($plugin_list[$key]), SafeMarkup::format('The expected @key plugin list key was found.', array('@key' => $key))); + $plugin_details = $plugin_list[$key]; + + $this->assertEqual($plugin_details['type'], $plugin_type, 'The expected plugin type was found.'); + $this->assertEqual($plugin_details['title'], $plugin_def['title'], 'The expected plugin title was found.'); + $this->assertEqual($plugin_details['provider'], $plugin_def['provider'], 'The expected plugin provider was found.'); + $this->assertTrue(in_array('test_view', $plugin_details['views']), 'The test_view View was found in the list of views using this plugin.'); + } + } + + /** + * Tests views.module: views_embed_view(). + */ + public function testViewsEmbedView() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + + $result = views_embed_view('test_argument'); + $renderer->renderPlain($result); + $this->assertEqual(count($result['view_build']['#view']->result), 5); + + $result = views_embed_view('test_argument', 'default', 1); + $renderer->renderPlain($result); + $this->assertEqual(count($result['view_build']['#view']->result), 1); + + $result = views_embed_view('test_argument', 'default', '1,2'); + $renderer->renderPlain($result); + $this->assertEqual(count($result['view_build']['#view']->result), 2); + + $result = views_embed_view('test_argument', 'default', '1,2', 'John'); + $renderer->renderPlain($result); + $this->assertEqual(count($result['view_build']['#view']->result), 1); + + $result = views_embed_view('test_argument', 'default', '1,2', 'John,George'); + $renderer->renderPlain($result); + $this->assertEqual(count($result['view_build']['#view']->result), 2); + } + + /** + * Tests the \Drupal\views\ViewsExecutable::preview() method. + */ + public function testViewsPreview() { + $view = Views::getView('test_argument'); + $result = $view->preview('default'); + $this->assertEqual(count($result['#view']->result), 5); + + $view = Views::getView('test_argument'); + $result = $view->preview('default', array('0' => 1)); + $this->assertEqual(count($result['#view']->result), 1); + + $view = Views::getView('test_argument'); + $result = $view->preview('default', array('3' => 1)); + $this->assertEqual(count($result['#view']->result), 1); + + $view = Views::getView('test_argument'); + $result = $view->preview('default', array('0' => '1,2')); + $this->assertEqual(count($result['#view']->result), 2); + + $view = Views::getView('test_argument'); + $result = $view->preview('default', array('3' => '1,2')); + $this->assertEqual(count($result['#view']->result), 2); + + $view = Views::getView('test_argument'); + $result = $view->preview('default', array('0' => '1,2', '1' => 'John')); + $this->assertEqual(count($result['#view']->result), 1); + + $view = Views::getView('test_argument'); + $result = $view->preview('default', array('3' => '1,2', '4' => 'John')); + $this->assertEqual(count($result['#view']->result), 1); + + $view = Views::getView('test_argument'); + $result = $view->preview('default', array('0' => '1,2', '1' => 'John,George')); + $this->assertEqual(count($result['#view']->result), 2); + + $view = Views::getView('test_argument'); + $result = $view->preview('default', array('3' => '1,2', '4' => 'John,George')); + $this->assertEqual(count($result['#view']->result), 2); + } + + /** + * Helper to return an expected views option array. + * + * @param array $views + * An array of Drupal\views\Entity\View objects for which to + * create an options array. + * + * @return array + * A formatted options array that matches the expected output. + */ + protected function formatViewOptions(array $views = array()) { + $expected_options = array(); + foreach ($views as $view) { + foreach ($view->get('display') as $display) { + $expected_options[$view->id() . ':' . $display['id']] = (string) t('View: @view - Display: @display', + array('@view' => $view->id(), '@display' => $display['id'])); + } + } + + return $expected_options; + } + + /** + * Ensure that a certain handler is a instance of a certain table/field. + */ + function assertInstanceHandler($handler, $table, $field, $id) { + $table_data = $this->container->get('views.views_data')->get($table); + $field_data = $table_data[$field][$id]; + + $this->assertEqual($field_data['id'], $handler->getPluginId()); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/ArgumentValidatorTest.php b/core/modules/views/tests/src/Kernel/Plugin/ArgumentValidatorTest.php new file mode 100644 index 0000000..ff477dc --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/ArgumentValidatorTest.php @@ -0,0 +1,70 @@ +initHandlers(); + $this->assertFalse($view->argument['null']->validateArgument($this->randomString())); + // Reset safed argument validation. + $view->argument['null']->argument_validated = NULL; + $this->assertTrue($view->argument['null']->validateArgument(12)); + } + + /** + * Tests the argument validator test plugin. + * + * @see Drupal\views_test_data\Plugin\views\argument_validator\ArgumentValidatorTest + */ + public function testArgumentValidatorPlugin() { + $view = Views::getView('test_view'); + + // Add a new argument and set the test plugin for the argument_validator. + $options = [ + 'specify_validation' => TRUE, + 'validate' => [ + 'type' => 'argument_validator_test' + ] + ]; + $id = $view->addHandler('default', 'argument', 'views_test_data', 'name', $options); + $view->initHandlers(); + + $test_value = $this->randomMachineName(); + + $argument = $view->argument[$id]; + $argument->options['validate_options']['test_value'] = $test_value; + $this->assertFalse($argument->validateArgument($this->randomMachineName()), 'A random value does not validate.'); + // Reset internal flag. + $argument->argument_validated = NULL; + $this->assertTrue($argument->validateArgument($test_value), 'The right argument validates.'); + + $plugin = $argument->getPlugin('argument_validator'); + $this->assertTrue($plugin instanceof ArgumentValidatorTestPlugin, 'The correct argument validator plugin is used.'); + $this->assertFalse($plugin->validateArgument($this->randomMachineName()), 'A random value does not validate.'); + $this->assertTrue($plugin->validateArgument($test_value), 'The right argument validates.'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/BlockDependenciesTest.php b/core/modules/views/tests/src/Kernel/Plugin/BlockDependenciesTest.php new file mode 100644 index 0000000..12d37bb --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/BlockDependenciesTest.php @@ -0,0 +1,115 @@ +createBlock('views_exposed_filter_block:test_exposed_block-page_1'); + $dependencies = $block->calculateDependencies()->getDependencies(); + $expected = array( + 'config' => array('views.view.test_exposed_block'), + 'module' => array('views'), + 'theme' => array('stark') + ); + $this->assertIdentical($expected, $dependencies); + } + + /** + * Tests that exposed filter blocks have the correct dependencies. + * + * @see \Drupal\views\Plugin\Derivative\ViewsBlock::getDerivativeDefinitions() + */ + public function testViewsBlock() { + $block = $this->createBlock('views_block:content_recent-block_1'); + $dependencies = $block->calculateDependencies()->getDependencies(); + $expected = array( + 'config' => array('views.view.content_recent'), + 'module' => array('views'), + 'theme' => array('stark') + ); + $this->assertIdentical($expected, $dependencies); + } + + /** + * Creates a block instance based on default settings. + * + * @param string $plugin_id + * The plugin ID of the block type for this block instance. + * @param array $settings + * (optional) An associative array of settings for the block entity. + * Override the defaults by specifying the key and value in the array, for + * example: + * @code + * $this->createBlock('system_powered_by_block', array( + * 'label' => t('Hello, world!'), + * )); + * @endcode + * The following defaults are provided: + * - label: Random string. + * - id: Random string. + * - region: 'sidebar_first'. + * - theme: The default theme. + * - visibility: Empty array. + * + * @return \Drupal\block\Entity\Block + * The block entity. + */ + protected function createBlock($plugin_id, array $settings = array()) { + $settings += array( + 'plugin' => $plugin_id, + 'region' => 'sidebar_first', + 'id' => strtolower($this->randomMachineName(8)), + 'theme' => $this->config('system.theme')->get('default'), + 'label' => $this->randomMachineName(8), + 'visibility' => array(), + 'weight' => 0, + ); + $values = []; + foreach (array('region', 'id', 'theme', 'plugin', 'weight', 'visibility') as $key) { + $values[$key] = $settings[$key]; + // Remove extra values that do not belong in the settings array. + unset($settings[$key]); + } + foreach ($values['visibility'] as $id => $visibility) { + $values['visibility'][$id]['id'] = $id; + } + $values['settings'] = $settings; + $block = Block::create($values); + $block->save(); + return $block; + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/CacheTest.php b/core/modules/views/tests/src/Kernel/Plugin/CacheTest.php new file mode 100644 index 0000000..cd367f4 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/CacheTest.php @@ -0,0 +1,391 @@ +installEntitySchema('node'); + $this->installEntitySchema('taxonomy_term'); + $this->installEntitySchema('user'); + + // Setup the current time properly. + \Drupal::request()->server->set('REQUEST_TIME', time()); + } + + /** + * {@inheritdoc} + */ + protected function viewsData() { + $data = parent::viewsData(); + + $data['views_test_data']['test_cache_context'] = [ + 'real field' => 'name', + 'title' => 'Test cache context', + 'filter' => [ + 'id' => 'views_test_test_cache_context', + ], + ]; + + return $data; + } + + + /** + * Tests time based caching. + * + * @see views_plugin_cache_time + */ + public function testTimeResultCaching() { + $view = Views::getView('test_cache'); + $view->setDisplay(); + $view->display_handler->overrideOption('cache', array( + 'type' => 'time', + 'options' => array( + 'results_lifespan' => '3600', + 'output_lifespan' => '3600', + ) + )); + + // Test the default (non-paged) display. + $this->executeView($view); + // Verify the result. + $this->assertEqual(5, count($view->result), 'The number of returned rows match.'); + + // Add another man to the beatles. + $record = array( + 'name' => 'Rod Davis', + 'age' => 29, + 'job' => 'Banjo', + ); + db_insert('views_test_data')->fields($record)->execute(); + + // The result should be the same as before, because of the caching. (Note + // that views_test_data records don't have associated cache tags, and hence + // the results cache items aren't invalidated.) + $view->destroy(); + $this->executeView($view); + // Verify the result. + $this->assertEqual(5, count($view->result), 'The number of returned rows match.'); + } + + /** + * Tests result caching with filters. + * + * @see views_plugin_cache_time + */ + public function testTimeResultCachingWithFilter() { + // Check that we can find the test filter plugin. + $plugin = $this->container->get('plugin.manager.views.filter')->createInstance('test_filter'); + $this->assertTrue($plugin instanceof FilterPlugin, 'Test filter plugin found.'); + + $view = Views::getView('test_filter'); + $view->initDisplay(); + $view->display_handler->overrideOption('cache', array( + 'type' => 'time', + 'options' => array( + 'results_lifespan' => '3600', + 'output_lifespan' => '3600', + ), + )); + + // Change the filtering. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'test_filter' => array( + 'id' => 'test_filter', + 'table' => 'views_test_data', + 'field' => 'name', + 'operator' => '=', + 'value' => 'John', + 'group' => 0, + ), + )); + + $this->executeView($view); + + // Get the cache item. + $cid1 = $view->display_handler->getPlugin('cache')->generateResultsKey(); + + // Build the expected result. + $dataset = array(array('name' => 'John')); + + // Verify the result. + $this->assertEqual(1, count($view->result), 'The number of returned rows match.'); + $this->assertIdenticalResultSet($view, $dataset, array( + 'views_test_data_name' => 'name', + )); + + $view->destroy(); + + $view->initDisplay(); + + // Change the filtering. + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'test_filter' => array( + 'id' => 'test_filter', + 'table' => 'views_test_data', + 'field' => 'name', + 'operator' => '=', + 'value' => 'Ringo', + 'group' => 0, + ), + )); + + $this->executeView($view); + + // Get the cache item. + $cid2 = $view->display_handler->getPlugin('cache')->generateResultsKey(); + $this->assertNotEqual($cid1, $cid2, "Results keys are different."); + + // Build the expected result. + $dataset = array(array('name' => 'Ringo')); + + // Verify the result. + $this->assertEqual(1, count($view->result), 'The number of returned rows match.'); + $this->assertIdenticalResultSet($view, $dataset, array( + 'views_test_data_name' => 'name', + )); + } + + /** + * Tests result caching with a pager. + */ + public function testTimeResultCachingWithPager() { + $view = Views::getView('test_cache'); + $view->setDisplay(); + $view->display_handler->overrideOption('cache', array( + 'type' => 'time', + 'options' => array( + 'results_lifespan' => '3600', + 'output_lifespan' => '3600', + ) + )); + + $mapping = ['views_test_data_name' => 'name']; + + $view->setDisplay('page_1'); + $view->setCurrentPage(0); + $this->executeView($view); + $this->assertIdenticalResultset($view, [['name' => 'John'], ['name' => 'George']], $mapping); + $view->destroy(); + + $view->setDisplay('page_1'); + $view->setCurrentPage(1); + $this->executeView($view); + $this->assertIdenticalResultset($view, [['name' => 'Ringo'], ['name' => 'Paul']], $mapping); + $view->destroy(); + + $view->setDisplay('page_1'); + $view->setCurrentPage(0); + $this->executeView($view); + $this->assertIdenticalResultset($view, [['name' => 'John'], ['name' => 'George']], $mapping); + $view->destroy(); + + $view->setDisplay('page_1'); + $view->setCurrentPage(2); + $this->executeView($view); + $this->assertIdenticalResultset($view, [['name' => 'Meredith']], $mapping); + $view->destroy(); + } + + /** + * Tests no caching. + * + * @see views_plugin_cache_time + */ + function testNoneResultCaching() { + // Create a basic result which just 2 results. + $view = Views::getView('test_cache'); + $view->setDisplay(); + $view->display_handler->overrideOption('cache', array( + 'type' => 'none', + 'options' => array(), + )); + + $this->executeView($view); + // Verify the result. + $this->assertEqual(5, count($view->result), 'The number of returned rows match.'); + + // Add another man to the beatles. + $record = array( + 'name' => 'Rod Davis', + 'age' => 29, + 'job' => 'Banjo', + ); + db_insert('views_test_data')->fields($record)->execute(); + + // The Result changes, because the view is not cached. + $view = Views::getView('test_cache'); + $view->setDisplay(); + $view->display_handler->overrideOption('cache', array( + 'type' => 'none', + 'options' => array(), + )); + + $this->executeView($view); + // Verify the result. + $this->assertEqual(6, count($view->result), 'The number of returned rows match.'); + } + + /** + * Tests css/js storage and restoring mechanism. + */ + function testHeaderStorage() { + // Create a view with output caching enabled. + // Some hook_views_pre_render in views_test_data.module adds the test css/js file. + // so they should be added to the css/js storage. + $view = Views::getView('test_view'); + $view->setDisplay(); + $view->storage->set('id', 'test_cache_header_storage'); + $view->display_handler->overrideOption('cache', array( + 'type' => 'time', + 'options' => array( + 'output_lifespan' => '3600', + ) + )); + + $output = $view->buildRenderable(); + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = \Drupal::service('renderer'); + $renderer->executeInRenderContext(new RenderContext(), function () use (&$output, $renderer) { + return $renderer->render($output); + }); + + unset($view->pre_render_called); + $view->destroy(); + + $view->setDisplay(); + $output = $view->buildRenderable(); + $renderer->executeInRenderContext(new RenderContext(), function () use (&$output, $renderer) { + return $renderer->render($output); + }); + + $this->assertTrue(in_array('views_test_data/test', $output['#attached']['library']), 'Make sure libraries are added for cached views.'); + $this->assertEqual(['foo' => 'bar'], $output['#attached']['drupalSettings'], 'Make sure drupalSettings are added for cached views.'); + // Note: views_test_data_views_pre_render() adds some cache tags. + $this->assertEqual(['config:views.view.test_cache_header_storage', 'views_test_data:1'], $output['#cache']['tags']); + $this->assertEqual(['non-existing-placeholder-just-for-testing-purposes' => ['#lazy_builder' => ['views_test_data_placeholders', ['bar']]]], $output['#attached']['placeholders']); + $this->assertFalse(!empty($view->build_info['pre_render_called']), 'Make sure hook_views_pre_render is not called for the cached view.'); + } + + /** + * Tests that Subqueries are cached as expected. + */ + public function testSubqueryStringCache() { + // Execute the view. + $view = Views::getView('test_groupwise_term_ui'); + $view->setDisplay(); + $this->executeView($view); + // Request for the cache. + $cid = 'views_relationship_groupwise_max:test_groupwise_term_ui:default:tid_representative'; + $cache = \Drupal::cache('data')->get($cid); + $this->assertEqual($cid, $cache->cid, 'Subquery String cached as expected.'); + } + + /** + * Tests the data contained in cached items. + */ + public function testCacheData() { + for ($i = 1; $i <= 5; $i++) { + Node::create([ + 'title' => $this->randomMachineName(8), + 'type' => 'page', + ])->save(); + } + + $view = Views::getView('test_display'); + $view->setDisplay(); + $view->display_handler->overrideOption('cache', array( + 'type' => 'time', + 'options' => array( + 'results_lifespan' => '3600', + 'output_lifespan' => '3600', + ) + )); + $this->executeView($view); + + // Get the cache item. + $cid = $view->display_handler->getPlugin('cache')->generateResultsKey(); + $cache = \Drupal::cache('data')->get($cid); + + // Assert there are results, empty results would mean this test case would + // pass otherwise. + $this->assertTrue(count($cache->data['result']), 'Results saved in cached data.'); + + // Assert each row doesn't contain '_entity' or '_relationship_entities' + // items. + foreach ($cache->data['result'] as $row) { + $this->assertIdentical($row->_entity, NULL, 'Cached row "_entity" property is NULL'); + $this->assertIdentical($row->_relationship_entities, [], 'Cached row "_relationship_entities" property is empty'); + } + } + + /** + * Tests the cache context integration for views result cache. + */ + public function testCacheContextIntegration() { + $view = Views::getView('test_cache'); + $view->setDisplay('page_2'); + \Drupal::state()->set('views_test_cache_context', 'George'); + $this->executeView($view); + + $map = ['views_test_data_name' => 'name']; + $this->assertIdenticalResultset($view, [['name' => 'George']], $map); + + // Update the entry in the DB to ensure that result caching works. + \Drupal::database()->update('views_test_data') + ->condition('name', 'George') + ->fields(['name' => 'egroeG']) + ->execute(); + + $view = Views::getView('test_cache'); + $view->setDisplay('page_2'); + $this->executeView($view); + $this->assertIdenticalResultset($view, [['name' => 'George']], $map); + + // Now change the cache context value, a different query should be executed. + $view = Views::getView('test_cache'); + $view->setDisplay('page_2'); + \Drupal::state()->set('views_test_cache_context', 'Paul'); + $this->executeView($view); + + $this->assertIdenticalResultset($view, [['name' => 'Paul']], $map); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/DisplayKernelTest.php b/core/modules/views/tests/src/Kernel/Plugin/DisplayKernelTest.php new file mode 100644 index 0000000..8467e66 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/DisplayKernelTest.php @@ -0,0 +1,122 @@ +mergeDefaults(); + $view->save(); + + // Reload to get saved storage values. + $view = Views::getView('test_display_defaults'); + $view->initDisplay(); + $display_data = $view->storage->get('display'); + + foreach ($view->displayHandlers as $id => $display) { + // Test the view plugin options against the storage. + foreach ($this->pluginTypes as $type) { + $options = $display->getOption($type); + $this->assertIdentical($display_data[$id]['display_options'][$type]['options'], $options['options']); + } + // Test the view handler options against the storage. + foreach ($this->handlerTypes as $type) { + $options = $display->getOption($type); + $this->assertIdentical($display_data[$id]['display_options'][$type], $options); + } + } + } + + /** + * Tests the \Drupal\views\Plugin\views\display\DisplayPluginBase::getPlugin() method. + */ + public function testGetPlugin() { + $view = Views::getView('test_display_defaults'); + $view->initDisplay(); + $display_handler = $view->display_handler; + + $this->assertTrue($display_handler->getPlugin('access') instanceof AccessPluginBase, 'An access plugin instance was returned.'); + $this->assertTrue($display_handler->getPlugin('cache') instanceof CachePluginBase, 'A cache plugin instance was returned.'); + $this->assertTrue($display_handler->getPlugin('exposed_form') instanceof ExposedFormPluginBase, 'An exposed_form plugin instance was returned.'); + $this->assertTrue($display_handler->getPlugin('pager') instanceof PagerPluginBase, 'A pager plugin instance was returned.'); + $this->assertTrue($display_handler->getPlugin('query') instanceof QueryPluginBase, 'A query plugin instance was returned.'); + $this->assertTrue($display_handler->getPlugin('row') instanceof RowPluginBase, 'A row plugin instance was returned.'); + $this->assertTrue($display_handler->getPlugin('style') instanceof StylePluginBase, 'A style plugin instance was returned.'); + // Test that nothing is returned when an invalid type is requested. + $this->assertNull($display_handler->getPlugin('invalid'), 'NULL was returned for an invalid instance'); + // Test that nothing was returned for an instance with no 'type' in options. + unset($display_handler->options['access']); + $this->assertNull($display_handler->getPlugin('access'), 'NULL was returned for a plugin type with no "type" option'); + + // Get a plugin twice, and make sure the same instance is returned. + $view->destroy(); + $view->initDisplay(); + $first = spl_object_hash($display_handler->getPlugin('style')); + $second = spl_object_hash($display_handler->getPlugin('style')); + $this->assertIdentical($first, $second, 'The same plugin instance was returned.'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/DisplayPageTest.php b/core/modules/views/tests/src/Kernel/Plugin/DisplayPageTest.php new file mode 100644 index 0000000..9573712 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/DisplayPageTest.php @@ -0,0 +1,157 @@ +setAccount(new AnonymousUserSession()); + $subrequest = Request::create('/test_page_display_403', 'GET'); + $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST); + $this->assertEqual($response->getStatusCode(), 403); + + $subrequest = Request::create('/test_page_display_404', 'GET'); + $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST); + $this->assertEqual($response->getStatusCode(), 404); + + $subrequest = Request::create('/test_page_display_200', 'GET'); + $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST); + $this->assertEqual($response->getStatusCode(), 200); + + $subrequest = Request::create('/test_page_display_200', 'GET'); + \Drupal::getContainer()->get('request_stack')->push($subrequest); + + // Test accessing a disabled page for a view. + $view = Views::getView('test_page_display'); + // Disable the view, rebuild menu, and request the page again. + $view->storage->disable()->save(); + // Router rebuild would occur in a kernel terminate event so we need to + // simulate that here. + \Drupal::service('router.builder')->rebuild(); + + $response = $this->container->get('http_kernel')->handle($subrequest, HttpKernelInterface::SUB_REQUEST); + $this->assertEqual($response->getStatusCode(), 404); + } + + /** + * Checks that the router items are properly registered + */ + public function testPageRouterItems() { + $collection = \Drupal::service('views.route_subscriber')->routes(); + + // Check the controller defaults. + foreach ($collection as $id => $route) { + $this->assertEqual($route->getDefault('_controller'), 'Drupal\views\Routing\ViewPageController::handle'); + $id_parts = explode('.', $id); + $this->assertEqual($route->getDefault('view_id'), $id_parts[1]); + $this->assertEqual($route->getDefault('display_id'), $id_parts[2]); + } + + // Check the generated patterns and default values. + $route = $collection->get('view.test_page_display_route.page_1'); + $this->assertEqual($route->getPath(), '/test_route_without_arguments'); + + $route = $collection->get('view.test_page_display_route.page_2'); + $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_0}'); + $this->assertTrue($route->hasDefault('arg_0'), 'A default value is set for the optional argument id.'); + + $route = $collection->get('view.test_page_display_route.page_3'); + $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_0}/suffix'); + $this->assertFalse($route->hasDefault('arg_0'), 'No default value is set for the required argument id.'); + + $route = $collection->get('view.test_page_display_route.page_4'); + $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_0}/suffix/{arg_1}'); + $this->assertFalse($route->hasDefault('arg_0'), 'No default value is set for the required argument id.'); + $this->assertTrue($route->hasDefault('arg_1'), 'A default value is set for the optional argument id_2.'); + + $route = $collection->get('view.test_page_display_route.page_5'); + $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_0}/{arg_1}'); + $this->assertTrue($route->hasDefault('arg_0'), 'A default value is set for the optional argument id.'); + $this->assertTrue($route->hasDefault('arg_1'), 'A default value is set for the optional argument id_2.'); + + $route = $collection->get('view.test_page_display_route.page_6'); + $this->assertEqual($route->getPath(), '/test_route_with_argument/{arg_0}/{arg_1}'); + $this->assertFalse($route->hasDefault('arg_0'), 'No default value is set for the required argument id.'); + $this->assertFalse($route->hasDefault('arg_1'), 'No default value is set for the required argument id_2.'); + } + + /** + * Tests the generated menu links of views. + */ + public function testMenuLinks() { + \Drupal::service('plugin.manager.menu.link')->rebuild(); + $tree = \Drupal::menuTree()->load('admin', new MenuTreeParameters()); + $this->assertTrue(isset($tree['system.admin']->subtree['views_view:views.test_page_display_menu.page_4'])); + $menu_link = $tree['system.admin']->subtree['views_view:views.test_page_display_menu.page_4']->link; + $this->assertEqual($menu_link->getTitle(), 'Test child (with parent)'); + $this->assertEqual($menu_link->isExpanded(), TRUE); + $this->assertEqual($menu_link->getDescription(), 'Sample description.'); + } + + /** + * Tests the calculated dependencies for various views using Page displays. + */ + public function testDependencies() { + $view = Views::getView('test_page_display'); + $this->assertIdentical([], $view->getDependencies()); + + $view = Views::getView('test_page_display_route'); + $expected = [ + 'content' => ['StaticTest'], + 'module' => ['views_test_data'], + ]; + $this->assertIdentical($expected, $view->getDependencies()); + + $view = Views::getView('test_page_display_menu'); + $expected = [ + 'config' => [ + 'system.menu.admin', + 'system.menu.tools', + ], + ]; + $this->assertIdentical($expected, $view->getDependencies()); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/JoinTest.php b/core/modules/views/tests/src/Kernel/Plugin/JoinTest.php new file mode 100644 index 0000000..8412825 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/JoinTest.php @@ -0,0 +1,212 @@ +manager = $this->container->get('plugin.manager.views.join'); + } + + /** + * Tests an example join plugin. + */ + public function testExamplePlugin() { + + // Setup a simple join and test the result sql. + $view = Views::getView('test_view'); + $view->initDisplay(); + $view->initQuery(); + + $configuration = array( + 'left_table' => 'views_test_data', + 'left_field' => 'uid', + 'table' => 'users_field_data', + 'field' => 'uid', + ); + $join = $this->manager->createInstance('join_test', $configuration); + $this->assertTrue($join instanceof JoinTestPlugin, 'The correct join class got loaded.'); + + $rand_int = rand(0, 1000); + $join->setJoinValue($rand_int); + + $query = db_select('views_test_data'); + $table = array('alias' => 'users_field_data'); + $join->buildJoin($query, $table, $view->query); + + $tables = $query->getTables(); + $join_info = $tables['users_field_data']; + $this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = $rand_int") !== FALSE, 'Make sure that the custom join plugin can extend the join base and alter the result.'); + } + + /** + * Tests the join plugin base. + */ + public function testBasePlugin() { + + // Setup a simple join and test the result sql. + $view = Views::getView('test_view'); + $view->initDisplay(); + $view->initQuery(); + + // First define a simple join without an extra condition. + // Set the various options on the join object. + $configuration = array( + 'left_table' => 'views_test_data', + 'left_field' => 'uid', + 'table' => 'users_field_data', + 'field' => 'uid', + 'adjusted' => TRUE, + ); + $join = $this->manager->createInstance('standard', $configuration); + $this->assertTrue($join instanceof JoinPluginBase, 'The correct join class got loaded.'); + $this->assertNull($join->extra, 'The field extra was not overridden.'); + $this->assertTrue($join->adjusted, 'The field adjusted was set correctly.'); + + // Build the actual join values and read them back from the dbtng query + // object. + $query = db_select('views_test_data'); + $table = array('alias' => 'users_field_data'); + $join->buildJoin($query, $table, $view->query); + + $tables = $query->getTables(); + $join_info = $tables['users_field_data']; + $this->assertEqual($join_info['join type'], 'LEFT', 'Make sure the default join type is LEFT'); + $this->assertEqual($join_info['table'], $configuration['table']); + $this->assertEqual($join_info['alias'], 'users_field_data'); + $this->assertEqual($join_info['condition'], 'views_test_data.uid = users_field_data.uid'); + + // Set a different alias and make sure table info is as expected. + $join = $this->manager->createInstance('standard', $configuration); + $table = array('alias' => 'users1'); + $join->buildJoin($query, $table, $view->query); + + $tables = $query->getTables(); + $join_info = $tables['users1']; + $this->assertEqual($join_info['alias'], 'users1'); + + // Set a different join type (INNER) and make sure it is used. + $configuration['type'] = 'INNER'; + $join = $this->manager->createInstance('standard', $configuration); + $table = array('alias' => 'users2'); + $join->buildJoin($query, $table, $view->query); + + $tables = $query->getTables(); + $join_info = $tables['users2']; + $this->assertEqual($join_info['join type'], 'INNER'); + + // Setup addition conditions and make sure it is used. + $random_name_1 = $this->randomMachineName(); + $random_name_2 = $this->randomMachineName(); + $configuration['extra'] = array( + array( + 'field' => 'name', + 'value' => $random_name_1 + ), + array( + 'field' => 'name', + 'value' => $random_name_2, + 'operator' => '<>' + ), + ); + $join = $this->manager->createInstance('standard', $configuration); + $table = array('alias' => 'users3'); + $join->buildJoin($query, $table, $view->query); + + $tables = $query->getTables(); + $join_info = $tables['users3']; + $this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = users3.uid") !== FALSE, 'Make sure the join condition appears in the query.'); + $this->assertTrue(strpos($join_info['condition'], "users3.name = :views_join_condition_0") !== FALSE, 'Make sure the first extra join condition appears in the query and uses the first placeholder.'); + $this->assertTrue(strpos($join_info['condition'], "users3.name <> :views_join_condition_1") !== FALSE, 'Make sure the second extra join condition appears in the query and uses the second placeholder.'); + $this->assertEqual(array_values($join_info['arguments']), array($random_name_1, $random_name_2), 'Make sure the arguments are in the right order'); + + // Test that 'IN' conditions are properly built. + $random_name_1 = $this->randomMachineName(); + $random_name_2 = $this->randomMachineName(); + $random_name_3 = $this->randomMachineName(); + $random_name_4 = $this->randomMachineName(); + $configuration['extra'] = array( + array( + 'field' => 'name', + 'value' => $random_name_1 + ), + array( + 'field' => 'name', + 'value' => array($random_name_2, $random_name_3, $random_name_4), + ), + ); + $join = $this->manager->createInstance('standard', $configuration); + $table = array('alias' => 'users4'); + $join->buildJoin($query, $table, $view->query); + + $tables = $query->getTables(); + $join_info = $tables['users4']; + $this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = users4.uid") !== FALSE, 'Make sure the join condition appears in the query.'); + $this->assertTrue(strpos($join_info['condition'], "users4.name = :views_join_condition_2") !== FALSE, 'Make sure the first extra join condition appears in the query.'); + $this->assertTrue(strpos($join_info['condition'], "users4.name IN ( :views_join_condition_3, :views_join_condition_4, :views_join_condition_5 )") !== FALSE, 'The IN condition for the join is properly formed.'); + + // Test that all the conditions are properly built. + $configuration['extra'] = array( + array( + 'field' => 'langcode', + 'value' => 'en' + ), + array( + 'left_field' => 'status', + 'value' => 0, + 'numeric' => TRUE, + ), + array( + 'field' => 'name', + 'left_field' => 'name' + ), + ); + $join = $this->manager->createInstance('standard', $configuration); + $table = array('alias' => 'users5'); + $join->buildJoin($query, $table, $view->query); + + $tables = $query->getTables(); + $join_info = $tables['users5']; + $this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = users5.uid") !== FALSE, 'Make sure the join condition appears in the query.'); + $this->assertTrue(strpos($join_info['condition'], "users5.langcode = :views_join_condition_6") !== FALSE, 'Make sure the first extra join condition appears in the query.'); + $this->assertTrue(strpos($join_info['condition'], "views_test_data.status = :views_join_condition_7") !== FALSE, 'Make sure the second extra join condition appears in the query.'); + $this->assertTrue(strpos($join_info['condition'], "users5.name = views_test_data.name") !== FALSE, 'Make sure the third extra join condition appears in the query.'); + $this->assertEqual(array_values($join_info['arguments']), array('en', 0), 'Make sure the arguments are in the right order'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/PagerKernelTest.php b/core/modules/views/tests/src/Kernel/Plugin/PagerKernelTest.php new file mode 100644 index 0000000..b589de9 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/PagerKernelTest.php @@ -0,0 +1,74 @@ +installEntitySchema('node'); + $this->installEntitySchema('user'); + } + + /** + * Tests pager-related setter methods on ViewExecutable. + * + * @see \Drupal\views\ViewExecutable::setItemsPerPage + * @see \Drupal\views\ViewExecutable::setOffset + * @see \Drupal\views\ViewExecutable::setCurrentPage + */ + public function testSetPagerMethods() { + $view = Views::getView('test_pager_full'); + + // Mark the view as cacheable in order have the cache checking working + // below. + $display = &$view->storage->getDisplay('default'); + $display['display_options']['cache']['type'] = 'tag'; + $view->storage->save(); + + $output = $view->preview(); + + \Drupal::service('renderer')->renderPlain($output); + $this->assertIdentical(CacheBackendInterface::CACHE_PERMANENT, $output['#cache']['max-age']); + + foreach (['setItemsPerPage', 'setOffset', 'setCurrentPage'] as $method) { + $view = Views::getView('test_pager_full'); + $view->setDisplay('default'); + $view->{$method}(1); + $output = $view->preview(); + + \Drupal::service('renderer')->renderPlain($output); + $this->assertIdentical(CacheBackendInterface::CACHE_PERMANENT, $output['#cache']['max-age'], 'Max age kept.'); + } + + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/PluginKernelTestBase.php b/core/modules/views/tests/src/Kernel/Plugin/PluginKernelTestBase.php new file mode 100644 index 0000000..2c41446 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/PluginKernelTestBase.php @@ -0,0 +1,17 @@ +_testInitQuery(); + $this->_testQueryExecute(); + $this->queryMethodsTests(); + } + + /** + * Tests the ViewExecutable::initQuery method. + */ + public function _testInitQuery() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + $view->initQuery(); + $this->assertTrue($view->query instanceof QueryTestPlugin, 'Make sure the right query plugin got instantiated.'); + } + + public function _testQueryExecute() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + $view->initQuery(); + $view->query->setAllItems($this->dataSet()); + + $this->executeView($view); + $this->assertTrue($view->result, 'Make sure the view result got filled'); + } + + /** + * Test methods provided by the QueryPluginBase. + * + * @see \Drupal\views\Plugin\views\query\QueryPluginBase + */ + protected function queryMethodsTests() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + $view->initQuery(); + $this->assertFalse($view->query->getLimit(), 'Default to an empty limit.'); + $rand_number = rand(5, 10); + $view->query->setLimit($rand_number); + $this->assertEqual($view->query->getLimit(), $rand_number, 'set_limit adapts the amount of items.'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/RelationshipJoinTestBase.php b/core/modules/views/tests/src/Kernel/Plugin/RelationshipJoinTestBase.php new file mode 100644 index 0000000..3dd3209 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/RelationshipJoinTestBase.php @@ -0,0 +1,88 @@ +installEntitySchema('user'); + $this->installConfig(array('user')); + parent::setUpFixtures(); + + // Create a record for uid 1. + $this->rootUser = User::create(['name' => $this->randomMachineName()]); + $this->rootUser->save(); + + Views::viewsData()->clear(); + } + + /** + * Overrides \Drupal\views\Tests\ViewTestBase::schemaDefinition(). + * + * Adds a uid column to test the relationships. + */ + protected function schemaDefinition() { + $schema = parent::schemaDefinition(); + + $schema['views_test_data']['fields']['uid'] = array( + 'description' => "The {users_field_data}.uid of the author of the beatle entry.", + 'type' => 'int', + 'unsigned' => TRUE, + 'not null' => TRUE, + 'default' => 0 + ); + + return $schema; + } + + /** + * Overrides \Drupal\views\Tests\ViewTestBase::viewsData(). + * + * Adds a relationship for the uid column. + */ + protected function viewsData() { + $data = parent::viewsData(); + $data['views_test_data']['uid'] = array( + 'title' => t('UID'), + 'help' => t('The test data UID'), + 'relationship' => array( + 'id' => 'standard', + 'base' => 'users_field_data', + 'base field' => 'uid' + ) + ); + + return $data; + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/RelationshipTest.php b/core/modules/views/tests/src/Kernel/Plugin/RelationshipTest.php new file mode 100644 index 0000000..2868c18 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/RelationshipTest.php @@ -0,0 +1,184 @@ + 'name', + 'users_field_data_views_test_data_uid' => 'uid', + ); + + /** + * Tests the query result of a view with a relationship. + */ + public function testRelationshipQuery() { + // Set the first entry to have the admin as author. + db_query("UPDATE {views_test_data} SET uid = 1 WHERE id = 1"); + db_query("UPDATE {views_test_data} SET uid = 2 WHERE id <> 1"); + + $view = Views::getView('test_view'); + $view->setDisplay(); + + $view->displayHandlers->get('default')->overrideOption('relationships', array( + 'uid' => array( + 'id' => 'uid', + 'table' => 'views_test_data', + 'field' => 'uid', + ), + )); + + $view->displayHandlers->get('default')->overrideOption('filters', array( + 'uid' => array( + 'id' => 'uid', + 'table' => 'users_field_data', + 'field' => 'uid', + 'relationship' => 'uid', + ), + )); + + $fields = $view->displayHandlers->get('default')->getOption('fields'); + $view->displayHandlers->get('default')->overrideOption('fields', $fields + array( + 'uid' => array( + 'id' => 'uid', + 'table' => 'users_field_data', + 'field' => 'uid', + 'relationship' => 'uid', + ), + )); + + $view->initHandlers(); + + // Check for all beatles created by admin. + $view->filter['uid']->value = array(1); + $this->executeView($view); + + $expected_result = array( + array( + 'name' => 'John', + 'uid' => 1 + ) + ); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + $view->destroy(); + + // Check for all beatles created by another user, which so doesn't exist. + $view->initHandlers(); + $view->filter['uid']->value = array(3); + $this->executeView($view); + $expected_result = array(); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + $view->destroy(); + + // Set the relationship to required, so only results authored by the admin + // should return. + $view->initHandlers(); + $view->relationship['uid']->options['required'] = TRUE; + $this->executeView($view); + + $expected_result = array( + array( + 'name' => 'John', + 'uid' => 1 + ) + ); + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + $view->destroy(); + + // Set the relationship to optional should cause to return all beatles. + $view->initHandlers(); + $view->relationship['uid']->options['required'] = FALSE; + $this->executeView($view); + + $expected_result = $this->dataSet(); + // Alter the expected result to contain the right uids. + foreach ($expected_result as &$row) { + // Only John has an existing author. + if ($row['name'] == 'John') { + $row['uid'] = 1; + } + else { + // The LEFT join should set an empty {users}.uid field. + $row['uid'] = NULL; + } + } + + $this->assertIdenticalResultset($view, $expected_result, $this->columnMap); + } + + /** + * Tests rendering of a view with a relationship. + */ + public function testRelationshipRender() { + $author1 = $this->createUser(); + db_query("UPDATE {views_test_data} SET uid = :uid WHERE id = 1", [':uid' => $author1->id()]); + $author2 = $this->createUser(); + db_query("UPDATE {views_test_data} SET uid = :uid WHERE id = 2", [':uid' => $author2->id()]); + // Set uid to non-existing author uid for row 3. + db_query("UPDATE {views_test_data} SET uid = :uid WHERE id = 3", [':uid' => $author2->id() + 123]); + + $view = Views::getView('test_view'); + // Add a relationship for authors. + $view->getDisplay()->overrideOption('relationships', [ + 'uid' => [ + 'id' => 'uid', + 'table' => 'views_test_data', + 'field' => 'uid', + ], + ]); + // Add fields for {views_test_data}.id and author name. + $view->getDisplay()->overrideOption('fields', [ + 'id' => [ + 'id' => 'id', + 'table' => 'views_test_data', + 'field' => 'id', + ], + 'author' => [ + 'id' => 'author', + 'table' => 'users_field_data', + 'field' => 'name', + 'relationship' => 'uid', + ], + ]); + + // Render the view. + $output = $view->preview(); + $html = $this->container->get('renderer')->renderRoot($output); + $this->setRawContent($html); + + // Check that the output contains correct values. + $xpath = '//div[@class="views-row" and div[@class="views-field views-field-id"]=:id and div[@class="views-field views-field-author"]=:author]'; + $this->assertEqual(1, count($this->xpath($xpath, [':id' => 1, ':author' => $author1->getUsername()]))); + $this->assertEqual(1, count($this->xpath($xpath, [':id' => 2, ':author' => $author2->getUsername()]))); + $this->assertEqual(1, count($this->xpath($xpath, [':id' => 3, ':author' => '']))); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/RowEntityTest.php b/core/modules/views/tests/src/Kernel/Plugin/RowEntityTest.php new file mode 100644 index 0000000..4c8df8e --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/RowEntityTest.php @@ -0,0 +1,73 @@ +installEntitySchema('taxonomy_term'); + $this->installConfig(array('taxonomy')); + \Drupal::service('router.builder')->rebuild(); + } + + /** + * Tests the entity row handler. + */ + public function testEntityRow() { + $vocab = Vocabulary::create(['name' => $this->randomMachineName(), 'vid' => strtolower($this->randomMachineName())]); + $vocab->save(); + $term = Term::create(['name' => $this->randomMachineName(), 'vid' => $vocab->id() ]); + $term->save(); + + $view = Views::getView('test_entity_row'); + $build = $view->preview(); + $this->render($build); + + $this->assertText($term->getName(), 'The rendered entity appears as row in the view.'); + + // Tests the available view mode options. + $form = array(); + $form_state = new FormState(); + $form_state->set('view', $view->storage); + $view->rowPlugin->buildOptionsForm($form, $form_state); + + $this->assertTrue(isset($form['view_mode']['#options']['default']), 'Ensure that the default view mode is available'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php b/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php new file mode 100644 index 0000000..1dc561d --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/RowRenderCacheTest.php @@ -0,0 +1,177 @@ +installEntitySchema('user'); + $this->installEntitySchema('node'); + $this->installSchema('node', 'node_access'); + + $type = NodeType::create(['type' => 'test']); + $type->save(); + + $this->editorUser = $this->createUser(['bypass node access']); + $this->powerUser = $this->createUser(['access content', 'create test content', 'edit own test content', 'delete own test content']); + $this->regularUser = $this->createUser(['access content']); + + // Create some test entities. + for ($i = 0; $i < 5; $i++) { + Node::create(['title' => 'b' . $i . $this->randomMachineName(), 'type' => 'test'])->save(); + } + + // Create a power user node. + Node::create(['title' => 'z' . $this->randomMachineName(), 'uid' => $this->powerUser->id(), 'type' => 'test'])->save(); + } + + /** + * Test complex field rewriting and uncacheable field handlers. + */ + public function testAdvancedCaching() { + // Test that row field output is actually cached and with the proper cache + // contexts. + $this->doTestRenderedOutput($this->editorUser); + $this->doTestRenderedOutput($this->editorUser, TRUE); + $this->doTestRenderedOutput($this->powerUser); + $this->doTestRenderedOutput($this->powerUser, TRUE); + $this->doTestRenderedOutput($this->regularUser); + $this->doTestRenderedOutput($this->regularUser, TRUE); + + // Alter the result set order and check that counter is still working + // correctly. + $this->doTestRenderedOutput($this->editorUser); + /** @var \Drupal\node\NodeInterface $node */ + $node = Node::load(6); + $node->setTitle('a' . $this->randomMachineName()); + $node->save(); + $this->doTestRenderedOutput($this->editorUser); + } + + /** + * Check whether the rendered output matches expectations. + * + * @param \Drupal\Core\Session\AccountInterface $account + * The user account to tests rendering with. + * @param bool $check_cache + * (optional) Whether explicitly test render cache entries. + */ + protected function doTestRenderedOutput(AccountInterface $account, $check_cache = FALSE) { + $this->setCurrentUser($account); + $view = Views::getView('test_row_render_cache'); + $view->setDisplay(); + $view->preview(); + + /** @var \Drupal\Core\Render\RenderCacheInterface $render_cache */ + $render_cache = $this->container->get('render_cache'); + + /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache_plugin */ + $cache_plugin = $view->display_handler->getPlugin('cache'); + + // Retrieve nodes and sort them in alphabetical order to match view results. + $nodes = Node::loadMultiple(); + usort($nodes, function (NodeInterface $a, NodeInterface $b) { + return strcmp($a->label(), $b->label()); + }); + + $index = 0; + foreach ($nodes as $node) { + $nid = $node->id(); + $access = $node->access('update'); + + $counter = $index + 1; + $expected = "$nid: $counter (just in case: $nid)"; + $counter_output = $view->style_plugin->getField($index, 'counter'); + $this->assertEqual($counter_output, $expected); + + $node_url = $node->url(); + $expected = "{$node->label()} $counter_output"; + $output = $view->style_plugin->getField($index, 'title'); + $this->assertEqual($output, $expected); + + $expected = $access ? "edit" : ""; + $output = $view->style_plugin->getField($index, 'edit_node'); + $this->assertEqual($output, $expected); + + $expected = $access ? "delete" : ""; + $output = $view->style_plugin->getField($index, 'delete_node'); + $this->assertEqual($output, $expected); + $expected = $access ? '
                              ' : ''; + $output = $view->style_plugin->getField($index, 'operations'); + $this->assertEqual($output, $expected); + + if ($check_cache) { + $keys = $cache_plugin->getRowCacheKeys($view->result[$index]); + $user_context = !$account->hasPermission('edit any test content') ? 'user' : 'user.permissions'; + $element = $render_cache->get(['#cache' => ['keys' => $keys, 'contexts' => ['languages:language_interface', 'theme', $user_context]]]); + $this->assertTrue($element); + } + + $index++; + } + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/SqlQueryTest.php b/core/modules/views/tests/src/Kernel/Plugin/SqlQueryTest.php new file mode 100644 index 0000000..ec985be --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/SqlQueryTest.php @@ -0,0 +1,87 @@ + 'test_metadata', 'key2' => 'test_metadata2'); + + return $data; + } + + /** + * Tests adding some metadata/tags to the views query. + */ + public function testExecuteMetadata() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + $view->initQuery(); + $view->execute(); + /** @var \Drupal\Core\Database\Query\Select $query */ + $main_query = $view->build_info['query']; + /** @var \Drupal\Core\Database\Query\Select $count_query */ + $count_query = $view->build_info['count_query']; + + foreach (array($main_query, $count_query) as $query) { + // Check query access tags. + $this->assertTrue($query->hasTag('test_tag')); + + // Check metadata. + $this->assertIdentical($query->getMetaData('key1'), 'test_metadata'); + $this->assertIdentical($query->getMetaData('key2'), 'test_metadata2'); + } + + $query_options = $view->display_handler->getOption('query'); + $query_options['options']['disable_sql_rewrite'] = TRUE; + $view->display_handler->setOption('query', $query_options); + $view->save(); + $view->destroy(); + + $view = Views::getView('test_view'); + $view->setDisplay(); + $view->initQuery(); + $view->execute(); + /** @var \Drupal\Core\Database\Query\Select $query */ + $main_query = $view->build_info['query']; + /** @var \Drupal\Core\Database\Query\Select $count_query */ + $count_query = $view->build_info['count_query']; + + foreach (array($main_query, $count_query) as $query) { + // Check query access tags. + $this->assertFalse($query->hasTag('test_tag')); + + // Check metadata. + $this->assertIdentical($query->getMetaData('key1'), NULL); + $this->assertIdentical($query->getMetaData('key2'), NULL); + } + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/StyleHtmlListTest.php b/core/modules/views/tests/src/Kernel/Plugin/StyleHtmlListTest.php new file mode 100644 index 0000000..ab4421b --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/StyleHtmlListTest.php @@ -0,0 +1,58 @@ +preview(); + $output = \Drupal::service('renderer')->renderRoot($output); + + // Check that an empty class attribute is not added if the wrapper class is + // not set. + $this->assertTrue(strpos($output, '
                              ') !== FALSE, 'Empty class is not added to DIV when class is not set'); + + // Check that an empty class attribute is not added if the list class is + // not set. + $this->assertTrue(strpos($output, '
                                ') !== FALSE, 'Empty class is not added to UL when class is not set'); + + // Set wrapper class and list class in style options. + $view->style_plugin->options['class'] = 'class'; + $view->style_plugin->options['wrapper_class'] = 'wrapper-class'; + + $output = $view->preview(); + $output = \Drupal::service('renderer')->renderRoot($output); + + // Check that class attribute is present if the wrapper class is set. + $this->assertTrue(strpos($output, '
                                ') !== FALSE, 'Class is added to DIV'); + + // Check that class attribute is present if the list class is set. + $this->assertTrue(strpos($output, '
                                  ') !== FALSE, 'Class is added to UL'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/StyleMappingTest.php b/core/modules/views/tests/src/Kernel/Plugin/StyleMappingTest.php new file mode 100644 index 0000000..dcf6742 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/StyleMappingTest.php @@ -0,0 +1,86 @@ +mappedOutputHelper($view); + $this->assertTrue(strpos($output, 'job') === FALSE, 'The job field is added to the view but not in the mapping.'); + $view->destroy(); + + $view->setDisplay(); + $view->displayHandlers->get('default')->options['style']['options']['mapping']['name_field'] = 'job'; + $output = $this->mappedOutputHelper($view); + $this->assertTrue(strpos($output, 'job') !== FALSE, 'The job field is added to the view and is in the mapping.'); + } + + /** + * Tests the mapping of fields. + * + * @param \Drupal\views\ViewExecutable $view + * The view to test. + * + * @return string + * The view rendered as HTML. + */ + protected function mappedOutputHelper($view) { + $output = $view->preview(); + $rendered_output = \Drupal::service('renderer')->renderRoot($output); + $this->storeViewPreview($rendered_output); + $rows = $this->elements->body->div->div; + $data_set = $this->dataSet(); + + $count = 0; + foreach ($rows as $row) { + $attributes = $row->attributes(); + $class = (string) $attributes['class'][0]; + $this->assertTrue(strpos($class, 'views-row-mapping-test') !== FALSE, 'Make sure that each row has the correct CSS class.'); + + foreach ($row->div as $field) { + // Split up the field-level class, the first part is the mapping name + // and the second is the field ID. + $field_attributes = $field->attributes(); + $name = strtok((string) $field_attributes['class'][0], '-'); + $field_id = strtok('-'); + + // The expected result is the mapping name and the field value, + // separated by ':'. + $expected_result = $name . ':' . $data_set[$count][$field_id]; + $actual_result = (string) $field; + $this->assertIdentical($expected_result, $actual_result, format_string('The fields were mapped successfully: %name => %field_id', array('%name' => $name, '%field_id' => $field_id))); + } + + $count++; + } + + return $rendered_output; + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/StyleTableUnitTest.php b/core/modules/views/tests/src/Kernel/Plugin/StyleTableUnitTest.php new file mode 100644 index 0000000..adffd6f --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/StyleTableUnitTest.php @@ -0,0 +1,161 @@ +setDisplay('default'); + $view->initStyle(); + $view->initHandlers(); + $view->initQuery(); + $style_plugin = $view->style_plugin; + + // Test the buildSort() method. + $request = new Request(); + $view->setRequest($request); + + $style_plugin->options['override'] = FALSE; + + $style_plugin->options['default'] = ''; + $this->assertTrue($style_plugin->buildSort(), 'If no order and no default order is specified, the normal sort should be used.'); + + $style_plugin->options['default'] = 'id'; + $this->assertTrue($style_plugin->buildSort(), 'If no order but a default order is specified, the normal sort should be used.'); + + $request->attributes->set('order', $this->randomMachineName()); + $this->assertTrue($style_plugin->buildSort(), 'If no valid field is chosen for order, the normal sort should be used.'); + + $request->attributes->set('order', 'id'); + $this->assertTrue($style_plugin->buildSort(), 'If a valid order is specified but the table is configured to not override, the normal sort should be used.'); + + $style_plugin->options['override'] = TRUE; + + $this->assertFalse($style_plugin->buildSort(), 'If a valid order is specified and the table is configured to override, the normal sort should not be used.'); + + // Test the buildSortPost() method. + $request = new Request(); + $view->setRequest($request); + + // Setup no valid default. + $this->prepareView($view); + $style_plugin = $view->style_plugin; + $style_plugin->options['default'] = ''; + $style_plugin->buildSortPost(); + $this->assertIdentical($style_plugin->order, NULL, 'No sort order was set, when no order was specified and no default column was selected.'); + $this->assertIdentical($style_plugin->active, NULL, 'No sort field was set, when no order was specified and no default column was selected.'); + $view->destroy(); + + // Setup a valid default + column specific default sort order. + $this->prepareView($view); + $style_plugin = $view->style_plugin; + $style_plugin->options['default'] = 'id'; + $style_plugin->options['info']['id']['default_sort_order'] = 'desc'; + $style_plugin->buildSortPost(); + $this->assertIdentical($style_plugin->order, 'desc', 'Fallback to the right default sort order.'); + $this->assertIdentical($style_plugin->active, 'id', 'Fallback to the right default sort field.'); + $view->destroy(); + + // Setup a valid default + table default sort order. + $this->prepareView($view); + $style_plugin = $view->style_plugin; + $style_plugin->options['default'] = 'id'; + $style_plugin->options['info']['id']['default_sort_order'] = NULL; + $style_plugin->options['order'] = 'asc'; + $style_plugin->buildSortPost(); + $this->assertIdentical($style_plugin->order, 'asc', 'Fallback to the right default sort order.'); + $this->assertIdentical($style_plugin->active, 'id', 'Fallback to the right default sort field.'); + $view->destroy(); + + // Use an invalid field. + $this->prepareView($view); + $style_plugin = $view->style_plugin; + $request->query->set('sort', 'asc'); + $random_name = $this->randomMachineName(); + $request->query->set('order', $random_name); + $style_plugin->buildSortPost(); + $this->assertIdentical($style_plugin->order, 'asc', 'No sort order was set, when invalid sort order was specified.'); + $this->assertIdentical($style_plugin->active, NULL, 'No sort field was set, when invalid sort order was specified.'); + $view->destroy(); + + // Use a existing field, and sort both ascending and descending. + foreach (array('asc', 'desc') as $order) { + $this->prepareView($view); + $style_plugin = $view->style_plugin; + $request->query->set('sort', $order); + $request->query->set('order', 'id'); + $style_plugin->buildSortPost(); + $this->assertIdentical($style_plugin->order, $order, 'Ensure the right sort order was set.'); + $this->assertIdentical($style_plugin->active, 'id', 'Ensure the right order was set.'); + $view->destroy(); + } + + $view->destroy(); + + // Excluded field to make sure its wrapping td doesn't show. + $this->prepareView($view); + $view->field['name']->options['exclude'] = TRUE; + $output = $view->preview(); + $output = \Drupal::service('renderer')->renderRoot($output); + $this->assertFalse(strpos($output, 'views-field-name') !== FALSE, "Excluded field's wrapper was not rendered."); + $view->destroy(); + + // Render a non empty result, and ensure that the empty area handler is not + // rendered. + $this->executeView($view); + $output = $view->preview(); + $output = \Drupal::service('renderer')->renderRoot($output); + + $this->assertFalse(strpos($output, 'custom text') !== FALSE, 'Empty handler was not rendered on a non empty table.'); + + // Render an empty result, and ensure that the area handler is rendered. + $view->setDisplay('default'); + $view->executed = TRUE; + $view->result = array(); + $output = $view->preview(); + $output = \Drupal::service('renderer')->renderRoot($output); + + $this->assertTrue(strpos($output, 'custom text') !== FALSE, 'Empty handler got rendered on an empty table.'); + } + + /** + * Prepares a view executable by initializing everything which is needed. + * + * @param \Drupal\views\ViewExecutable $view + * The executable to prepare. + */ + protected function prepareView(ViewExecutable $view) { + $view->setDisplay(); + $view->initStyle(); + $view->initHandlers(); + $view->initQuery(); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/StyleTestBase.php b/core/modules/views/tests/src/Kernel/Plugin/StyleTestBase.php new file mode 100644 index 0000000..4daed1f --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/StyleTestBase.php @@ -0,0 +1,38 @@ +loadHTML('' . $output . ''); + if ($htmlDom) { + // It's much easier to work with simplexml than DOM, luckily enough + // we can just simply import our DOM tree. + $this->elements = simplexml_import_dom($htmlDom); + } + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/StyleUnformattedTest.php b/core/modules/views/tests/src/Kernel/Plugin/StyleUnformattedTest.php new file mode 100644 index 0000000..2b5575c --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/StyleUnformattedTest.php @@ -0,0 +1,47 @@ +setDisplay(); + $output = $view->preview(); + $this->storeViewPreview(\Drupal::service('renderer')->renderRoot($output)); + + $rows = $this->elements->body->div->div; + $count = 0; + $count_result = count($view->result); + foreach ($rows as $row) { + $count++; + $attributes = $row->attributes(); + $class = (string) $attributes['class'][0]; + $this->assertTrue(strpos($class, 'views-row') !== FALSE, 'Make sure that the views row class is set right.'); + } + $this->assertIdentical($count, $count_result); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php b/core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php new file mode 100644 index 0000000..a3ab063 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php @@ -0,0 +1,59 @@ + 'views', + ); + $plugin_id = 'views_block:test_view_block-block_1'; + $views_block = ViewsBlock::create($this->container, array(), $plugin_id, $plugin_definition); + + $this->assertEqual($views_block->getMachineNameSuggestion(), 'views_block__test_view_block_block_1'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/PluginInstanceTest.php b/core/modules/views/tests/src/Kernel/PluginInstanceTest.php new file mode 100644 index 0000000..0e33855 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/PluginInstanceTest.php @@ -0,0 +1,101 @@ +definitions = Views::getPluginDefinitions(); + } + + /** + * Confirms that there is plugin data for all views plugin types. + */ + public function testPluginData() { + // Check that we have an array of data. + $this->assertTrue(is_array($this->definitions), 'Plugin data is an array.'); + + // Check all plugin types. + foreach ($this->pluginTypes as $type) { + $this->assertTrue(array_key_exists($type, $this->definitions), format_string('Key for plugin type @type found.', array('@type' => $type))); + $this->assertTrue(is_array($this->definitions[$type]) && !empty($this->definitions[$type]), format_string('Plugin type @type has an array of plugins.', array('@type' => $type))); + } + + // Tests that the plugin list has not missed any types. + $diff = array_diff(array_keys($this->definitions), $this->pluginTypes); + $this->assertTrue(empty($diff), 'All plugins were found and matched.'); + } + + /** + * Tests creating instances of every views plugin. + * + * This will iterate through all plugins from _views_fetch_plugin_data(). + */ + public function testPluginInstances() { + foreach ($this->definitions as $type => $plugins) { + // Get a plugin manager for this type. + $manager = $this->container->get("plugin.manager.views.$type"); + foreach ($plugins as $id => $definition) { + // Get a reflection class for this plugin. + // We only want to test true plugins, i.e. They extend PluginBase. + $reflection = new \ReflectionClass($definition['class']); + if ($reflection->isSubclassOf('Drupal\views\Plugin\views\PluginBase')) { + // Create a plugin instance and check what it is. This is not just + // good to check they can be created but for throwing any notices for + // method signatures etc. too. + $instance = $manager->createInstance($id); + $this->assertTrue($instance instanceof $definition['class'], format_string('Instance of @type:@id created', array('@type' => $type, '@id' => $id))); + } + } + } + } + +} diff --git a/core/modules/views/tests/src/Kernel/QueryGroupByTest.php b/core/modules/views/tests/src/Kernel/QueryGroupByTest.php new file mode 100644 index 0000000..69df4dc --- /dev/null +++ b/core/modules/views/tests/src/Kernel/QueryGroupByTest.php @@ -0,0 +1,340 @@ +installEntitySchema('user'); + $this->installEntitySchema('entity_test'); + $this->installEntitySchema('entity_test_mul'); + + $this->storage = $this->container->get('entity.manager')->getStorage('entity_test'); + + ConfigurableLanguage::createFromLangcode('it')->save(); + } + + + /** + * Tests aggregate count feature. + */ + public function testAggregateCount() { + $this->setupTestEntities(); + + $view = Views::getView('test_aggregate_count'); + $this->executeView($view); + + $this->assertEqual(count($view->result), 2, 'Make sure the count of items is right.'); + + $types = array(); + foreach ($view->result as $item) { + // num_records is a alias for id. + $types[$item->entity_test_name] = $item->num_records; + } + + $this->assertEqual($types['name1'], 4, 'Groupby the name: name1 returned the expected amount of results.'); + $this->assertEqual($types['name2'], 3, 'Groupby the name: name2 returned the expected amount of results.'); + } + + /** + * Provides a test helper which runs a view with some aggregation function. + * + * @param string|null $aggregation_function + * Which aggregation function should be used, for example sum or count. If + * NULL is passed the aggregation will be tested with no function. + * @param array $values + * The expected views result. + */ + public function groupByTestHelper($aggregation_function, $values) { + $this->setupTestEntities(); + + $view = Views::getView('test_group_by_count'); + $view->setDisplay(); + // There is no need for a function in order to have aggregation. + if (empty($aggregation_function)) { + // The test table has 2 fields ('id' and 'name'). We'll remove 'id' + // because it's unique and will test aggregation on 'name'. + unset($view->displayHandlers->get('default')->options['fields']['id']); + } + else { + $view->displayHandlers->get('default')->options['fields']['id']['group_type'] = $aggregation_function; + } + + $this->executeView($view); + + $this->assertEqual(count($view->result), 2, 'Make sure the count of items is right.'); + // Group by name to identify the right count. + $results = array(); + foreach ($view->result as $item) { + $results[$item->entity_test_name] = $item->id; + } + $this->assertEqual($results['name1'], $values[0], format_string('Aggregation with @aggregation_function and groupby name: name1 returned the expected amount of results', array('@aggregation_function' => $aggregation_function))); + $this->assertEqual($results['name2'], $values[1], format_string('Aggregation with @aggregation_function and groupby name: name2 returned the expected amount of results', array('@aggregation_function' => $aggregation_function))); + } + + /** + * Helper method that creates some test entities. + */ + protected function setupTestEntities() { + // Create 4 entities with name1 and 3 entities with name2. + $entity_1 = array( + 'name' => 'name1', + ); + + $this->storage->create($entity_1)->save(); + $this->storage->create($entity_1)->save(); + $this->storage->create($entity_1)->save(); + $this->storage->create($entity_1)->save(); + + $entity_2 = array( + 'name' => 'name2', + ); + $this->storage->create($entity_2)->save(); + $this->storage->create($entity_2)->save(); + $this->storage->create($entity_2)->save(); + } + + /** + * Tests the count aggregation function. + */ + public function testGroupByCount() { + $this->groupByTestHelper('count', array(4, 3)); + } + + /** + * Tests the sum aggregation function. + */ + public function testGroupBySum() { + $this->groupByTestHelper('sum', array(10, 18)); + } + + /** + * Tests the average aggregation function. + */ + public function testGroupByAverage() { + $this->groupByTestHelper('avg', array(2.5, 6)); + } + + /** + * Tests the min aggregation function. + */ + public function testGroupByMin() { + $this->groupByTestHelper('min', array(1, 5)); + } + + /** + * Tests the max aggregation function. + */ + public function testGroupByMax() { + $this->groupByTestHelper('max', array(4, 7)); + } + + /** + * Tests aggregation with no specific function. + */ + public function testGroupByNone() { + $this->groupByTestHelper(NULL, array(1, 5)); + } + + /** + * Tests groupby with filters. + */ + public function testGroupByCountOnlyFilters() { + // Check if GROUP BY and HAVING are included when a view + // doesn't display SUM, COUNT, MAX, etc. functions in SELECT statement. + + for ($x = 0; $x < 10; $x++) { + $this->storage->create(array('name' => 'name1'))->save(); + } + + $view = Views::getView('test_group_by_in_filters'); + $this->executeView($view); + + $this->assertTrue(strpos($view->build_info['query'], 'GROUP BY'), 'Make sure that GROUP BY is in the query'); + $this->assertTrue(strpos($view->build_info['query'], 'HAVING'), 'Make sure that HAVING is in the query'); + } + + /** + * Tests grouping on base field. + */ + public function testGroupByBaseField() { + $this->setupTestEntities(); + + $view = Views::getView('test_group_by_count'); + $view->setDisplay(); + // This tests that the GROUP BY portion of the query is properly formatted + // to include the base table to avoid ambiguous field errors. + $view->displayHandlers->get('default')->options['fields']['name']['group_type'] = 'min'; + unset($view->displayHandlers->get('default')->options['fields']['id']['group_type']); + $this->executeView($view); + $this->assertTrue(strpos($view->build_info['query'], 'GROUP BY entity_test.id'), 'GROUP BY field includes the base table name when grouping on the base field.'); + } + + /** + * Tests grouping a field with cardinality > 1. + */ + public function testGroupByFieldWithCardinality() { + $field_storage = FieldStorageConfig::create([ + 'type' => 'integer', + 'field_name' => 'field_test', + 'cardinality' => 4, + 'entity_type' => 'entity_test_mul', + ]); + $field_storage->save(); + $field = FieldConfig::create([ + 'field_name' => 'field_test', + 'entity_type' => 'entity_test_mul', + 'bundle' => 'entity_test_mul', + ]); + $field->save(); + + $entities = []; + $entity = EntityTestMul::create([ + 'field_test' => [1, 1, 1], + ]); + $entity->save(); + $entities[] = $entity; + + $entity = EntityTestMul::create([ + 'field_test' => [2, 2, 2], + ]); + $entity->save(); + $entities[] = $entity; + + $entity = EntityTestMul::create([ + 'field_test' => [2, 2, 2], + ]); + $entity->save(); + $entities[] = $entity; + + $view = Views::getView('test_group_by_count_multicardinality'); + $this->executeView($view); + $this->assertEqual(2, count($view->result)); + + $this->assertEqual('3', $view->getStyle()->getField(0, 'id')); + $this->assertEqual('1', $view->getStyle()->getField(0, 'field_test')); + $this->assertEqual('6', $view->getStyle()->getField(1, 'id')); + $this->assertEqual('2', $view->getStyle()->getField(1, 'field_test')); + + $entities[2]->field_test[0]->value = 3; + $entities[2]->field_test[1]->value = 4; + $entities[2]->field_test[2]->value = 5; + $entities[2]->save(); + + $view = Views::getView('test_group_by_count_multicardinality'); + $this->executeView($view); + $this->assertEqual(5, count($view->result)); + + $this->assertEqual('3', $view->getStyle()->getField(0, 'id')); + $this->assertEqual('1', $view->getStyle()->getField(0, 'field_test')); + $this->assertEqual('3', $view->getStyle()->getField(1, 'id')); + $this->assertEqual('2', $view->getStyle()->getField(1, 'field_test')); + $this->assertEqual('1', $view->getStyle()->getField(2, 'id')); + $this->assertEqual('3', $view->getStyle()->getField(2, 'field_test')); + $this->assertEqual('1', $view->getStyle()->getField(3, 'id')); + $this->assertEqual('4', $view->getStyle()->getField(3, 'field_test')); + $this->assertEqual('1', $view->getStyle()->getField(4, 'id')); + $this->assertEqual('5', $view->getStyle()->getField(4, 'field_test')); + + // Check that translated values are correctly retrieved and are not grouped + // into the original entity. + $translation = $entity->addTranslation('it'); + $translation->field_test = [6, 6, 6]; + $translation->save(); + + $view = Views::getView('test_group_by_count_multicardinality'); + $this->executeView($view); + + $this->assertEqual(6, count($view->result)); + $this->assertEqual('3', $view->getStyle()->getField(5, 'id')); + $this->assertEqual('6', $view->getStyle()->getField(5, 'field_test')); + } + + /** + * Tests groupby with a field not existing on some bundle. + */ + public function testGroupByWithFieldsNotExistingOnBundle() { + $field_storage = FieldStorageConfig::create([ + 'type' => 'integer', + 'field_name' => 'field_test', + 'cardinality' => 4, + 'entity_type' => 'entity_test_mul', + ]); + $field_storage->save(); + $field = FieldConfig::create([ + 'field_name' => 'field_test', + 'entity_type' => 'entity_test_mul', + 'bundle' => 'entity_test_mul', + ]); + $field->save(); + + $entities = []; + $entity = EntityTestMul::create([ + 'field_test' => [1], + 'type' => 'entity_test_mul', + ]); + $entity->save(); + $entities[] = $entity; + + $entity = EntityTestMul::create([ + 'type' => 'entity_test_mul2', + ]); + $entity->save(); + $entities[] = $entity; + + $view = Views::getView('test_group_by_field_not_within_bundle'); + $this->executeView($view); + + $this->assertEqual(2, count($view->result)); + // The first result is coming from entity_test_mul2, so no field could be + // rendered. + $this->assertEqual('', $view->getStyle()->getField(0, 'field_test')); + // The second result is coming from entity_test_mul, so its field value + // could be rendered. + $this->assertEqual('1', $view->getStyle()->getField(1, 'field_test')); + } + +} diff --git a/core/modules/views/tests/src/Kernel/RenderCacheIntegrationTest.php b/core/modules/views/tests/src/Kernel/RenderCacheIntegrationTest.php new file mode 100644 index 0000000..c7f07db --- /dev/null +++ b/core/modules/views/tests/src/Kernel/RenderCacheIntegrationTest.php @@ -0,0 +1,308 @@ +installEntitySchema('entity_test'); + $this->installEntitySchema('user'); + } + + /** + * Tests a field-based view's cache tags when using the "none" cache plugin. + */ + public function testFieldBasedViewCacheTagsWithCachePluginNone() { + $view = Views::getview('entity_test_fields'); + $view->getDisplay()->overrideOption('cache', [ + 'type' => 'none', + ]); + $view->save(); + + $this->assertCacheTagsForFieldBasedView(FALSE); + } + + /** + * Tests a field-based view's cache tags when using the "tag" cache plugin. + */ + public function testFieldBasedViewCacheTagsWithCachePluginTag() { + $view = Views::getview('entity_test_fields'); + $view->getDisplay()->overrideOption('cache', [ + 'type' => 'tag', + ]); + $view->save(); + + $this->assertCacheTagsForFieldBasedView(TRUE); + } + + /** + * Tests a field-based view's cache tags when using the "time" cache plugin. + */ + public function testFieldBasedViewCacheTagsWithCachePluginTime() { + $view = Views::getview('entity_test_fields'); + $view->getDisplay()->overrideOption('cache', [ + 'type' => 'time', + 'options' => [ + 'results_lifespan' => 3600, + 'output_lifespan' => 3600, + ], + ]); + $view->save(); + + $this->assertCacheTagsForFieldBasedView(TRUE); + } + + /** + * Tests cache tags on output & result cache items for a field-based view. + * + * @param bool $do_assert_views_caches + * Whether to check Views' result & output caches. + */ + protected function assertCacheTagsForFieldBasedView($do_assert_views_caches) { + $this->pass('Checking cache tags for field-based view.'); + $view = Views::getview('entity_test_fields'); + + // Empty result (no entities yet). + $this->pass('Test without entities'); + $base_tags = ['config:views.view.entity_test_fields', 'entity_test_list']; + $this->assertViewsCacheTags($view, $base_tags, $do_assert_views_caches, $base_tags); + $this->assertViewsCacheTagsFromStaticRenderArray($view, $base_tags, $do_assert_views_caches); + + + // Non-empty result (1 entity). + /** @var \Drupal\Core\Entity\EntityInterface[] $entities */ + $entities[] = $entity = EntityTest::create(); + $entity->save(); + + $this->pass('Test with entities'); + $tags_with_entity = Cache::mergeTags($base_tags, $entities[0]->getCacheTags()); + $this->assertViewsCacheTags($view, $tags_with_entity, $do_assert_views_caches, $tags_with_entity); + $this->assertViewsCacheTagsFromStaticRenderArray($view, $tags_with_entity, $do_assert_views_caches); + + + // Paged result (more entities than the items-per-page limit). + for ($i = 0; $i < 5; $i++) { + $entities[] = $entity = EntityTest::create(); + $entity->save(); + } + + // Page 1. + $this->pass('Test pager'); + $this->pass('Page 1'); + \Drupal::request()->query->set('page', 0); + $tags_page_1 = Cache::mergeTags($base_tags, $entities[1]->getCacheTags()); + $tags_page_1 = Cache::mergeTags($tags_page_1, $entities[2]->getCacheTags()); + $tags_page_1 = Cache::mergeTags($tags_page_1, $entities[3]->getCacheTags()); + $tags_page_1 = Cache::mergeTags($tags_page_1, $entities[4]->getCacheTags()); + $tags_page_1 = Cache::mergeTags($tags_page_1, $entities[5]->getCacheTags()); + $this->assertViewsCacheTags($view, $tags_page_1, $do_assert_views_caches, $tags_page_1); + $this->assertViewsCacheTagsFromStaticRenderArray($view, $tags_page_1, $do_assert_views_caches); + $view->destroy(); + // Page 2. + $this->pass('Page 2'); + $view->setCurrentPage(1); + \Drupal::request()->query->set('page', 1); + $tags_page_2 = Cache::mergeTags($base_tags, $entities[0]->getCacheTags()); + $this->assertViewsCacheTags($view, $tags_page_2, $do_assert_views_caches, $tags_page_2); + $view->destroy(); + + // Ensure that invalidation works on both pages. + $this->pass('Page invalidations'); + $this->pass('Page 2'); + $view->setCurrentPage(1); + \Drupal::request()->query->set('page', 1); + $entities[0]->name->value = $random_name = $this->randomMachineName(); + $entities[0]->save(); + $build = $this->assertViewsCacheTags($view, $tags_page_2, $do_assert_views_caches, $tags_page_2); + // @todo Static render arrays don't support different pages yet, see + // https://www.drupal.org/node/2500701. + // $this->assertViewsCacheTagsFromStaticRenderArray($view, $tags_page_2, $do_assert_views_caches); + $this->assertTrue(strpos($build['#markup'], $random_name) !== FALSE); + $view->destroy(); + + $this->pass('Page 1'); + $view->setCurrentPage(0); + \Drupal::request()->query->set('page', 0); + $entities[1]->name->value = $random_name = $this->randomMachineName(); + $entities[1]->save(); + $build = $this->assertViewsCacheTags($view, $tags_page_1, $do_assert_views_caches, $tags_page_1); + $this->assertViewsCacheTagsFromStaticRenderArray($view, $tags_page_1, $do_assert_views_caches); + $this->assertTrue(strpos($build['#markup'], $random_name) !== FALSE); + $view->destroy(); + + // Setup arguments to ensure that render caching also varies by them. + $this->pass('Test arguments'); + + // Custom assert for a single result row. + $single_entity_assertions = function(array $build, EntityInterface $entity) { + $this->setRawContent($build['#markup']); + + $result = $this->cssSelect('div.views-row'); + $count = count($result); + $this->assertEqual($count, 1); + + $this->assertEqual((string) $result[0]->div->span, (string) $entity->id()); + }; + + // Execute the view once with a static renderable and one with a full + // prepared render array. + $tags_argument = Cache::mergeTags($base_tags, $entities[0]->getCacheTags()); + $view->setArguments([$entities[0]->id()]); + $build = $this->assertViewsCacheTags($view, $tags_argument, $do_assert_views_caches, $tags_argument); + $single_entity_assertions($build, $entities[0]); + + $view->setArguments([$entities[0]->id()]); + $build = $this->assertViewsCacheTagsFromStaticRenderArray($view, $tags_argument, $do_assert_views_caches); + $single_entity_assertions($build, $entities[0]); + + // Set a different argument and ensure that the result is different. + $tags2_argument = Cache::mergeTags($base_tags, $entities[1]->getCacheTags()); + $view->setArguments([$entities[1]->id()]); + $build = $this->assertViewsCacheTagsFromStaticRenderArray($view, $tags2_argument, $do_assert_views_caches); + $single_entity_assertions($build, $entities[1]); + + $view->destroy(); + } + + /** + * Tests a entity-based view's cache tags when using the "none" cache plugin. + */ + public function testEntityBasedViewCacheTagsWithCachePluginNone() { + $view = Views::getview('entity_test_row'); + $view->getDisplay()->overrideOption('cache', [ + 'type' => 'none', + ]); + $view->save(); + + $this->assertCacheTagsForEntityBasedView(FALSE); + } + + /** + * Tests a entity-based view's cache tags when using the "tag" cache plugin. + */ + public function testEntityBasedViewCacheTagsWithCachePluginTag() { + $view = Views::getview('entity_test_row'); + $view->getDisplay()->overrideOption('cache', [ + 'type' => 'tag', + ]); + $view->save(); + + $this->assertCacheTagsForEntityBasedView(TRUE); + } + + /** + * Tests a entity-based view's cache tags when using the "time" cache plugin. + */ + public function testEntityBasedViewCacheTagsWithCachePluginTime() { + $view = Views::getview('entity_test_row'); + $view->getDisplay()->overrideOption('cache', [ + 'type' => 'time', + 'options' => [ + 'results_lifespan' => 3600, + 'output_lifespan' => 3600, + ], + ]); + $view->save(); + + $this->assertCacheTagsForEntityBasedView(TRUE); + } + + /** + * Tests cache tags on output & result cache items for an entity-based view. + */ + protected function assertCacheTagsForEntityBasedView($do_assert_views_caches) { + $this->pass('Checking cache tags for entity-based view.'); + $view = Views::getview('entity_test_row'); + + // Empty result (no entities yet). + $base_tags = $base_render_tags = ['config:views.view.entity_test_row', 'entity_test_list']; + $this->assertViewsCacheTags($view, $base_tags, $do_assert_views_caches, $base_tags); + $this->assertViewsCacheTagsFromStaticRenderArray($view, $base_tags, $do_assert_views_caches); + + // Non-empty result (1 entity). + $entities[] = $entity = EntityTest::create(); + $entity->save(); + + $result_tags_with_entity = Cache::mergeTags($base_tags, $entities[0]->getCacheTags()); + $render_tags_with_entity = Cache::mergeTags($base_render_tags, $entities[0]->getCacheTags()); + $render_tags_with_entity = Cache::mergeTags($render_tags_with_entity, ['entity_test_view']); + $this->assertViewsCacheTags($view, $result_tags_with_entity, $do_assert_views_caches, $render_tags_with_entity); + $this->assertViewsCacheTagsFromStaticRenderArray($view, $render_tags_with_entity, $do_assert_views_caches); + + + // Paged result (more entities than the items-per-page limit). + for ($i = 0; $i < 5; $i++) { + $entities[] = $entity = EntityTest::create(); + $entity->save(); + } + + $new_entities_cache_tags = Cache::mergeTags($entities[1]->getCacheTags(), $entities[2]->getCacheTags()); + $new_entities_cache_tags = Cache::mergeTags($new_entities_cache_tags, $entities[3]->getCacheTags()); + $new_entities_cache_tags = Cache::mergeTags($new_entities_cache_tags, $entities[4]->getCacheTags()); + $new_entities_cache_tags = Cache::mergeTags($new_entities_cache_tags, $entities[5]->getCacheTags()); + $result_tags_page_1 = Cache::mergeTags($base_tags, $new_entities_cache_tags); + $render_tags_page_1 = Cache::mergeTags($base_render_tags, $new_entities_cache_tags); + $render_tags_page_1 = Cache::mergeTags($render_tags_page_1, ['entity_test_view']); + $this->assertViewsCacheTags($view, $result_tags_page_1, $do_assert_views_caches, $render_tags_page_1); + $this->assertViewsCacheTagsFromStaticRenderArray($view, $render_tags_page_1, $do_assert_views_caches); + } + + /** + * Ensure that the view renderable contains the cache contexts. + */ + public function testBuildRenderableWithCacheContexts() { + $view = View::load('test_view'); + $display =& $view->getDisplay('default'); + $display['cache_metadata']['contexts'] = ['views_test_cache_context']; + $executable = $view->getExecutable(); + + $build = $executable->buildRenderable(); + $this->assertEqual(['views_test_cache_context'], $build['#cache']['contexts']); + } + + /** + * Ensures that saving a view calculates the cache contexts. + */ + public function testViewAddCacheMetadata() { + $view = View::load('test_display'); + $view->save(); + + $this->assertEqual(['languages:' . LanguageInterface::TYPE_CONTENT, 'languages:' . LanguageInterface::TYPE_INTERFACE, 'url.query_args', 'user.node_grants:view', 'user.permissions'], $view->getDisplay('default')['cache_metadata']['contexts']); + } + +} diff --git a/core/modules/views/tests/src/Kernel/TokenReplaceTest.php b/core/modules/views/tests/src/Kernel/TokenReplaceTest.php new file mode 100644 index 0000000..7f4f2fa --- /dev/null +++ b/core/modules/views/tests/src/Kernel/TokenReplaceTest.php @@ -0,0 +1,98 @@ +container->get('router.builder')->rebuild(); + } + + /** + * Tests core token replacements generated from a view. + */ + function testTokenReplacement() { + $token_handler = \Drupal::token(); + $view = Views::getView('test_tokens'); + $view->setDisplay('page_1'); + $this->executeView($view); + + $expected = array( + '[view:label]' => 'Test tokens', + '[view:description]' => 'Test view to token replacement tests.', + '[view:id]' => 'test_tokens', + '[view:title]' => 'Test token page', + '[view:url]' => $view->getUrl(NULL, 'page_1')->setAbsolute(TRUE)->toString(), + '[view:total-rows]' => (string) $view->total_rows, + '[view:base-table]' => 'views_test_data', + '[view:base-field]' => 'id', + '[view:items-per-page]' => '10', + '[view:current-page]' => '1', + '[view:page-count]' => '1', + ); + + $base_bubbleable_metadata = BubbleableMetadata::createFromObject($view->storage); + $metadata_tests = []; + $metadata_tests['[view:label]'] = $base_bubbleable_metadata; + $metadata_tests['[view:description]'] = $base_bubbleable_metadata; + $metadata_tests['[view:id]'] = $base_bubbleable_metadata; + $metadata_tests['[view:title]'] = $base_bubbleable_metadata; + $metadata_tests['[view:url]'] = $base_bubbleable_metadata; + $metadata_tests['[view:total-rows]'] = $base_bubbleable_metadata; + $metadata_tests['[view:base-table]'] = $base_bubbleable_metadata; + $metadata_tests['[view:base-field]'] = $base_bubbleable_metadata; + $metadata_tests['[view:items-per-page]'] = $base_bubbleable_metadata; + $metadata_tests['[view:current-page]'] = $base_bubbleable_metadata; + $metadata_tests['[view:page-count]'] = $base_bubbleable_metadata; + + foreach ($expected as $token => $expected_output) { + $bubbleable_metadata = new BubbleableMetadata(); + $output = $token_handler->replace($token, array('view' => $view), [], $bubbleable_metadata); + $this->assertIdentical($output, $expected_output, format_string('Token %token replaced correctly.', array('%token' => $token))); + $this->assertEqual($bubbleable_metadata, $metadata_tests[$token]); + } + } + + /** + * Tests core token replacements generated from a view without results. + */ + function testTokenReplacementNoResults() { + $token_handler = \Drupal::token(); + $view = Views::getView('test_tokens'); + $view->setDisplay('page_2'); + $this->executeView($view); + + $expected = array( + '[view:page-count]' => '1', + ); + + foreach ($expected as $token => $expected_output) { + $output = $token_handler->replace($token, array('view' => $view)); + $this->assertIdentical($output, $expected_output, format_string('Token %token replaced correctly.', array('%token' => $token))); + } + } + +} diff --git a/core/modules/views/tests/src/Kernel/ViewExecutableTest.php b/core/modules/views/tests/src/Kernel/ViewExecutableTest.php new file mode 100644 index 0000000..db50c39 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/ViewExecutableTest.php @@ -0,0 +1,492 @@ +installEntitySchema('user'); + $this->installEntitySchema('node'); + $this->installEntitySchema('comment'); + $this->installSchema('comment', array('comment_entity_statistics')); + $this->installConfig(array('system', 'field', 'node', 'comment')); + + NodeType::create([ + 'type' => 'page', + 'name' => 'Page', + ])->save(); + $this->addDefaultCommentField('node', 'page'); + parent::setUpFixtures(); + + $this->installConfig(array('filter')); + } + + /** + * Tests the views.executable container service. + */ + public function testFactoryService() { + $factory = $this->container->get('views.executable'); + $this->assertTrue($factory instanceof ViewExecutableFactory, 'A ViewExecutableFactory instance was returned from the container.'); + $view = entity_load('view', 'test_executable_displays'); + $this->assertTrue($factory->get($view) instanceof ViewExecutable, 'A ViewExecutable instance was returned from the factory.'); + } + + /** + * Tests the initDisplay() and initHandlers() methods. + */ + public function testInitMethods() { + $view = Views::getView('test_destroy'); + $view->initDisplay(); + + $this->assertTrue($view->display_handler instanceof DefaultDisplay, 'Make sure a reference to the current display handler is set.'); + $this->assertTrue($view->displayHandlers->get('default') instanceof DefaultDisplay, 'Make sure a display handler is created for each display.'); + + $view->destroy(); + $view->initHandlers(); + + // Check for all handler types. + $handler_types = array_keys(ViewExecutable::getHandlerTypes()); + foreach ($handler_types as $type) { + // The views_test integration doesn't have relationships. + if ($type == 'relationship') { + continue; + } + $this->assertTrue(count($view->$type), format_string('Make sure a %type instance got instantiated.', array('%type' => $type))); + } + + // initHandlers() should create display handlers automatically as well. + $this->assertTrue($view->display_handler instanceof DefaultDisplay, 'Make sure a reference to the current display handler is set.'); + $this->assertTrue($view->displayHandlers->get('default') instanceof DefaultDisplay, 'Make sure a display handler is created for each display.'); + + $view_hash = spl_object_hash($view); + $display_hash = spl_object_hash($view->display_handler); + + // Test the initStyle() method. + $view->initStyle(); + $this->assertTrue($view->style_plugin instanceof DefaultStyle, 'Make sure a reference to the style plugin is set.'); + // Test the plugin has been inited and view have references to the view and + // display handler. + $this->assertEqual(spl_object_hash($view->style_plugin->view), $view_hash); + $this->assertEqual(spl_object_hash($view->style_plugin->displayHandler), $display_hash); + + // Test the initQuery method(). + $view->initQuery(); + $this->assertTrue($view->query instanceof Sql, 'Make sure a reference to the query is set'); + $this->assertEqual(spl_object_hash($view->query->view), $view_hash); + $this->assertEqual(spl_object_hash($view->query->displayHandler), $display_hash); + + $view->destroy(); + + // Test the plugin get methods. + $display_plugin = $view->getDisplay(); + $this->assertTrue($display_plugin instanceof DefaultDisplay, 'An instance of DefaultDisplay was returned.'); + $this->assertTrue($view->display_handler instanceof DefaultDisplay, 'The display_handler property has been set.'); + $this->assertIdentical($display_plugin, $view->getDisplay(), 'The same display plugin instance was returned.'); + + $style_plugin = $view->getStyle(); + $this->assertTrue($style_plugin instanceof DefaultStyle, 'An instance of DefaultStyle was returned.'); + $this->assertTrue($view->style_plugin instanceof DefaultStyle, 'The style_plugin property has been set.'); + $this->assertIdentical($style_plugin, $view->getStyle(), 'The same style plugin instance was returned.'); + + $pager_plugin = $view->getPager(); + $this->assertTrue($pager_plugin instanceof PagerPluginBase, 'An instance of PagerPluginBase was returned.'); + $this->assertTrue($view->pager instanceof PagerPluginBase, 'The pager property has been set.'); + $this->assertIdentical($pager_plugin, $view->getPager(), 'The same pager plugin instance was returned.'); + + $query_plugin = $view->getQuery(); + $this->assertTrue($query_plugin instanceof QueryPluginBase, 'An instance of QueryPluginBase was returned.'); + $this->assertTrue($view->query instanceof QueryPluginBase, 'The query property has been set.'); + $this->assertIdentical($query_plugin, $view->getQuery(), 'The same query plugin instance was returned.'); + } + + /** + * Tests the generation of the executable object. + */ + public function testConstructing() { + Views::getView('test_destroy'); + } + + /** + * Tests the accessing of values on the object. + */ + public function testProperties() { + $view = Views::getView('test_destroy'); + foreach ($this->executableProperties as $property) { + $this->assertTrue(isset($view->{$property})); + } + + // Per default exposed input should fall back to an empty array. + $this->assertEqual($view->getExposedInput(), []); + } + + public function testSetDisplayWithInvalidDisplay() { + $view = Views::getView('test_executable_displays'); + $view->initDisplay(); + + // Error is triggered while calling the wrong display. + $this->setExpectedException(\PHPUnit_Framework_Error::class); + $view->setDisplay('invalid'); + + $this->assertEqual($view->current_display, 'default', 'If setDisplay is called with an invalid display id the default display should be used.'); + $this->assertEqual(spl_object_hash($view->display_handler), spl_object_hash($view->displayHandlers->get('default'))); + } + + /** + * Tests the display related methods and properties. + */ + public function testDisplays() { + $view = Views::getView('test_executable_displays'); + + // Tests Drupal\views\ViewExecutable::initDisplay(). + $view->initDisplay(); + $this->assertTrue($view->displayHandlers instanceof DisplayPluginCollection, 'The displayHandlers property has the right class.'); + // Tests the classes of the instances. + $this->assertTrue($view->displayHandlers->get('default') instanceof DefaultDisplay); + $this->assertTrue($view->displayHandlers->get('page_1') instanceof Page); + $this->assertTrue($view->displayHandlers->get('page_2') instanceof Page); + + // After initializing the default display is the current used display. + $this->assertEqual($view->current_display, 'default'); + $this->assertEqual(spl_object_hash($view->display_handler), spl_object_hash($view->displayHandlers->get('default'))); + + // All handlers should have a reference to the default display. + $this->assertEqual(spl_object_hash($view->displayHandlers->get('page_1')->default_display), spl_object_hash($view->displayHandlers->get('default'))); + $this->assertEqual(spl_object_hash($view->displayHandlers->get('page_2')->default_display), spl_object_hash($view->displayHandlers->get('default'))); + + // Tests Drupal\views\ViewExecutable::setDisplay(). + $view->setDisplay(); + $this->assertEqual($view->current_display, 'default', 'If setDisplay is called with no parameter the default display should be used.'); + $this->assertEqual(spl_object_hash($view->display_handler), spl_object_hash($view->displayHandlers->get('default'))); + + // Set two different valid displays. + $view->setDisplay('page_1'); + $this->assertEqual($view->current_display, 'page_1', 'If setDisplay is called with a valid display id the appropriate display should be used.'); + $this->assertEqual(spl_object_hash($view->display_handler), spl_object_hash($view->displayHandlers->get('page_1'))); + + $view->setDisplay('page_2'); + $this->assertEqual($view->current_display, 'page_2', 'If setDisplay is called with a valid display id the appropriate display should be used.'); + $this->assertEqual(spl_object_hash($view->display_handler), spl_object_hash($view->displayHandlers->get('page_2'))); + + // Destroy the view, so we can start again and test an invalid display. + $view->destroy(); + + // Test the style and row plugins are replaced correctly when setting the + // display. + $view->setDisplay('page_1'); + $view->initStyle(); + $this->assertTrue($view->style_plugin instanceof DefaultStyle); + $this->assertTrue($view->rowPlugin instanceof Fields); + + $view->setDisplay('page_2'); + $view->initStyle(); + $this->assertTrue($view->style_plugin instanceof Grid); + // @todo Change this rowPlugin type too. + $this->assertTrue($view->rowPlugin instanceof Fields); + + // Test the newDisplay() method. + $view = $this->container->get('entity.manager')->getStorage('view')->create(array('id' => 'test_executable_displays')); + $executable = $view->getExecutable(); + + $executable->newDisplay('page'); + $executable->newDisplay('page'); + $executable->newDisplay('display_test'); + + $this->assertTrue($executable->displayHandlers->get('default') instanceof DefaultDisplay); + $this->assertFalse(isset($executable->displayHandlers->get('default')->default_display)); + $this->assertTrue($executable->displayHandlers->get('page_1') instanceof Page); + $this->assertTrue($executable->displayHandlers->get('page_1')->default_display instanceof DefaultDisplay); + $this->assertTrue($executable->displayHandlers->get('page_2') instanceof Page); + $this->assertTrue($executable->displayHandlers->get('page_2')->default_display instanceof DefaultDisplay); + $this->assertTrue($executable->displayHandlers->get('display_test_1') instanceof DisplayTest); + $this->assertTrue($executable->displayHandlers->get('display_test_1')->default_display instanceof DefaultDisplay); + } + + /** + * Tests the setting/getting of properties. + */ + public function testPropertyMethods() { + $view = Views::getView('test_executable_displays'); + + // Test the setAjaxEnabled() method. + $this->assertFalse($view->ajaxEnabled()); + $view->setAjaxEnabled(TRUE); + $this->assertTrue($view->ajaxEnabled()); + + $view->setDisplay(); + // There should be no pager set initially. + $this->assertNull($view->usePager()); + + // Add a pager, initialize, and test. + $view->displayHandlers->get('default')->overrideOption('pager', array( + 'type' => 'full', + 'options' => array('items_per_page' => 10), + )); + $view->initPager(); + $this->assertTrue($view->usePager()); + + // Test setting and getting the offset. + $rand = rand(); + $view->setOffset($rand); + $this->assertEqual($view->getOffset(), $rand); + + // Test the getBaseTable() method. + $expected = array( + 'views_test_data' => TRUE, + '#global' => TRUE, + ); + $this->assertIdentical($view->getBaseTables(), $expected); + + // Test response methods. + $this->assertTrue($view->getResponse() instanceof Response, 'New response object returned.'); + $new_response = new Response(); + $view->setResponse($new_response); + $this->assertIdentical(spl_object_hash($view->getResponse()), spl_object_hash($new_response), 'New response object correctly set.'); + + // Test the getPath() method. + $path = $this->randomMachineName(); + $view->displayHandlers->get('page_1')->overrideOption('path', $path); + $view->setDisplay('page_1'); + $this->assertEqual($view->getPath(), $path); + // Test the override_path property override. + $override_path = $this->randomMachineName(); + $view->override_path = $override_path; + $this->assertEqual($view->getPath(), $override_path); + + // Test the title methods. + $title = $this->randomString(); + $view->setTitle($title); + $this->assertEqual($view->getTitle(), Xss::filterAdmin($title)); + } + + /** + * Tests the deconstructor to be sure that necessary objects are removed. + */ + public function testDestroy() { + $view = Views::getView('test_destroy'); + + $view->preview(); + $view->destroy(); + + $this->assertViewDestroy($view); + } + + /** + * Asserts that expected view properties have been unset by destroy(). + * + * @param \Drupal\views\ViewExecutable $view + */ + protected function assertViewDestroy($view) { + $reflection = new \ReflectionClass($view); + $defaults = $reflection->getDefaultProperties(); + // The storage and user should remain. + unset($defaults['storage'], $defaults['user'], $defaults['request'], $defaults['routeProvider']); + + foreach ($defaults as $property => $default) { + $this->assertIdentical($this->getProtectedProperty($view, $property), $default); + } + } + + /** + * Returns a protected property from a class instance. + * + * @param object $instance + * The class instance to return the property from. + * @param string $property + * The name of the property to return. + * + * @return mixed + * The instance property value. + */ + protected function getProtectedProperty($instance, $property) { + $reflection = new \ReflectionProperty($instance, $property); + $reflection->setAccessible(TRUE); + return $reflection->getValue($instance); + } + + /** + * Tests ViewExecutable::getHandlerTypes(). + */ + public function testGetHandlerTypes() { + $types = ViewExecutable::getHandlerTypes(); + foreach (array('field', 'filter', 'argument', 'sort', 'header', 'footer', 'empty') as $type) { + $this->assertTrue(isset($types[$type])); + // @todo The key on the display should be footers, headers and empties + // or something similar instead of the singular, but so long check for + // this special case. + if (isset($types[$type]['type']) && $types[$type]['type'] == 'area') { + $this->assertEqual($types[$type]['plural'], $type); + } + else { + $this->assertEqual($types[$type]['plural'], $type . 's'); + } + } + } + + /** + * Tests ViewExecutable::getHandlers(). + */ + public function testGetHandlers() { + $view = Views::getView('test_executable_displays'); + $view->setDisplay('page_1'); + + $view->getHandlers('field', 'page_2'); + + // getHandlers() shouldn't change the active display. + $this->assertEqual('page_1', $view->current_display, "The display shouldn't change after getHandlers()"); + } + + /** + * Tests the validation of display handlers. + */ + public function testValidate() { + $view = Views::getView('test_executable_displays'); + $view->setDisplay('page_1'); + + $validate = $view->validate(); + + // Validating a view shouldn't change the active display. + $this->assertEqual('page_1', $view->current_display, "The display should be constant while validating"); + + $count = 0; + foreach ($view->displayHandlers as $id => $display) { + $match = function($value) use ($display) { + return strpos($value, $display->display['display_title']) !== false; + }; + $this->assertTrue(array_filter($validate[$id], $match), format_string('Error message found for @id display', array('@id' => $id))); + $count++; + } + + $this->assertEqual(count($view->displayHandlers), $count, 'Error messages from all handlers merged.'); + + // Test that a deleted display is not included. + $display = &$view->storage->getDisplay('default'); + $display['deleted'] = TRUE; + $validate_deleted = $view->validate(); + + $this->assertNotIdentical($validate, $validate_deleted, 'Master display has not been validated.'); + } + + /** + * Tests that nested loops of the display handlers won't break validation. + */ + public function testValidateNestedLoops() { + $view = View::create(['id' => 'test_validate_nested_loops']); + $executable = $view->getExecutable(); + + $executable->newDisplay('display_test'); + $executable->newDisplay('display_test'); + $errors = $executable->validate(); + $total_error_count = array_reduce($errors, function ($carry, $item) { + $carry += count($item); + + return $carry; + }); + // Assert that there were 9 total errors across 3 displays. + $this->assertIdentical(9, $total_error_count); + $this->assertIdentical(3, count($errors)); + } + + /** + * Tests serialization of the ViewExecutable object. + */ + public function testSerialization() { + $view = Views::getView('test_executable_displays'); + $view->setDisplay('page_1'); + $view->setArguments(['test']); + $view->setCurrentPage(2); + + $serialized = serialize($view); + + // Test the view storage object is not present in the actual serialized + // string. + $this->assertIdentical(strpos($serialized, '"Drupal\views\Entity\View"'), FALSE, 'The Drupal\views\Entity\View class was not found in the serialized string.'); + + /** @var \Drupal\views\ViewExecutable $unserialized */ + $unserialized = unserialize($serialized); + + $this->assertTrue($unserialized instanceof ViewExecutable); + $this->assertIdentical($view->storage->id(), $unserialized->storage->id(), 'The expected storage entity was loaded on the unserialized view.'); + $this->assertIdentical($unserialized->current_display, 'page_1', 'The expected display was set on the unserialized view.'); + $this->assertIdentical($unserialized->args, ['test'], 'The expected argument was set on the unserialized view.'); + $this->assertIdentical($unserialized->getCurrentPage(), 2, 'The expected current page was set on the unserialized view.'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/ViewStorageTest.php b/core/modules/views/tests/src/Kernel/ViewStorageTest.php new file mode 100644 index 0000000..0a8c07b --- /dev/null +++ b/core/modules/views/tests/src/Kernel/ViewStorageTest.php @@ -0,0 +1,356 @@ +entityType = \Drupal::entityManager()->getDefinition('view'); + $this->controller = $this->container->get('entity.manager')->getStorage('view'); + + // Confirm that an info array has been returned. + $this->assertTrue($this->entityType instanceof EntityTypeInterface, 'The View info array is loaded.'); + + // CRUD tests. + $this->loadTests(); + $this->createTests(); + $this->displayTests(); + + // Helper method tests + $this->displayMethodTests(); + } + + /** + * Tests loading configuration entities. + */ + protected function loadTests() { + $view = entity_load('view', 'test_view_storage'); + $data = $this->config('views.view.test_view_storage')->get(); + + // Confirm that an actual view object is loaded and that it returns all of + // expected properties. + $this->assertTrue($view instanceof View, 'Single View instance loaded.'); + foreach ($this->configProperties as $property) { + $this->assertTrue($view->get($property) !== NULL, format_string('Property: @property loaded onto View.', array('@property' => $property))); + } + + // Check the displays have been loaded correctly from config display data. + $expected_displays = array('default', 'block_1', 'page_1'); + $this->assertEqual(array_keys($view->get('display')), $expected_displays, 'The correct display names are present.'); + + // Check each ViewDisplay object and confirm that it has the correct key and + // property values. + foreach ($view->get('display') as $key => $display) { + $this->assertEqual($key, $display['id'], 'The display has the correct ID assigned.'); + + // Get original display data and confirm that the display options array + // exists. + $original_options = $data['display'][$key]; + foreach ($original_options as $orig_key => $value) { + $this->assertIdentical($display[$orig_key], $value, format_string('@key is identical to saved data', array('@key' => $key))); + } + } + + // Make sure that loaded default views get a UUID. + $view = Views::getView('test_view_storage'); + $this->assertTrue($view->storage->uuid()); + } + + /** + * Tests creating configuration entities. + */ + protected function createTests() { + // Create a new View instance with empty values. + $created = $this->controller->create(array()); + + $this->assertTrue($created instanceof View, 'Created object is a View.'); + // Check that the View contains all of the properties. + foreach ($this->configProperties as $property) { + $this->assertTrue(property_exists($created, $property), format_string('Property: @property created on View.', array('@property' => $property))); + } + + // Create a new View instance with config values. + $values = $this->config('views.view.test_view_storage')->get(); + $values['id'] = 'test_view_storage_new'; + unset($values['uuid']); + $created = $this->controller->create($values); + + $this->assertTrue($created instanceof View, 'Created object is a View.'); + // Check that the View contains all of the properties. + $properties = $this->configProperties; + // Remove display from list. + array_pop($properties); + + // Test all properties except displays. + foreach ($properties as $property) { + $this->assertTrue($created->get($property) !== NULL, format_string('Property: @property created on View.', array('@property' => $property))); + $this->assertIdentical($values[$property], $created->get($property), format_string('Property value: @property matches configuration value.', array('@property' => $property))); + } + + // Check the UUID of the loaded View. + $created->save(); + $created_loaded = entity_load('view', 'test_view_storage_new'); + $this->assertIdentical($created->uuid(), $created_loaded->uuid(), 'The created UUID has been saved correctly.'); + } + + /** + * Tests adding, saving, and loading displays on configuration entities. + */ + protected function displayTests() { + // Check whether a display can be added and saved to a View. + $view = entity_load('view', 'test_view_storage_new'); + + $new_id = $view->addDisplay('page', 'Test', 'test'); + $display = $view->get('display'); + + // Ensure the right display_plugin is created/instantiated. + $this->assertEqual($display[$new_id]['display_plugin'], 'page', 'New page display "test" uses the right display plugin.'); + + $executable = $view->getExecutable(); + $executable->initDisplay(); + $this->assertTrue($executable->displayHandlers->get($new_id) instanceof Page, 'New page display "test" uses the right display plugin.'); + + // To save this with a new ID, we should use createDuplicate(). + $view = $view->createDuplicate(); + $view->set('id', 'test_view_storage_new_new2'); + $view->save(); + $values = $this->config('views.view.test_view_storage_new_new2')->get(); + + $this->assertTrue(isset($values['display']['test']) && is_array($values['display']['test']), 'New display was saved.'); + } + + /** + * Tests the display related functions like getDisplaysList(). + */ + protected function displayMethodTests() { + $config['display'] = array( + 'page_1' => array( + 'display_options' => array('path' => 'test'), + 'display_plugin' => 'page', + 'id' => 'page_2', + 'display_title' => 'Page 1', + 'position' => 1 + ), + 'feed_1' => array( + 'display_options' => array('path' => 'test.xml'), + 'display_plugin' => 'feed', + 'id' => 'feed', + 'display_title' => 'Feed', + 'position' => 2 + ), + 'page_2' => array( + 'display_options' => array('path' => 'test/%/extra'), + 'display_plugin' => 'page', + 'id' => 'page_2', + 'display_title' => 'Page 2', + 'position' => 3 + ) + ); + $view = $this->controller->create($config); + + // Tests Drupal\views\Entity\View::addDisplay() + $view = $this->controller->create(array()); + $random_title = $this->randomMachineName(); + + $id = $view->addDisplay('page', $random_title); + $this->assertEqual($id, 'page_1', format_string('Make sure the first display (%id_new) has the expected ID (%id)', array('%id_new' => $id, '%id' => 'page_1'))); + $display = $view->get('display'); + $this->assertEqual($display[$id]['display_title'], $random_title); + + $random_title = $this->randomMachineName(); + $id = $view->addDisplay('page', $random_title); + $display = $view->get('display'); + $this->assertEqual($id, 'page_2', format_string('Make sure the second display (%id_new) has the expected ID (%id)', array('%id_new' => $id, '%id' => 'page_2'))); + $this->assertEqual($display[$id]['display_title'], $random_title); + + $id = $view->addDisplay('page'); + $display = $view->get('display'); + $this->assertEqual($display[$id]['display_title'], 'Page 3'); + + // Ensure the 'default' display always has position zero, regardless of when + // it was set relative to other displays. Even if the 'default' display + // exists, adding it again will overwrite it, which is asserted with the new + // title. + $view->addDisplay('default', $random_title); + $displays = $view->get('display'); + $this->assertEqual($displays['default']['display_title'], $random_title, 'Default display is defined with the new title'); + $this->assertEqual($displays['default']['position'], 0, 'Default displays are always in position zero'); + + // Tests Drupal\views\Entity\View::generateDisplayId(). Since + // generateDisplayId() is protected, we have to use reflection to unit-test + // it. + $view = $this->controller->create(array()); + $ref_generate_display_id = new \ReflectionMethod($view, 'generateDisplayId'); + $ref_generate_display_id->setAccessible(TRUE); + $this->assertEqual( + $ref_generate_display_id->invoke($view, 'default'), + 'default', + 'The plugin ID for default is always default.' + ); + $this->assertEqual( + $ref_generate_display_id->invoke($view, 'feed'), + 'feed_1', + 'The generated ID for the first instance of a plugin type should have an suffix of _1.' + ); + $view->addDisplay('feed', 'feed title'); + $this->assertEqual( + $ref_generate_display_id->invoke($view, 'feed'), + 'feed_2', + 'The generated ID for the first instance of a plugin type should have an suffix of _2.' + ); + + // Tests item related methods(). + $view = $this->controller->create(array('base_table' => 'views_test_data')); + $view->addDisplay('default'); + $view = $view->getExecutable(); + + $display_id = 'default'; + $expected_items = array(); + // Tests addHandler with getItem. + // Therefore add one item without any options and one item with some + // options. + $id1 = $view->addHandler($display_id, 'field', 'views_test_data', 'id'); + $item1 = $view->getHandler($display_id, 'field', 'id'); + $expected_items[$id1] = $expected_item = array( + 'id' => 'id', + 'table' => 'views_test_data', + 'field' => 'id', + 'plugin_id' => 'numeric', + ); + $this->assertEqual($item1, $expected_item); + + $options = array( + 'alter' => array( + 'text' => $this->randomMachineName() + ) + ); + $id2 = $view->addHandler($display_id, 'field', 'views_test_data', 'name', $options); + $item2 = $view->getHandler($display_id, 'field', 'name'); + $expected_items[$id2] = $expected_item = array( + 'id' => 'name', + 'table' => 'views_test_data', + 'field' => 'name', + 'plugin_id' => 'standard', + ) + $options; + $this->assertEqual($item2, $expected_item); + + // Tests the expected fields from the previous additions. + $this->assertEqual($view->getHandlers('field', $display_id), $expected_items); + + // Alter an existing item via setItem and check the result via getItem + // and getItems. + $item = array( + 'alter' => array( + 'text' => $this->randomMachineName(), + ) + ) + $item1; + $expected_items[$id1] = $item; + $view->setHandler($display_id, 'field', $id1, $item); + $this->assertEqual($view->getHandler($display_id, 'field', 'id'), $item); + $this->assertEqual($view->getHandlers('field', $display_id), $expected_items); + + // Test removeItem method. + unset($expected_items[$id2]); + $view->removeHandler($display_id, 'field', $id2); + $this->assertEqual($view->getHandlers('field', $display_id), $expected_items); + } + + /** + * Tests the createDuplicate() View method. + */ + public function testCreateDuplicate() { + $view = Views::getView('test_view_storage'); + $copy = $view->storage->createDuplicate(); + + $this->assertTrue($copy instanceof View, 'The copied object is a View.'); + + // Check that the original view and the copy have different UUIDs. + $this->assertNotIdentical($view->storage->uuid(), $copy->uuid(), 'The copied view has a new UUID.'); + + // Check the 'name' (ID) is using the View objects default value (NULL) as it + // gets unset. + $this->assertIdentical($copy->id(), NULL, 'The ID has been reset.'); + + // Check the other properties. + // @todo Create a reusable property on the base test class for these? + $config_properties = array( + 'disabled', + 'description', + 'tag', + 'base_table', + 'label', + 'core', + ); + + foreach ($config_properties as $property) { + $this->assertIdentical($view->storage->get($property), $copy->get($property), format_string('@property property is identical.', array('@property' => $property))); + } + + // Check the displays are the same. + $copy_display = $copy->get('display'); + foreach ($view->storage->get('display') as $id => $display) { + // assertIdentical will not work here. + $this->assertEqual($display, $copy_display[$id], format_string('The @display display has been copied correctly.', array('@display' => $id))); + } + } + +} diff --git a/core/modules/views/tests/src/Kernel/ViewsConfigDependenciesIntegrationTest.php b/core/modules/views/tests/src/Kernel/ViewsConfigDependenciesIntegrationTest.php new file mode 100644 index 0000000..25db7d9 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/ViewsConfigDependenciesIntegrationTest.php @@ -0,0 +1,81 @@ + 'foo']); + $style->save(); + + // Create a new image field 'bar' to be used in 'entity_test_fields' view. + FieldStorageConfig::create([ + 'entity_type' => 'entity_test', + 'field_name' => 'bar', + 'type' => 'image', + ])->save(); + FieldConfig::create([ + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + 'field_name' => 'bar', + ])->save(); + + /** @var \Drupal\views\ViewEntityInterface $view */ + $view = View::load('entity_test_fields'); + $display =& $view->getDisplay('default'); + + // Add the 'bar' image field to 'entity_test_fields' view. + $display['display_options']['fields']['bar'] = [ + 'id' => 'bar', + 'field' => 'bar', + 'plugin_id' => 'field', + 'table' => 'entity_test__bar', + 'entity_type' => 'entity_test', + 'entity_field' => 'bar', + 'type' => 'image', + 'settings' => ['image_style' => 'foo', 'image_link' => ''], + ]; + $view->save(); + + $dependencies = $view->getDependencies() + ['config' => []]; + + // Checks that style 'foo' is a dependency of view 'entity_test_fields'. + $this->assertTrue(in_array('image.style.foo', $dependencies['config'])); + + // Delete the 'foo' image style. + $style->delete(); + + // Checks that the view has been deleted too. + $this->assertNull(View::load('entity_test_fields')); + } + +} diff --git a/core/modules/views/tests/src/Kernel/ViewsHooksTest.php b/core/modules/views/tests/src/Kernel/ViewsHooksTest.php new file mode 100644 index 0000000..1c59604 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/ViewsHooksTest.php @@ -0,0 +1,123 @@ + 'all', + 'views_data_alter' => 'alter', + 'views_query_substitutions' => 'view', + 'views_form_substitutions' => 'view', + 'views_analyze' => 'view', + 'views_pre_view' => 'view', + 'views_pre_build' => 'view', + 'views_post_build' => 'view', + 'views_pre_execute' => 'view', + 'views_post_execute' => 'view', + 'views_pre_render' => 'view', + 'views_post_render' => 'view', + 'views_query_alter' => 'view', + 'views_invalidate_cache' => 'all', + ); + + /** + * The module handler to use for invoking hooks. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + + protected function setUp($import_test_views = TRUE) { + parent::setUp(); + + $this->moduleHandler = $this->container->get('module_handler'); + } + + /** + * Tests the hooks. + */ + public function testHooks() { + $view = Views::getView('test_view'); + $view->setDisplay(); + + // Test each hook is found in the implementations array and is invoked. + foreach (static::$hooks as $hook => $type) { + $this->assertTrue($this->moduleHandler->implementsHook('views_test_data', $hook), format_string('The hook @hook was registered.', array('@hook' => $hook))); + + if ($hook == 'views_post_render') { + $this->moduleHandler->invoke('views_test_data', $hook, array($view, &$view->display_handler->output, $view->display_handler->getPlugin('cache'))); + continue; + } + + switch ($type) { + case 'view': + $this->moduleHandler->invoke('views_test_data', $hook, array($view)); + break; + + case 'alter': + $data = array(); + $this->moduleHandler->invoke('views_test_data', $hook, array($data)); + break; + + default: + $this->moduleHandler->invoke('views_test_data', $hook); + } + + $this->assertTrue($this->container->get('state')->get('views_hook_test_' . $hook), format_string('The %hook hook was invoked.', array('%hook' => $hook))); + // Reset the module implementations cache, so we ensure that the + // .views.inc file is loaded actively. + $this->moduleHandler->resetImplementations(); + } + } + + /** + * Tests how hook_views_form_substitutions() makes substitutions. + * + * @see views_test_data_views_form_substitutions() + * @see views_pre_render_views_form_views_form() + */ + public function testViewsPreRenderViewsFormViewsForm() { + $element = [ + 'output' => [ + '#plain_text' => '', + ], + '#substitutions' => ['#value' => []], + ]; + $element = \Drupal::service('renderer')->executeInRenderContext(new RenderContext(), function() use ($element) { + return views_pre_render_views_form_views_form($element); + }); + $this->setRawContent((string) $element['output']['#markup']); + $this->assertEscaped('escaped'); + $this->assertRaw('unescaped'); + } + +} diff --git a/core/modules/views/tests/src/Kernel/ViewsKernelTestBase.php b/core/modules/views/tests/src/Kernel/ViewsKernelTestBase.php new file mode 100644 index 0000000..9d6e47a --- /dev/null +++ b/core/modules/views/tests/src/Kernel/ViewsKernelTestBase.php @@ -0,0 +1,156 @@ +installSchema('system', ['router', 'sequences', 'key_value_expire']); + $this->setUpFixtures(); + + if ($import_test_views) { + ViewTestData::createTestViews(get_class($this), ['views_test_config']); + } + } + /** + * Sets up the configuration and schema of views and views_test_data modules. + * + * Because the schema of views_test_data.module is dependent on the test + * using it, it cannot be enabled normally. + */ + protected function setUpFixtures() { + // First install the system module. Many Views have Page displays have menu + // links, and for those to work, the system menus must already be present. + $this->installConfig(['system']); + + /** @var \Drupal\Core\State\StateInterface $state */ + $state = $this->container->get('state'); + // Define the schema and views data variable before enabling the test module. + $state->set('views_test_data_schema', $this->schemaDefinition()); + $state->set('views_test_data_views_data', $this->viewsData()); + + $this->installConfig(['views', 'views_test_config', 'views_test_data']); + foreach ($this->schemaDefinition() as $table => $schema) { + $this->installSchema('views_test_data', $table); + } + + $this->container->get('router.builder')->rebuild(); + + // Load the test dataset. + $data_set = $this->dataSet(); + $query = Database::getConnection()->insert('views_test_data') + ->fields(array_keys($data_set[0])); + foreach ($data_set as $record) { + $query->values($record); + } + $query->execute(); + } + + /** + * Orders a nested array containing a result set based on a given column. + * + * @param array $result_set + * An array of rows from a result set, with each row as an associative + * array keyed by column name. + * @param string $column + * The column name by which to sort the result set. + * @param bool $reverse + * (optional) Boolean indicating whether to sort the result set in reverse + * order. Defaults to FALSE. + * + * @return array + * The sorted result set. + */ + protected function orderResultSet($result_set, $column, $reverse = FALSE) { + $order = $reverse ? -1 : 1; + usort($result_set, function ($a, $b) use ($column, $order) { + if ($a[$column] == $b[$column]) { + return 0; + } + return $order * (($a[$column] < $b[$column]) ? -1 : 1); + }); + return $result_set; + } + + /** + * Executes a view with debugging. + * + * @param \Drupal\views\ViewExecutable $view + * The view object. + * @param array $args + * (optional) An array of the view arguments to use for the view. + */ + protected function executeView($view, array $args = []) { + $view->setDisplay(); + $view->preExecute($args); + $view->execute(); + $verbose_message = '
                                  Executed view: ' . ((string) $view->build_info['query']). '
                                  '; + if ($view->build_info['query'] instanceof SelectInterface) { + $verbose_message .= '
                                  Arguments: ' . print_r($view->build_info['query']->getArguments(), TRUE) . '
                                  '; + } + $this->verbose($verbose_message); + } + + /** + * Returns the schema definition. + */ + protected function schemaDefinition() { + return ViewTestData::schemaDefinition(); + } + + /** + * Returns the views data definition. + */ + protected function viewsData() { + return ViewTestData::viewsData(); + } + + /** + * Returns a very simple test dataset. + */ + protected function dataSet() { + return ViewTestData::dataSet(); + } + +} diff --git a/core/modules/views/tests/src/Kernel/Wizard/WizardPluginBaseKernelTest.php b/core/modules/views/tests/src/Kernel/Wizard/WizardPluginBaseKernelTest.php new file mode 100644 index 0000000..ccc5e40 --- /dev/null +++ b/core/modules/views/tests/src/Kernel/Wizard/WizardPluginBaseKernelTest.php @@ -0,0 +1,79 @@ +installConfig(array('language')); + + $this->wizard = $this->container->get('plugin.manager.views.wizard')->createInstance('standard:views_test_data', array()); + } + + /** + * Tests the creating of a view. + * + * @see \Drupal\views\Plugin\views\wizard\WizardPluginBase + */ + public function testCreateView() { + $form = array(); + $form_state = new FormState(); + $form = $this->wizard->buildForm($form, $form_state); + $random_id = strtolower($this->randomMachineName()); + $random_label = $this->randomMachineName(); + $random_description = $this->randomMachineName(); + + // Add a new language and mark it as default. + ConfigurableLanguage::createFromLangcode('it')->save(); + $this->config('system.site')->set('default_langcode', 'it')->save(); + + $form_state->setValues([ + 'id' => $random_id, + 'label' => $random_label, + 'description' => $random_description, + 'base_table' => 'views_test_data', + ]); + + $this->wizard->validateView($form, $form_state); + $view = $this->wizard->createView($form, $form_state); + $this->assertTrue($view instanceof ViewUI, 'The created view is a ViewUI object.'); + $this->assertEqual($view->get('id'), $random_id); + $this->assertEqual($view->get('label'), $random_label); + $this->assertEqual($view->get('description'), $random_description); + $this->assertEqual($view->get('base_table'), 'views_test_data'); + $this->assertEqual($view->get('langcode'), 'it'); + } +} + diff --git a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php index 67771b5..e6d8f9f 100644 --- a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php +++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php @@ -94,6 +94,7 @@ protected function setUp() { 'id' => 'entity_test', 'label' => 'Entity test', 'entity_keys' => [ + 'uuid' => 'uuid', 'id' => 'id', 'langcode' => 'langcode', 'bundle' => 'type', @@ -991,4 +992,3 @@ function t($string, array $args = []) { } } } - diff --git a/core/modules/views/tests/src/Unit/Plugin/area/ResultTest.php b/core/modules/views/tests/src/Unit/Plugin/area/ResultTest.php index 785028c..0245e3f5 100644 --- a/core/modules/views/tests/src/Unit/Plugin/area/ResultTest.php +++ b/core/modules/views/tests/src/Unit/Plugin/area/ResultTest.php @@ -7,9 +7,15 @@ namespace Drupal\Tests\views\Unit\Plugin\area; +use Drupal\Core\Routing\RouteProviderInterface; +use Drupal\Core\Session\AccountInterface; use Drupal\Tests\UnitTestCase; +use Drupal\views\Entity\View; +use Drupal\views\Plugin\views\pager\PagerPluginBase; use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\area\Result; +use Drupal\views\ViewsData; +use Prophecy\Argument; /** * @coversDefaultClass \Drupal\views\Plugin\views\area\Result @@ -34,22 +40,16 @@ class ResultTest extends UnitTestCase { protected function setUp() { parent::setUp(); - $storage = $this->getMockBuilder('Drupal\views\Entity\View') - ->disableOriginalConstructor() - ->setMethods(array('label')) - ->getMock(); - $storage->expects($this->any()) - ->method('label') - ->will($this->returnValue('ResultTest')); - - $user = $this->getMock('Drupal\Core\Session\AccountInterface'); - $views_data = $this->getMockBuilder('Drupal\views\ViewsData') - ->disableOriginalConstructor() - ->getMock(); - $route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); - $this->view = new ViewExecutable($storage, $user, $views_data, $route_provider); - - $this->resultHandler = new Result(array(), 'result', array()); + $storage = $this->prophesize(View::class); + $storage->label()->willReturn('ResultTest'); + $storage->set(Argument::cetera())->willReturn(NULL); + + $user = $this->prophesize(AccountInterface::class)->reveal(); + $views_data = $this->prophesize(ViewsData::class)->reveal(); + $route_provider = $this->prophesize(RouteProviderInterface::class)->reveal(); + $this->view = new ViewExecutable($storage->reveal(), $user, $views_data, $route_provider); + + $this->resultHandler = new Result([], 'result', []); $this->resultHandler->view = $this->view; } @@ -121,18 +121,15 @@ public function providerTestResultArea() { * The value to return from getItemsPerPage(). */ protected function setupViewPager($items_per_page = 0) { - $pager = $this->getMockBuilder('Drupal\views\Plugin\views\pager\PagerPluginBase') - ->disableOriginalConstructor() - ->setMethods(array('getItemsPerPage', 'getCurrentPage')) - ->getMock(); - $pager->expects($this->once()) - ->method('getItemsPerPage') - ->will($this->returnValue($items_per_page)); - $pager->expects($this->once()) - ->method('getCurrentPage') - ->will($this->returnValue(0)); - - $this->view->pager = $pager; + $pager = $this->prophesize(PagerPluginBase::class); + $pager->getItemsPerPage() + ->willReturn($items_per_page) + ->shouldBeCalledTimes(1); + $pager->getCurrentPage() + ->willReturn(0) + ->shouldBeCalledTimes(1); + + $this->view->pager = $pager->reveal(); $this->view->style_plugin = new \stdClass(); $this->view->total_rows = 100; $this->view->result = array(1, 2, 3, 4, 5); diff --git a/core/modules/views/views.install b/core/modules/views/views.install index d4af0ea..37d03b6 100644 --- a/core/modules/views/views.install +++ b/core/modules/views/views.install @@ -347,3 +347,19 @@ function views_update_8004() { /** * @} End of "addtogroup updates-8.0.x". */ + +/** + * @addtogroup updates-8.1.0 + * @{ + */ + +/** + * Clear views data cache. + */ +function views_update_8005() { + // Empty update function to rebuild the views data. +} + +/** + * @} End of "addtogroup updates-8.1.0". + */ diff --git a/core/modules/views/views.module b/core/modules/views/views.module index b8c2560..c6632a5 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -5,12 +5,11 @@ * Primarily Drupal hooks and global API functions to manipulate views. */ +use Drupal\Component\Render\MarkupInterface; use Drupal\Component\Utility\Html; -use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Database\Query\AlterableInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\Element; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Url; use Drupal\views\Plugin\Derivative\ViewsLocalTask; @@ -622,7 +621,7 @@ function views_pre_render_views_form_views_form($element) { foreach ($substitutions as $placeholder => $substitution) { $search[] = Html::escape($placeholder); // Ensure that any replacements made are safe to make. - if (!SafeMarkup::isSafe($substitution)) { + if (!($substitution instanceof MarkupInterface)) { $substitution = Html::escape($substitution); } $replace[] = $substitution; diff --git a/core/modules/views/views.post_update.php b/core/modules/views/views.post_update.php index ecbf851..09a08f6 100644 --- a/core/modules/views/views.post_update.php +++ b/core/modules/views/views.post_update.php @@ -6,6 +6,7 @@ */ use Drupal\Core\StringTranslation\TranslatableMarkup; +use Drupal\views\Entity\View; use Drupal\views\Views; /** @@ -139,3 +140,22 @@ function views_post_update_cleanup_duplicate_views_data() { /** * @} End of "addtogroup updates-8.0.0-rc". */ + +/** + * @addtogroup updates-8.0.x + * @{ + */ + +/** + * Include field formatter dependencies in a view when the formatter is used. + */ +function views_post_update_field_formatter_dependencies() { + $views = View::loadMultiple(); + array_walk($views, function(View $view) { + $view->save(); + }); +} + +/** + * @} End of "addtogroup updates-8.0.x". + */ diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc index c14416f..94c519e 100644 --- a/core/modules/views/views.theme.inc +++ b/core/modules/views/views.theme.inc @@ -277,11 +277,29 @@ function template_preprocess_views_view_summary(&$variables) { if (!empty($argument->options['summary_options']['base_path'])) { $base_path = $argument->options['summary_options']['base_path']; - $tokens = $this->getArgumentsTokens(); - $base_path = $this->viewsTokenReplace($base_path, $tokens); + $tokens = $view->getDisplay()->getArgumentsTokens(); + $base_path = $argument->globalTokenReplace($base_path, $tokens); // @todo Views should expect and store a leading /. See: // https://www.drupal.org/node/2423913 $url = Url::fromUserInput('/' . $base_path); + try { + /** @var \Symfony\Component\Routing\Route $route */ + $route_name = $url->getRouteName(); + $route = \Drupal::service('router.route_provider')->getRouteByName($route_name); + + $route_variables = $route->compile()->getVariables(); + $parameters = $url->getRouteParameters(); + + foreach ($route_variables as $variable_name) { + $parameters[$variable_name] = array_shift($args); + } + + $url->setRouteParameters($parameters); + } + catch (Exception $e) { + // If the given route doesn't exist, default to + $url = Url::fromRoute(''); + } } else { $url = $view->getUrl($args)->setOptions($url_options); @@ -307,6 +325,7 @@ function template_preprocess_views_view_summary(&$variables) { * visually distinct. */ function template_preprocess_views_view_summary_unformatted(&$variables) { + /** @var \Drupal\views\ViewExecutable $view */ $view = $variables['view']; $argument = $view->argument[$view->build_info['summary_level']]; @@ -346,11 +365,27 @@ function template_preprocess_views_view_summary_unformatted(&$variables) { if (!empty($argument->options['summary_options']['base_path'])) { $base_path = $argument->options['summary_options']['base_path']; - $tokens = $this->getArgumentsTokens(); - $base_path = $this->viewsTokenReplace($base_path, $tokens); + $tokens = $view->getDisplay()->getArgumentsTokens(); + $base_path = $argument->globalTokenReplace($base_path, $tokens); // @todo Views should expect and store a leading /. See: // https://www.drupal.org/node/2423913 $url = Url::fromUserInput('/' . $base_path); + try { + /** @var \Symfony\Component\Routing\Route $route */ + $route = \Drupal::service('router.route_provider')->getRouteByName($url->getRouteName()); + $route_variables = $route->compile()->getVariables(); + $parameters = $url->getRouteParameters(); + + foreach ($route_variables as $variable_name) { + $parameters[$variable_name] = array_shift($args); + } + + $url->setRouteParameters($parameters); + } + catch (Exception $e) { + // If the given route doesn't exist, default to + $url = Url::fromRoute(''); + } } else { $url = $view->getUrl($args)->setOptions($url_options); diff --git a/core/modules/views_ui/admin.inc b/core/modules/views_ui/admin.inc index 01bcf7a..fd031d2 100644 --- a/core/modules/views_ui/admin.inc +++ b/core/modules/views_ui/admin.inc @@ -226,7 +226,11 @@ function views_ui_standard_display_dropdown(&$form, FormStateInterface $form_sta $form['progress']['#weight'] = -1001; } - if ($current_display->isDefaultDisplay()) { + // The dropdown should not be added when : + // - this is the default display. + // - there is no master shown and just one additional display (mostly page) + // and the current display is defaulted. + if ($current_display->isDefaultDisplay() || ($current_display->isDefaulted($section) && !\Drupal::config('views.settings')->get('ui.show.master_display') && count($displays) <= 2)) { return; } diff --git a/core/modules/views_ui/images/arrow-active.png b/core/modules/views_ui/images/arrow-active.png deleted file mode 100644 index 3bbd3c2..0000000 --- a/core/modules/views_ui/images/arrow-active.png +++ /dev/null @@ -1,3 +0,0 @@ -PNG - - IHDR~gAMAOX2tEXtSoftwareAdobe ImageReadyqe<EPLTEJJJRRRKKKLLLĺNNNdddOOOؓIIIgFNzIDATxbbc0AL  C CTGL QFf10D*",@`@L@]2d@`Ab@1l0a vЧIENDB` \ No newline at end of file diff --git a/core/modules/views_ui/images/close.png b/core/modules/views_ui/images/close.png deleted file mode 100644 index 2af0da5..0000000 --- a/core/modules/views_ui/images/close.png +++ /dev/null @@ -1,3 +0,0 @@ -PNG - - IHDRatEXtSoftwareAdobe ImageReadyqe<IDATxڬSA ȓg׊1F;Y"1eXǵF;tђLܱk?$3C)TQ&V-7/;tnap58S>dЩ E:JdH?gd|\?IENDB` \ No newline at end of file diff --git a/core/modules/views_ui/images/expanded-options.png b/core/modules/views_ui/images/expanded-options.png deleted file mode 100644 index b7b755c..0000000 --- a/core/modules/views_ui/images/expanded-options.png +++ /dev/null @@ -1,3 +0,0 @@ -PNG - - IHDR2sBITOIDAT10 Ems Lrs9+3s-YЪ PU723m7"DBڃ"|˲xѦiRW4M?Ǯs}|VQk= :tq43q,ܭ'u!"[}4IENDB` \ No newline at end of file diff --git a/core/modules/views_ui/images/loading.gif b/core/modules/views_ui/images/loading.gif deleted file mode 100644 index 2dbcd62..0000000 --- a/core/modules/views_ui/images/loading.gif +++ /dev/null @@ -1,43 +0,0 @@ -GIF89a00###'''(((---111555:::===CCCGGGKKKMMMQQQUUUZZZ^^^aaaggghhhmmmqqqwwwxxx{{{///333444>>>AAAEEEIIIOOORRRXXX```eeekkklllssszzz~~~&&&***,,,222666<<~@ 1;d2;D@[#/nҠ˪uW ٚhxHr·îݫWF?&9yk氅=x8 K j7р.! -,00WWZZqP:z- 8Yܧ$+Cɩ4T(|ѓ`&,pۿOBQGw̡ZL!IWġ{`XA$TP T f1@M("wb}.!c$Rqr1x?DEI2ȤMōR">b%_@Œ2RD:n@r8! &6 -9ys w(GXhLoa`7wr4LXQ @YqG -)Gw*XL뮱A4jiȱ:;$B-}hGr@`ȡJ;Cr]QGA,v0&AN zǭpCWG ms̑@A1uy(G_Jt2j3iuT,U;d u\AZAqlytD! -,00HAh8ȰÆ B!B3j$@ 5HD̀LtKQ9J /s`sOr2``JIS塂[|\8`F@6B!L@B ̘0qdJ7ϖT QRάlbUҥkv,  x‹+Xh!-10`G=.B\BJ% "F%b",~# (00Mb@th& -#E*ڛ`i|$(L> EC! 4$gp"^@iՑ~Fé0p\F4PQF>)W > -*]wƭ&[ڌG ĢHF -ji  -ў:FFQliĀ -3; -jH.{ + -.BU*:>4kA>ȩ,FlQ!7H# Hp7\@qCr'C<6@ !ǐY ʐG@B -҃A=P@! -,00H`AG=z䨡#JttNJwn ȱA9ƈcIǓ#)R$0:`RƘoDsaRdЀwzIIw'$(z,v% 5P#PQ. -IG")>"^6NɖgؐfV0i@ǏG ɲ(崜 ̏4HOrӣ5 -IhF,rD!ʁ9=p"?e Q7E9-CX{$8uX`ZրB5 CjӪs r؋vB4VNv`Ea4Pڴ.$%܅G!40Ivc /~EѲAr{0֘]jH!/S5;| 5(s” FT?ܐ!ƂnJ`H |X`@fPHHX!ǒ%@" TS +2H}H c4jE8Ǝ0 -IFx(%(cif!b%2ƒ`_F!!YxQAHN ȣxK]r g!hPFal(+) .hhAL` 2*F7.@B&C")/ Y:F{ls -B@X0lIP +0D&!D5a - T+n"|r@c~ C - -4JB~L˛v(sÅ#L)M -h*  1XgH@NG_E#aY0Z^r5+) b&5ՆܺDp;ɷ1= -Hp(aƍ>4k';YSFuCG^\MpU$nGk jJT.i<(!K8 <2@}Ztoy;>Cs&d D#CÂ0p9Rl0F4%R0` Z8F*xA@^x8.28~.fc J<Ï@䅉DH=$I4y"W<СS(`he zMe^y7za'Pm|^|F8eI$`g$ŢJ*f@I'P Q(A@@&CpiBŨ )pb"J zĨxqi'g$e"&";Ey,j`4\F-\@"A8 ĽM q@n j 'MpypoQ9tA4̰M8~A*!>4y1o񗸓DC927% C8MBh@C`dH:"S@; \ No newline at end of file diff --git a/core/modules/views_ui/images/overridden.gif b/core/modules/views_ui/images/overridden.gif deleted file mode 100644 index b781191..0000000 --- a/core/modules/views_ui/images/overridden.gif +++ /dev/null @@ -1,2 +0,0 @@ -GIF89a 赶Ƽ!, , %$ @1K#=A 01 -D ; \ No newline at end of file diff --git a/core/modules/views_ui/images/status-active.gif b/core/modules/views_ui/images/status-active.gif deleted file mode 100644 index 207e95c..0000000 --- a/core/modules/views_ui/images/status-active.gif +++ /dev/null @@ -1,10 +0,0 @@ -GIF89a s {{5FZk؋ᵵス! NETSCAPE2.0!, w@!` C4,@1rذ@L` @&0xHrfCj\!g2=08fN\2Ď)48և$*$`!, `0aA*\( (p z@ -=$` +HTiA(`. p8  -00bOFoZ4hL Q\!, Vpa` "ArX(Ѓ N`: - CPD( E` B(g3# -`pą> !, a@!TH(PB "0 ÄRЃ 8p(Xdp0@b!8x$^Tpx!7*0 !, b0aA*\( (p`` 3^ -/ C( `cN: Q00@y@S3Appa@!, Wpa` "Aa'`Q  E @.D  -* 00A66,w.`$I!, ^@TH(PB "0 Äx1C$pQ<2%PdPp`0'4DXf xr$ #/0Da !, _0@*\( (p`` 3^ -/ XA+Pq(A -XaLˆ9q! 2 !, Xpa` "Aa'`Q'); newRow.find('td').append(fakeOperator); newRow.insertBefore(titleRow); - dropdowns = dropdowns.add(fakeOperator); + dropdowns.add(fakeOperator); } return dropdowns; diff --git a/core/modules/views_ui/src/Form/Ajax/ConfigHandler.php b/core/modules/views_ui/src/Form/Ajax/ConfigHandler.php index e82dc42..d3fdd6a 100644 --- a/core/modules/views_ui/src/Form/Ajax/ConfigHandler.php +++ b/core/modules/views_ui/src/Form/Ajax/ConfigHandler.php @@ -8,7 +8,6 @@ namespace Drupal\views_ui\Form\Ajax; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Url; use Drupal\views\ViewEntityInterface; use Drupal\views\ViewExecutable; use Drupal\views\Views; diff --git a/core/modules/views_ui/src/Form/BasicSettingsForm.php b/core/modules/views_ui/src/Form/BasicSettingsForm.php index 1732c9a..712433c 100644 --- a/core/modules/views_ui/src/Form/BasicSettingsForm.php +++ b/core/modules/views_ui/src/Form/BasicSettingsForm.php @@ -134,8 +134,19 @@ public function buildForm(array $form, FormStateInterface $form_state) { ), ); + $form['live_preview']['options']['ui_show_sql_query_enabled'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Show the SQL query'), + '#default_value' => $config->get('ui.show.sql_query.enabled'), + ); + $form['live_preview']['options']['ui_show_sql_query_where'] = array( '#type' => 'radios', + '#states' => array( + 'visible' => array( + ':input[name="ui_show_sql_query_enabled"]' => array('checked' => TRUE), + ), + ), '#title' => t('Show SQL query'), '#options' => array( 'above' => $this->t('Above the preview'), @@ -144,11 +155,6 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => $config->get('ui.show.sql_query.where'), ); - $form['live_preview']['options']['ui_show_sql_query_enabled'] = array( - '#type' => 'checkbox', - '#title' => $this->t('Show the SQL query'), - '#default_value' => $config->get('ui.show.sql_query.enabled'), - ); $form['live_preview']['options']['ui_show_performance_statistics'] = array( '#type' => 'checkbox', '#title' => $this->t('Show performance statistics'), diff --git a/core/modules/views_ui/src/Tests/DisplayTest.php b/core/modules/views_ui/src/Tests/DisplayTest.php index 4fab7d8..7c3eb7a 100644 --- a/core/modules/views_ui/src/Tests/DisplayTest.php +++ b/core/modules/views_ui/src/Tests/DisplayTest.php @@ -182,7 +182,7 @@ public function testLinkDisplay() { // The form redirects to the master display. $this->drupalGet($path); - $this->assertLink(t('Custom URL'), 0, 'The link option has custom url as summary.'); + $this->assertLink(t('Custom URL'), 0, 'The link option has custom URL as summary.'); // Test the default link_url value for new display $this->drupalPostForm(NULL, array(), t('Add Block')); @@ -305,4 +305,32 @@ public function testActionLinks() { $this->assertNoRaw($display_title); } + /** + * Tests that the override option is hidden when it's not needed. + */ + public function testHideDisplayOverride() { + // Test that the override option appears with two displays. + $this->drupalGet('admin/structure/views/nojs/handler/test_display/page_1/field/title'); + $this->assertText('All displays'); + + // Remove a display and test if the override option is hidden. + $this->drupalPostForm('admin/structure/views/view/test_display/edit/block_1', [], t('Delete @display', ['@display' => 'Block'])); + $this->drupalPostForm(NULL, [], t('Save')); + + $this->drupalGet('admin/structure/views/nojs/handler/test_display/page_1/field/title'); + $this->assertNoText('All displays'); + + // Test that the override option is shown when display master is on. + \Drupal::configFactory()->getEditable('views.settings')->set('ui.show.master_display', TRUE)->save(); + $this->drupalGet('admin/structure/views/nojs/handler/test_display/page_1/field/title'); + $this->assertText('All displays'); + + // Test that the override option is shown if the current display is + // overridden so that the option to revert is available. + $this->drupalPostForm(NULL, ['override[dropdown]' => 'page_1'], t('Apply')); + \Drupal::configFactory()->getEditable('views.settings')->set('ui.show.master_display', FALSE)->save(); + $this->drupalGet('admin/structure/views/nojs/handler/test_display/page_1/field/title'); + $this->assertText('Revert to default'); + } + } diff --git a/core/modules/views_ui/src/Tests/FilterUITest.php b/core/modules/views_ui/src/Tests/FilterUITest.php index 1b1cc13..6ed031b 100644 --- a/core/modules/views_ui/src/Tests/FilterUITest.php +++ b/core/modules/views_ui/src/Tests/FilterUITest.php @@ -72,7 +72,7 @@ public function testFiltersUI() { $this->drupalGet('admin/structure/views/view/test_filter_groups'); - $this->assertLink('Content: Node ID (= 1)', 0, 'Content: Node ID (= 1) link appears correctly.'); + $this->assertLink('Content: ID (= 1)', 0, 'Content: ID (= 1) link appears correctly.'); // Tests that we can create a new filter group from UI. $this->drupalGet('admin/structure/views/nojs/rearrange-filter/test_filter_groups/page'); @@ -95,4 +95,33 @@ public function testFiltersUI() { $this->assertNoRaw('Group 3', 'Group 3 has not been added yet.'); } + /** + * Tests the identifier settings and restrictions. + */ + public function testFilterIdentifier() { + $admin_user = $this->drupalCreateUser(array('administer views', 'administer site configuration')); + $this->drupalLogin($admin_user); + $path = 'admin/structure/views/nojs/handler/test_filter_in_operator_ui/default/filter/type'; + + // Set an empty identifier. + $edit = array( + 'options[expose][identifier]' => '', + ); + $this->drupalPostForm($path, $edit, t('Apply')); + $this->assertText('The identifier is required if the filter is exposed.'); + + // Set the identifier to 'value'. + $edit = array( + 'options[expose][identifier]' => 'value', + ); + $this->drupalPostForm($path, $edit, t('Apply')); + $this->assertText('This identifier is not allowed.'); + + // Set the identifier to a value with a restricted character. + $edit = array( + 'options[expose][identifier]' => 'value value', + ); + $this->drupalPostForm($path, $edit, t('Apply')); + $this->assertText('This identifier has illegal characters.'); + } } diff --git a/core/modules/views_ui/src/Tests/HandlerTest.php b/core/modules/views_ui/src/Tests/HandlerTest.php index 0394f10..bc3e018 100644 --- a/core/modules/views_ui/src/Tests/HandlerTest.php +++ b/core/modules/views_ui/src/Tests/HandlerTest.php @@ -245,9 +245,9 @@ public function testNoDuplicateFields() { $add_handler_url = 'admin/structure/views/nojs/add-handler/test_node_view/default/' . $handler_type; $this->drupalGet($add_handler_url); - $this->assertNoDuplicateField('Node ID', 'Content'); - $this->assertNoDuplicateField('Node ID', 'Content revision'); - $this->assertNoDuplicateField('Type', 'Content'); + $this->assertNoDuplicateField('ID', 'Content'); + $this->assertNoDuplicateField('ID', 'Content revision'); + $this->assertNoDuplicateField('Content type', 'Content'); $this->assertNoDuplicateField('UUID', 'Content'); $this->assertNoDuplicateField('Revision ID', 'Content'); $this->assertNoDuplicateField('Revision ID', 'Content revision'); diff --git a/core/modules/views_ui/src/Tests/RedirectTest.php b/core/modules/views_ui/src/Tests/RedirectTest.php index 30d387c..2edb970 100644 --- a/core/modules/views_ui/src/Tests/RedirectTest.php +++ b/core/modules/views_ui/src/Tests/RedirectTest.php @@ -44,7 +44,7 @@ public function testRedirect() { $this->drupalPostForm($path_edit_path, array('path' => $new_path), t('Apply')); $this->drupalPostForm($edit_path, array(), t('Save'), array('query' => array('destination' => 'test-redirect-view'))); - $this->assertUrl($new_path, array(), 'Make sure the user got redirected to the expected page after changing the url of a page display.'); + $this->assertUrl($new_path, array(), 'Make sure the user got redirected to the expected page after changing the URL of a page display.'); } } diff --git a/core/modules/views_ui/src/Tests/TagTest.php b/core/modules/views_ui/src/Tests/TagTest.php deleted file mode 100644 index b18ee98..0000000 --- a/core/modules/views_ui/src/Tests/TagTest.php +++ /dev/null @@ -1,76 +0,0 @@ -loadInclude('views_ui', 'inc', 'admin'); - - // Save 15 views with a tag. - $tags = array(); - for ($i = 0; $i < 16; $i++) { - $suffix = $i % 2 ? 'odd' : 'even'; - $tag = 'autocomplete_tag_test_' . $suffix . $this->randomMachineName(); - $tags[] = $tag; - entity_create('view', array('tag' => $tag, 'id' => $this->randomMachineName()))->save(); - } - - // Make sure just ten results are returns. - $controller = ViewsUIController::create($this->container); - $request = $this->container->get('request_stack')->getCurrentRequest(); - $request->query->set('q', 'autocomplete_tag_test'); - $result = $controller->autocompleteTag($request); - $matches = (array) json_decode($result->getContent(), TRUE); - $this->assertEqual(count($matches), 10, 'Make sure the maximum amount of tag results is 10.'); - - // Make sure the returned array has the proper format. - $suggestions = array_map(function ($tag) { - return array('value' => $tag, 'label' => Html::escape($tag)); - }, $tags); - foreach ($matches as $match) { - $this->assertTrue(in_array($match, $suggestions), 'Make sure the returned array has the proper format.'); - } - - - // Make sure that matching by a certain prefix works. - $request->query->set('q', 'autocomplete_tag_test_even'); - $result = $controller->autocompleteTag($request); - $matches = (array) json_decode($result->getContent(), TRUE); - $this->assertEqual(count($matches), 8, 'Make sure that only a subset is returned.'); - foreach ($matches as $tag) { - $this->assertTrue(array_search($tag['value'], $tags) !== FALSE, format_string('Make sure the returned tag @tag actually exists.', array('@tag' => $tag['value']))); - } - - // Make sure an invalid result doesn't return anything. - $request->query->set('q', $this->randomMachineName()); - $result = $controller->autocompleteTag($request); - $matches = (array) json_decode($result->getContent()); - $this->assertEqual(count($matches), 0, "Make sure an invalid tag doesn't return anything."); - } - -} diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php index 284473c..818c4b4 100644 --- a/core/modules/views_ui/src/ViewEditForm.php +++ b/core/modules/views_ui/src/ViewEditForm.php @@ -15,7 +15,6 @@ use Drupal\Core\Ajax\ReplaceCommand; use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\Element; use Drupal\Core\Render\ElementInfoManagerInterface; use Drupal\Core\Url; use Drupal\user\SharedTempStoreFactory; diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php index 8783c3e..784c59b 100644 --- a/core/modules/views_ui/src/ViewUI.php +++ b/core/modules/views_ui/src/ViewUI.php @@ -12,7 +12,6 @@ use Drupal\Component\Utility\Xss; use Drupal\Core\EventSubscriber\AjaxResponseSubscriber; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Url; use Drupal\views\Views; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\views\ViewExecutable; diff --git a/core/modules/views_ui/tests/src/Kernel/TagTest.php b/core/modules/views_ui/tests/src/Kernel/TagTest.php new file mode 100644 index 0000000..54f34df --- /dev/null +++ b/core/modules/views_ui/tests/src/Kernel/TagTest.php @@ -0,0 +1,77 @@ +loadInclude('views_ui', 'inc', 'admin'); + + // Save 15 views with a tag. + $tags = array(); + for ($i = 0; $i < 16; $i++) { + $suffix = $i % 2 ? 'odd' : 'even'; + $tag = 'autocomplete_tag_test_' . $suffix . $this->randomMachineName(); + $tags[] = $tag; + View::create(array('tag' => $tag, 'id' => $this->randomMachineName()))->save(); + } + + // Make sure just ten results are returns. + $controller = ViewsUIController::create($this->container); + $request = $this->container->get('request_stack')->getCurrentRequest(); + $request->query->set('q', 'autocomplete_tag_test'); + $result = $controller->autocompleteTag($request); + $matches = (array) json_decode($result->getContent(), TRUE); + $this->assertEqual(count($matches), 10, 'Make sure the maximum amount of tag results is 10.'); + + // Make sure the returned array has the proper format. + $suggestions = array_map(function ($tag) { + return array('value' => $tag, 'label' => Html::escape($tag)); + }, $tags); + foreach ($matches as $match) { + $this->assertTrue(in_array($match, $suggestions), 'Make sure the returned array has the proper format.'); + } + + + // Make sure that matching by a certain prefix works. + $request->query->set('q', 'autocomplete_tag_test_even'); + $result = $controller->autocompleteTag($request); + $matches = (array) json_decode($result->getContent(), TRUE); + $this->assertEqual(count($matches), 8, 'Make sure that only a subset is returned.'); + foreach ($matches as $tag) { + $this->assertTrue(array_search($tag['value'], $tags) !== FALSE, format_string('Make sure the returned tag @tag actually exists.', array('@tag' => $tag['value']))); + } + + // Make sure an invalid result doesn't return anything. + $request->query->set('q', $this->randomMachineName()); + $result = $controller->autocompleteTag($request); + $matches = (array) json_decode($result->getContent()); + $this->assertEqual(count($matches), 0, "Make sure an invalid tag doesn't return anything."); + } + +} diff --git a/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php b/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php index 4089d3a..215728c 100644 --- a/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php +++ b/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php @@ -7,7 +7,6 @@ namespace Drupal\Tests\views_ui\Unit; -use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Entity\EntityInterface; use Drupal\Tests\UnitTestCase; @@ -168,8 +167,6 @@ public function testBuildRowEntityList() { $display_paths = $row['data']['path']['data']['#items']; // These values will be escaped by Twig when rendered. $this->assertEquals('/test_page, /malformed_path, /', implode(', ', $display_paths)); - $this->assertFalse(SafeMarkup::isSafe('/malformed_path'), '/ & < > " \' ', + ); + + // Encode and write, and reload and decode the configuration data. + $filestorage = new FileStorage(config_get_config_directory(CONFIG_SYNC_DIRECTORY)); + $filestorage->write($name, $config_data); + $config_parsed = $filestorage->read($name); + + $key = 'numeric keys'; + $this->assertIdentical($config_data[$key], $config_parsed[$key]); + + $key = 'nested keys'; + $this->assertIdentical($config_data[$key], $config_parsed[$key]); + + $key = 'HTML'; + $this->assertIdentical($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]); + + $key = 'UTF-8'; + $this->assertIdentical($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]); + + $key = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΣὨ'; + $this->assertIdentical($config_data['nested keys'][$key], $config_parsed['nested keys'][$key]); + + $key = 'invalid xml'; + $this->assertIdentical($config_data[$key], $config_parsed[$key]); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigImportRecreateTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigImportRecreateTest.php new file mode 100644 index 0000000..26a08de --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigImportRecreateTest.php @@ -0,0 +1,110 @@ +installEntitySchema('node'); + $this->installConfig(array('field', 'node')); + + $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync')); + + // Set up the ConfigImporter object for testing. + $storage_comparer = new StorageComparer( + $this->container->get('config.storage.sync'), + $this->container->get('config.storage'), + $this->container->get('config.manager') + ); + $this->configImporter = new ConfigImporter( + $storage_comparer->createChangelist(), + $this->container->get('event_dispatcher'), + $this->container->get('config.manager'), + $this->container->get('lock'), + $this->container->get('config.typed'), + $this->container->get('module_handler'), + $this->container->get('module_installer'), + $this->container->get('theme_handler'), + $this->container->get('string_translation') + ); + } + + public function testRecreateEntity() { + $type_name = Unicode::strtolower($this->randomMachineName(16)); + $content_type = NodeType::create([ + 'type' => $type_name, + 'name' => 'Node type one', + ]); + $content_type->save(); + node_add_body_field($content_type); + /** @var \Drupal\Core\Config\StorageInterface $active */ + $active = $this->container->get('config.storage'); + /** @var \Drupal\Core\Config\StorageInterface $sync */ + $sync = $this->container->get('config.storage.sync'); + + $config_name = $content_type->getEntityType()->getConfigPrefix() . '.' . $content_type->id(); + $this->copyConfig($active, $sync); + + // Delete the content type. This will also delete a field storage, a field, + // an entity view display and an entity form display. + $content_type->delete(); + $this->assertFalse($active->exists($config_name), 'Content type\'s old name does not exist active store.'); + // Recreate with the same type - this will have a different UUID. + $content_type = NodeType::create([ + 'type' => $type_name, + 'name' => 'Node type two', + ]); + $content_type->save(); + node_add_body_field($content_type); + + $this->configImporter->reset(); + // A node type, a field, an entity view display and an entity form display + // will be recreated. + $creates = $this->configImporter->getUnprocessedConfiguration('create'); + $deletes = $this->configImporter->getUnprocessedConfiguration('delete'); + $this->assertEqual(5, count($creates), 'There are 5 configuration items to create.'); + $this->assertEqual(5, count($deletes), 'There are 5 configuration items to delete.'); + $this->assertEqual(0, count($this->configImporter->getUnprocessedConfiguration('update')), 'There are no configuration items to update.'); + $this->assertIdentical($creates, array_reverse($deletes), 'Deletes and creates contain the same configuration names in opposite orders due to dependencies.'); + + $this->configImporter->import(); + + // Verify that there is nothing more to import. + $this->assertFalse($this->configImporter->reset()->hasUnprocessedConfigurationChanges()); + $content_type = NodeType::load($type_name); + $this->assertEqual('Node type one', $content_type->label()); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigImportRenameValidationTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigImportRenameValidationTest.php new file mode 100644 index 0000000..80b4cb1 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigImportRenameValidationTest.php @@ -0,0 +1,163 @@ +installEntitySchema('user'); + $this->installEntitySchema('node'); + $this->installConfig(array('field')); + + // Set up the ConfigImporter object for testing. + $storage_comparer = new StorageComparer( + $this->container->get('config.storage.sync'), + $this->container->get('config.storage'), + $this->container->get('config.manager') + ); + $this->configImporter = new ConfigImporter( + $storage_comparer->createChangelist(), + $this->container->get('event_dispatcher'), + $this->container->get('config.manager'), + $this->container->get('lock.persistent'), + $this->container->get('config.typed'), + $this->container->get('module_handler'), + $this->container->get('module_installer'), + $this->container->get('theme_handler'), + $this->container->get('string_translation') + ); + } + + /** + * Tests configuration renaming validation. + */ + public function testRenameValidation() { + // Create a test entity. + $test_entity_id = $this->randomMachineName(); + $test_entity = entity_create('config_test', array( + 'id' => $test_entity_id, + 'label' => $this->randomMachineName(), + )); + $test_entity->save(); + $uuid = $test_entity->uuid(); + + // Stage the test entity and then delete it from the active storage. + $active = $this->container->get('config.storage'); + $sync = $this->container->get('config.storage.sync'); + $this->copyConfig($active, $sync); + $test_entity->delete(); + + // Create a content type with a matching UUID in the active storage. + $content_type = NodeType::create([ + 'type' => Unicode::strtolower($this->randomMachineName(16)), + 'name' => $this->randomMachineName(), + 'uuid' => $uuid, + ]); + $content_type->save(); + + // Confirm that the staged configuration is detected as a rename since the + // UUIDs match. + $this->configImporter->reset(); + $expected = array( + 'node.type.' . $content_type->id() . '::config_test.dynamic.' . $test_entity_id, + ); + $renames = $this->configImporter->getUnprocessedConfiguration('rename'); + $this->assertIdentical($expected, $renames); + + // Try to import the configuration. We expect an exception to be thrown + // because the staged entity is of a different type. + try { + $this->configImporter->import(); + $this->fail('Expected ConfigImporterException thrown when a renamed configuration entity does not match the existing entity type.'); + } + catch (ConfigImporterException $e) { + $this->pass('Expected ConfigImporterException thrown when a renamed configuration entity does not match the existing entity type.'); + $expected = array( + SafeMarkup::format('Entity type mismatch on rename. @old_type not equal to @new_type for existing configuration @old_name and staged configuration @new_name.', array('@old_type' => 'node_type', '@new_type' => 'config_test', '@old_name' => 'node.type.' . $content_type->id(), '@new_name' => 'config_test.dynamic.' . $test_entity_id)) + ); + $this->assertEqual($expected, $this->configImporter->getErrors()); + } + } + + /** + * Tests configuration renaming validation for simple configuration. + */ + public function testRenameSimpleConfigValidation() { + $uuid = new Php(); + // Create a simple configuration with a UUID. + $config = $this->config('config_test.new'); + $uuid_value = $uuid->generate(); + $config->set('uuid', $uuid_value)->save(); + + $active = $this->container->get('config.storage'); + $sync = $this->container->get('config.storage.sync'); + $this->copyConfig($active, $sync); + $config->delete(); + + // Create another simple configuration with the same UUID. + $config = $this->config('config_test.old'); + $config->set('uuid', $uuid_value)->save(); + + // Confirm that the staged configuration is detected as a rename since the + // UUIDs match. + $this->configImporter->reset(); + $expected = array( + 'config_test.old::config_test.new' + ); + $renames = $this->configImporter->getUnprocessedConfiguration('rename'); + $this->assertIdentical($expected, $renames); + + // Try to import the configuration. We expect an exception to be thrown + // because the rename is for simple configuration. + try { + $this->configImporter->import(); + $this->fail('Expected ConfigImporterException thrown when simple configuration is renamed.'); + } + catch (ConfigImporterException $e) { + $this->pass('Expected ConfigImporterException thrown when simple configuration is renamed.'); + $expected = array( + SafeMarkup::format('Rename operation for simple configuration. Existing configuration @old_name and staged configuration @new_name.', array('@old_name' => 'config_test.old', '@new_name' => 'config_test.new')) + ); + $this->assertEqual($expected, $this->configImporter->getErrors()); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterMissingContentTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterMissingContentTest.php new file mode 100644 index 0000000..7b4dc0c --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterMissingContentTest.php @@ -0,0 +1,106 @@ +installSchema('system', 'sequences'); + $this->installEntitySchema('entity_test'); + $this->installEntitySchema('user'); + $this->installConfig(array('config_test')); + // Installing config_test's default configuration pollutes the global + // variable being used for recording hook invocations by this test already, + // so it has to be cleared out manually. + unset($GLOBALS['hook_config_test']); + + $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync')); + + // Set up the ConfigImporter object for testing. + $storage_comparer = new StorageComparer( + $this->container->get('config.storage.sync'), + $this->container->get('config.storage'), + $this->container->get('config.manager') + ); + $this->configImporter = new ConfigImporter( + $storage_comparer->createChangelist(), + $this->container->get('event_dispatcher'), + $this->container->get('config.manager'), + $this->container->get('lock'), + $this->container->get('config.typed'), + $this->container->get('module_handler'), + $this->container->get('module_installer'), + $this->container->get('theme_handler'), + $this->container->get('string_translation') + ); + } + + /** + * Tests the missing content event is fired. + * + * @see \Drupal\Core\Config\ConfigImporter::processMissingContent() + * @see \Drupal\config_import_test\EventSubscriber + */ + function testMissingContent() { + \Drupal::state()->set('config_import_test.config_import_missing_content', TRUE); + + // Update a configuration entity in the sync directory to have a dependency + // on two content entities that do not exist. + $storage = $this->container->get('config.storage'); + $sync = $this->container->get('config.storage.sync'); + $entity_one = EntityTest::create(array('name' => 'one')); + $entity_two = EntityTest::create(array('name' => 'two')); + $entity_three = EntityTest::create(array('name' => 'three')); + $dynamic_name = 'config_test.dynamic.dotted.default'; + $original_dynamic_data = $storage->read($dynamic_name); + // Entity one will be resolved by + // \Drupal\config_import_test\EventSubscriber::onConfigImporterMissingContentOne(). + $original_dynamic_data['dependencies']['content'][] = $entity_one->getConfigDependencyName(); + // Entity two will be resolved by + // \Drupal\config_import_test\EventSubscriber::onConfigImporterMissingContentTwo(). + $original_dynamic_data['dependencies']['content'][] = $entity_two->getConfigDependencyName(); + // Entity three will be resolved by + // \Drupal\Core\Config\Importer\FinalMissingContentSubscriber. + $original_dynamic_data['dependencies']['content'][] = $entity_three->getConfigDependencyName(); + $sync->write($dynamic_name, $original_dynamic_data); + + // Import. + $this->configImporter->reset()->import(); + $this->assertEqual([], $this->configImporter->getErrors(), 'There were no errors during the import.'); + $this->assertEqual($entity_one->uuid(), \Drupal::state()->get('config_import_test.config_import_missing_content_one'), 'The missing content event is fired during configuration import.'); + $this->assertEqual($entity_two->uuid(), \Drupal::state()->get('config_import_test.config_import_missing_content_two'), 'The missing content event is fired during configuration import.'); + $original_dynamic_data = $storage->read($dynamic_name); + $this->assertEqual([$entity_one->getConfigDependencyName(), $entity_two->getConfigDependencyName(), $entity_three->getConfigDependencyName()], $original_dynamic_data['dependencies']['content'], 'The imported configuration entity has the missing content entity dependency.'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php new file mode 100644 index 0000000..62b6228 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigImporterTest.php @@ -0,0 +1,694 @@ +installConfig(array('config_test')); + // Installing config_test's default configuration pollutes the global + // variable being used for recording hook invocations by this test already, + // so it has to be cleared out manually. + unset($GLOBALS['hook_config_test']); + + $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync')); + + // Set up the ConfigImporter object for testing. + $storage_comparer = new StorageComparer( + $this->container->get('config.storage.sync'), + $this->container->get('config.storage'), + $this->container->get('config.manager') + ); + $this->configImporter = new ConfigImporter( + $storage_comparer->createChangelist(), + $this->container->get('event_dispatcher'), + $this->container->get('config.manager'), + $this->container->get('lock'), + $this->container->get('config.typed'), + $this->container->get('module_handler'), + $this->container->get('module_installer'), + $this->container->get('theme_handler'), + $this->container->get('string_translation') + ); + } + + /** + * Tests omission of module APIs for bare configuration operations. + */ + function testNoImport() { + $dynamic_name = 'config_test.dynamic.dotted.default'; + + // Verify the default configuration values exist. + $config = $this->config($dynamic_name); + $this->assertIdentical($config->get('id'), 'dotted.default'); + + // Verify that a bare $this->config() does not involve module APIs. + $this->assertFalse(isset($GLOBALS['hook_config_test'])); + } + + /** + * Tests that trying to import from an empty sync configuration directory + * fails. + */ + function testEmptyImportFails() { + try { + $this->container->get('config.storage.sync')->deleteAll(); + $this->configImporter->reset()->import(); + $this->fail('ConfigImporterException thrown, successfully stopping an empty import.'); + } + catch (ConfigImporterException $e) { + $this->pass('ConfigImporterException thrown, successfully stopping an empty import.'); + } + } + + /** + * Tests verification of site UUID before importing configuration. + */ + function testSiteUuidValidate() { + $sync = \Drupal::service('config.storage.sync'); + // Create updated configuration object. + $config_data = $this->config('system.site')->get(); + // Generate a new site UUID. + $config_data['uuid'] = \Drupal::service('uuid')->generate(); + $sync->write('system.site', $config_data); + try { + $this->configImporter->reset()->import(); + $this->fail('ConfigImporterException not thrown, invalid import was not stopped due to mis-matching site UUID.'); + } + catch (ConfigImporterException $e) { + $this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.'); + $error_log = $this->configImporter->getErrors(); + $expected = array('Site UUID in source storage does not match the target storage.'); + $this->assertEqual($expected, $error_log); + } + } + + /** + * Tests deletion of configuration during import. + */ + function testDeleted() { + $dynamic_name = 'config_test.dynamic.dotted.default'; + $storage = $this->container->get('config.storage'); + $sync = $this->container->get('config.storage.sync'); + + // Verify the default configuration values exist. + $config = $this->config($dynamic_name); + $this->assertIdentical($config->get('id'), 'dotted.default'); + + // Delete the file from the sync directory. + $sync->delete($dynamic_name); + + // Import. + $this->configImporter->reset()->import(); + + // Verify the file has been removed. + $this->assertIdentical($storage->read($dynamic_name), FALSE); + + $config = $this->config($dynamic_name); + $this->assertIdentical($config->get('id'), NULL); + + // Verify that appropriate module API hooks have been invoked. + $this->assertTrue(isset($GLOBALS['hook_config_test']['load'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['presave'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['insert'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['update'])); + $this->assertTrue(isset($GLOBALS['hook_config_test']['predelete'])); + $this->assertTrue(isset($GLOBALS['hook_config_test']['delete'])); + + $this->assertFalse($this->configImporter->hasUnprocessedConfigurationChanges()); + $logs = $this->configImporter->getErrors(); + $this->assertEqual(count($logs), 0); + } + + /** + * Tests creation of configuration during import. + */ + function testNew() { + $dynamic_name = 'config_test.dynamic.new'; + $storage = $this->container->get('config.storage'); + $sync = $this->container->get('config.storage.sync'); + + // Verify the configuration to create does not exist yet. + $this->assertIdentical($storage->exists($dynamic_name), FALSE, $dynamic_name . ' not found.'); + + // Create new config entity. + $original_dynamic_data = array( + 'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651', + 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(), + 'status' => TRUE, + 'dependencies' => array(), + 'id' => 'new', + 'label' => 'New', + 'weight' => 0, + 'style' => '', + 'size' => '', + 'size_value' => '', + 'protected_property' => '', + ); + $sync->write($dynamic_name, $original_dynamic_data); + + $this->assertIdentical($sync->exists($dynamic_name), TRUE, $dynamic_name . ' found.'); + + // Import. + $this->configImporter->reset()->import(); + + // Verify the values appeared. + $config = $this->config($dynamic_name); + $this->assertIdentical($config->get('label'), $original_dynamic_data['label']); + + // Verify that appropriate module API hooks have been invoked. + $this->assertFalse(isset($GLOBALS['hook_config_test']['load'])); + $this->assertTrue(isset($GLOBALS['hook_config_test']['presave'])); + $this->assertTrue(isset($GLOBALS['hook_config_test']['insert'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['update'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['predelete'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['delete'])); + + // Verify that hook_config_import_steps_alter() can add steps to + // configuration synchronization. + $this->assertTrue(isset($GLOBALS['hook_config_test']['config_import_steps_alter'])); + + // Verify that there is nothing more to import. + $this->assertFalse($this->configImporter->hasUnprocessedConfigurationChanges()); + $logs = $this->configImporter->getErrors(); + $this->assertEqual(count($logs), 0); + } + + /** + * Tests that secondary writes are overwritten. + */ + function testSecondaryWritePrimaryFirst() { + $name_primary = 'config_test.dynamic.primary'; + $name_secondary = 'config_test.dynamic.secondary'; + $sync = $this->container->get('config.storage.sync'); + $uuid = $this->container->get('uuid'); + + $values_primary = array( + 'id' => 'primary', + 'label' => 'Primary', + 'weight' => 0, + 'uuid' => $uuid->generate(), + ); + $sync->write($name_primary, $values_primary); + $values_secondary = array( + 'id' => 'secondary', + 'label' => 'Secondary Sync', + 'weight' => 0, + 'uuid' => $uuid->generate(), + // Add a dependency on primary, to ensure that is synced first. + 'dependencies' => array( + 'config' => array($name_primary), + ) + ); + $sync->write($name_secondary, $values_secondary); + + // Import. + $this->configImporter->reset()->import(); + + $entity_storage = \Drupal::entityManager()->getStorage('config_test'); + $primary = $entity_storage->load('primary'); + $this->assertEqual($primary->id(), 'primary'); + $this->assertEqual($primary->uuid(), $values_primary['uuid']); + $this->assertEqual($primary->label(), $values_primary['label']); + $secondary = $entity_storage->load('secondary'); + $this->assertEqual($secondary->id(), 'secondary'); + $this->assertEqual($secondary->uuid(), $values_secondary['uuid']); + $this->assertEqual($secondary->label(), $values_secondary['label']); + + $logs = $this->configImporter->getErrors(); + $this->assertEqual(count($logs), 1); + $this->assertEqual($logs[0], SafeMarkup::format('Deleted and replaced configuration entity "@name"', array('@name' => $name_secondary))); + } + + /** + * Tests that secondary writes are overwritten. + */ + function testSecondaryWriteSecondaryFirst() { + $name_primary = 'config_test.dynamic.primary'; + $name_secondary = 'config_test.dynamic.secondary'; + $sync = $this->container->get('config.storage.sync'); + $uuid = $this->container->get('uuid'); + + $values_primary = array( + 'id' => 'primary', + 'label' => 'Primary', + 'weight' => 0, + 'uuid' => $uuid->generate(), + // Add a dependency on secondary, so that is synced first. + 'dependencies' => array( + 'config' => array($name_secondary), + ) + ); + $sync->write($name_primary, $values_primary); + $values_secondary = array( + 'id' => 'secondary', + 'label' => 'Secondary Sync', + 'weight' => 0, + 'uuid' => $uuid->generate(), + ); + $sync->write($name_secondary, $values_secondary); + + // Import. + $this->configImporter->reset()->import(); + + $entity_storage = \Drupal::entityManager()->getStorage('config_test'); + $primary = $entity_storage->load('primary'); + $this->assertEqual($primary->id(), 'primary'); + $this->assertEqual($primary->uuid(), $values_primary['uuid']); + $this->assertEqual($primary->label(), $values_primary['label']); + $secondary = $entity_storage->load('secondary'); + $this->assertEqual($secondary->id(), 'secondary'); + $this->assertEqual($secondary->uuid(), $values_secondary['uuid']); + $this->assertEqual($secondary->label(), $values_secondary['label']); + + $logs = $this->configImporter->getErrors(); + $this->assertEqual(count($logs), 1); + $this->assertEqual($logs[0], Html::escape("Unexpected error during import with operation create for $name_primary: 'config_test' entity with ID 'secondary' already exists.")); + } + + /** + * Tests that secondary updates for deleted files work as expected. + */ + function testSecondaryUpdateDeletedDeleterFirst() { + $name_deleter = 'config_test.dynamic.deleter'; + $name_deletee = 'config_test.dynamic.deletee'; + $name_other = 'config_test.dynamic.other'; + $storage = $this->container->get('config.storage'); + $sync = $this->container->get('config.storage.sync'); + $uuid = $this->container->get('uuid'); + + $values_deleter = array( + 'id' => 'deleter', + 'label' => 'Deleter', + 'weight' => 0, + 'uuid' => $uuid->generate(), + ); + $storage->write($name_deleter, $values_deleter); + $values_deleter['label'] = 'Updated Deleter'; + $sync->write($name_deleter, $values_deleter); + $values_deletee = array( + 'id' => 'deletee', + 'label' => 'Deletee', + 'weight' => 0, + 'uuid' => $uuid->generate(), + // Add a dependency on deleter, to make sure that is synced first. + 'dependencies' => array( + 'config' => array($name_deleter), + ) + ); + $storage->write($name_deletee, $values_deletee); + $values_deletee['label'] = 'Updated Deletee'; + $sync->write($name_deletee, $values_deletee); + + // Ensure that import will continue after the error. + $values_other = array( + 'id' => 'other', + 'label' => 'Other', + 'weight' => 0, + 'uuid' => $uuid->generate(), + // Add a dependency on deleter, to make sure that is synced first. This + // will also be synced after the deletee due to alphabetical ordering. + 'dependencies' => array( + 'config' => array($name_deleter), + ) + ); + $storage->write($name_other, $values_other); + $values_other['label'] = 'Updated other'; + $sync->write($name_other, $values_other); + + // Check update changelist order. + $updates = $this->configImporter->reset()->getStorageComparer()->getChangelist('update'); + $expected = array( + $name_deleter, + $name_deletee, + $name_other, + ); + $this->assertIdentical($expected, $updates); + + // Import. + $this->configImporter->import(); + + $entity_storage = \Drupal::entityManager()->getStorage('config_test'); + $deleter = $entity_storage->load('deleter'); + $this->assertEqual($deleter->id(), 'deleter'); + $this->assertEqual($deleter->uuid(), $values_deleter['uuid']); + $this->assertEqual($deleter->label(), $values_deleter['label']); + + // The deletee was deleted in + // \Drupal\config_test\Entity\ConfigTest::postSave(). + $this->assertFalse($entity_storage->load('deletee')); + + $other = $entity_storage->load('other'); + $this->assertEqual($other->id(), 'other'); + $this->assertEqual($other->uuid(), $values_other['uuid']); + $this->assertEqual($other->label(), $values_other['label']); + + $logs = $this->configImporter->getErrors(); + $this->assertEqual(count($logs), 1); + $this->assertEqual($logs[0], SafeMarkup::format('Update target "@name" is missing.', array('@name' => $name_deletee))); + } + + /** + * Tests that secondary updates for deleted files work as expected. + * + * This test is completely hypothetical since we only support full + * configuration tree imports. Therefore, any configuration updates that cause + * secondary deletes should be reflected already in the staged configuration. + */ + function testSecondaryUpdateDeletedDeleteeFirst() { + $name_deleter = 'config_test.dynamic.deleter'; + $name_deletee = 'config_test.dynamic.deletee'; + $storage = $this->container->get('config.storage'); + $sync = $this->container->get('config.storage.sync'); + $uuid = $this->container->get('uuid'); + + $values_deleter = array( + 'id' => 'deleter', + 'label' => 'Deleter', + 'weight' => 0, + 'uuid' => $uuid->generate(), + // Add a dependency on deletee, to make sure that is synced first. + 'dependencies' => array( + 'config' => array($name_deletee), + ), + ); + $storage->write($name_deleter, $values_deleter); + $values_deleter['label'] = 'Updated Deleter'; + $sync->write($name_deleter, $values_deleter); + $values_deletee = array( + 'id' => 'deletee', + 'label' => 'Deletee', + 'weight' => 0, + 'uuid' => $uuid->generate(), + ); + $storage->write($name_deletee, $values_deletee); + $values_deletee['label'] = 'Updated Deletee'; + $sync->write($name_deletee, $values_deletee); + + // Import. + $this->configImporter->reset()->import(); + + $entity_storage = \Drupal::entityManager()->getStorage('config_test'); + // Both entities are deleted. ConfigTest::postSave() causes updates of the + // deleter entity to delete the deletee entity. Since the deleter depends on + // the deletee, removing the deletee causes the deleter to be removed. + $this->assertFalse($entity_storage->load('deleter')); + $this->assertFalse($entity_storage->load('deletee')); + $logs = $this->configImporter->getErrors(); + $this->assertEqual(count($logs), 0); + } + + /** + * Tests that secondary deletes for deleted files work as expected. + */ + function testSecondaryDeletedDeleteeSecond() { + $name_deleter = 'config_test.dynamic.deleter'; + $name_deletee = 'config_test.dynamic.deletee'; + $storage = $this->container->get('config.storage'); + + $uuid = $this->container->get('uuid'); + + $values_deleter = array( + 'id' => 'deleter', + 'label' => 'Deleter', + 'weight' => 0, + 'uuid' => $uuid->generate(), + // Add a dependency on deletee, to make sure this delete is synced first. + 'dependencies' => array( + 'config' => array($name_deletee), + ), + ); + $storage->write($name_deleter, $values_deleter); + $values_deletee = array( + 'id' => 'deletee', + 'label' => 'Deletee', + 'weight' => 0, + 'uuid' => $uuid->generate(), + ); + $storage->write($name_deletee, $values_deletee); + + // Import. + $this->configImporter->reset()->import(); + + $entity_storage = \Drupal::entityManager()->getStorage('config_test'); + $this->assertFalse($entity_storage->load('deleter')); + $this->assertFalse($entity_storage->load('deletee')); + // The deletee entity does not exist as the delete worked and although the + // delete occurred in \Drupal\config_test\Entity\ConfigTest::postDelete() + // this does not matter. + $logs = $this->configImporter->getErrors(); + $this->assertEqual(count($logs), 0); + } + + /** + * Tests updating of configuration during import. + */ + function testUpdated() { + $name = 'config_test.system'; + $dynamic_name = 'config_test.dynamic.dotted.default'; + $storage = $this->container->get('config.storage'); + $sync = $this->container->get('config.storage.sync'); + + // Verify that the configuration objects to import exist. + $this->assertIdentical($storage->exists($name), TRUE, $name . ' found.'); + $this->assertIdentical($storage->exists($dynamic_name), TRUE, $dynamic_name . ' found.'); + + // Replace the file content of the existing configuration objects in the + // sync directory. + $original_name_data = array( + 'foo' => 'beer', + ); + $sync->write($name, $original_name_data); + $original_dynamic_data = $storage->read($dynamic_name); + $original_dynamic_data['label'] = 'Updated'; + $sync->write($dynamic_name, $original_dynamic_data); + + // Verify the active configuration still returns the default values. + $config = $this->config($name); + $this->assertIdentical($config->get('foo'), 'bar'); + $config = $this->config($dynamic_name); + $this->assertIdentical($config->get('label'), 'Default'); + + // Import. + $this->configImporter->reset()->import(); + + // Verify the values were updated. + \Drupal::configFactory()->reset($name); + $config = $this->config($name); + $this->assertIdentical($config->get('foo'), 'beer'); + $config = $this->config($dynamic_name); + $this->assertIdentical($config->get('label'), 'Updated'); + + // Verify that the original file content is still the same. + $this->assertIdentical($sync->read($name), $original_name_data); + $this->assertIdentical($sync->read($dynamic_name), $original_dynamic_data); + + // Verify that appropriate module API hooks have been invoked. + $this->assertTrue(isset($GLOBALS['hook_config_test']['load'])); + $this->assertTrue(isset($GLOBALS['hook_config_test']['presave'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['insert'])); + $this->assertTrue(isset($GLOBALS['hook_config_test']['update'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['predelete'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['delete'])); + + // Verify that there is nothing more to import. + $this->assertFalse($this->configImporter->hasUnprocessedConfigurationChanges()); + $logs = $this->configImporter->getErrors(); + $this->assertEqual(count($logs), 0); + } + + /** + * Tests the isInstallable method() + */ + function testIsInstallable() { + $config_name = 'config_test.dynamic.isinstallable'; + $this->assertFalse($this->container->get('config.storage')->exists($config_name)); + \Drupal::state()->set('config_test.isinstallable', TRUE); + $this->installConfig(array('config_test')); + $this->assertTrue($this->container->get('config.storage')->exists($config_name)); + } + + /** + * Tests dependency validation during configuration import. + * + * @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber + * @see \Drupal\Core\Config\ConfigImporter::createExtensionChangelist() + */ + public function testUnmetDependency() { + $storage = $this->container->get('config.storage'); + $sync = $this->container->get('config.storage.sync'); + + // Test an unknown configuration owner. + $sync->write('unknown.config', ['test' => 'test']); + + // Make a config entity have unmet dependencies. + $config_entity_data = $sync->read('config_test.dynamic.dotted.default'); + $config_entity_data['dependencies'] = ['module' => ['unknown']]; + $sync->write('config_test.dynamic.dotted.module', $config_entity_data); + $config_entity_data['dependencies'] = ['theme' => ['unknown']]; + $sync->write('config_test.dynamic.dotted.theme', $config_entity_data); + $config_entity_data['dependencies'] = ['config' => ['unknown']]; + $sync->write('config_test.dynamic.dotted.config', $config_entity_data); + + // Make an active config depend on something that is missing in sync. + // The whole configuration needs to be consistent, not only the updated one. + $config_entity_data['dependencies'] = []; + $storage->write('config_test.dynamic.dotted.deleted', $config_entity_data); + $config_entity_data['dependencies'] = ['config' => ['config_test.dynamic.dotted.deleted']]; + $storage->write('config_test.dynamic.dotted.existing', $config_entity_data); + $sync->write('config_test.dynamic.dotted.existing', $config_entity_data); + + $extensions = $sync->read('core.extension'); + // Add a module and a theme that do not exist. + $extensions['module']['unknown_module'] = 0; + $extensions['theme']['unknown_theme'] = 0; + // Add a module and a theme that depend on uninstalled extensions. + $extensions['module']['book'] = 0; + $extensions['theme']['bartik'] = 0; + + $sync->write('core.extension', $extensions); + try { + $this->configImporter->reset()->import(); + $this->fail('ConfigImporterException not thrown; an invalid import was not stopped due to missing dependencies.'); + } + catch (ConfigImporterException $e) { + $this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.'); + $error_log = $this->configImporter->getErrors(); + $expected = [ + 'Unable to install the unknown_module module since it does not exist.', + 'Unable to install the Book module since it requires the Node, Text, Field, Filter, User modules.', + 'Unable to install the unknown_theme theme since it does not exist.', + 'Unable to install the Bartik theme since it requires the Classy theme.', + 'Configuration config_test.dynamic.dotted.config depends on the unknown configuration that will not exist after import.', + 'Configuration config_test.dynamic.dotted.existing depends on the config_test.dynamic.dotted.deleted configuration that will not exist after import.', + 'Configuration config_test.dynamic.dotted.module depends on the unknown module that will not be installed after import.', + 'Configuration config_test.dynamic.dotted.theme depends on the unknown theme that will not be installed after import.', + 'Configuration unknown.config depends on the unknown extension that will not be installed after import.', + ]; + foreach ($expected as $expected_message) { + $this->assertTrue(in_array($expected_message, $error_log), $expected_message); + } + } + + // Make a config entity have mulitple unmet dependencies. + $config_entity_data = $sync->read('config_test.dynamic.dotted.default'); + $config_entity_data['dependencies'] = ['module' => ['unknown', 'dblog']]; + $sync->write('config_test.dynamic.dotted.module', $config_entity_data); + $config_entity_data['dependencies'] = ['theme' => ['unknown', 'seven']]; + $sync->write('config_test.dynamic.dotted.theme', $config_entity_data); + $config_entity_data['dependencies'] = ['config' => ['unknown', 'unknown2']]; + $sync->write('config_test.dynamic.dotted.config', $config_entity_data); + try { + $this->configImporter->reset()->import(); + $this->fail('ConfigImporterException not thrown, invalid import was not stopped due to missing dependencies.'); + } + catch (ConfigImporterException $e) { + $this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.'); + $error_log = $this->configImporter->getErrors(); + $expected = [ + 'Configuration config_test.dynamic.dotted.config depends on configuration (unknown, unknown2) that will not exist after import.', + 'Configuration config_test.dynamic.dotted.module depends on modules (unknown, Database Logging) that will not be installed after import.', + 'Configuration config_test.dynamic.dotted.theme depends on themes (unknown, Seven) that will not be installed after import.', + ]; + foreach ($expected as $expected_message) { + $this->assertTrue(in_array($expected_message, $error_log), $expected_message); + } + } + } + + /** + * Tests missing core.extension during configuration import. + * + * @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber + */ + public function testMissingCoreExtension() { + $sync = $this->container->get('config.storage.sync'); + $sync->delete('core.extension'); + try { + $this->configImporter->reset()->import(); + $this->fail('ConfigImporterException not thrown, invalid import was not stopped due to missing dependencies.'); + } + catch (ConfigImporterException $e) { + $this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.'); + $error_log = $this->configImporter->getErrors(); + $this->assertEqual(['The core.extension configuration does not exist.'], $error_log); + } + } + + /** + * Tests install profile validation during configuration import. + * + * @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber + */ + public function testInstallProfile() { + $sync = $this->container->get('config.storage.sync'); + + $extensions = $sync->read('core.extension'); + // Add an install profile. + $extensions['module']['standard'] = 0; + + $sync->write('core.extension', $extensions); + try { + $this->configImporter->reset()->import(); + $this->fail('ConfigImporterException not thrown; an invalid import was not stopped due to missing dependencies.'); + } + catch (ConfigImporterException $e) { + $this->assertEqual($e->getMessage(), 'There were errors validating the config synchronization.'); + $error_log = $this->configImporter->getErrors(); + // Install profiles should not even be scanned at this point. + $this->assertEqual(['Unable to install the standard module since it does not exist.'], $error_log); + } + } + + /** + * Tests config_get_config_directory(). + */ + public function testConfigGetConfigDirectory() { + global $config_directories; + $directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY); + $this->assertEqual($config_directories[CONFIG_SYNC_DIRECTORY], $directory); + + $message = 'Calling config_get_config_directory() with CONFIG_ACTIVE_DIRECTORY results in an exception.'; + try { + config_get_config_directory(CONFIG_ACTIVE_DIRECTORY); + $this->fail($message); + } + catch (\Exception $e) { + $this->pass($message); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigInstallTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigInstallTest.php new file mode 100644 index 0000000..9522370 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigInstallTest.php @@ -0,0 +1,253 @@ +config($default_config); + $this->assertIdentical($config->isNew(), TRUE); + $config = $this->config($default_configuration_entity); + $this->assertIdentical($config->isNew(), TRUE); + + // Ensure that schema provided by modules that are not installed is not + // available. + $this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'), 'Configuration schema for config_schema_test.someschema does not exist.'); + + // Install the test module. + $this->installModules(array('config_test')); + + // Verify that default module config exists. + \Drupal::configFactory()->reset($default_config); + \Drupal::configFactory()->reset($default_configuration_entity); + $config = $this->config($default_config); + $this->assertIdentical($config->isNew(), FALSE); + $config = $this->config($default_configuration_entity); + $this->assertIdentical($config->isNew(), FALSE); + + // Verify that config_test API hooks were invoked for the dynamic default + // configuration entity. + $this->assertFalse(isset($GLOBALS['hook_config_test']['load'])); + $this->assertTrue(isset($GLOBALS['hook_config_test']['presave'])); + $this->assertTrue(isset($GLOBALS['hook_config_test']['insert'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['update'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['predelete'])); + $this->assertFalse(isset($GLOBALS['hook_config_test']['delete'])); + + // Install the schema test module. + $this->enableModules(array('config_schema_test')); + $this->installConfig(array('config_schema_test')); + + // After module installation the new schema should exist. + $this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'), 'Configuration schema for config_schema_test.someschema exists.'); + + // Test that uninstalling configuration removes configuration schema. + $this->config('core.extension')->set('module', array())->save(); + \Drupal::service('config.manager')->uninstall('module', 'config_test'); + $this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema'), 'Configuration schema for config_schema_test.someschema does not exist.'); + } + + /** + * Tests that collections are ignored if the event does not return anything. + */ + public function testCollectionInstallationNoCollections() { + // Install the test module. + $this->enableModules(array('config_collection_install_test')); + $this->installConfig(array('config_collection_install_test')); + /** @var \Drupal\Core\Config\StorageInterface $active_storage */ + $active_storage = \Drupal::service('config.storage'); + $this->assertEqual(array(), $active_storage->getAllCollectionNames()); + } + + /** + * Tests config objects in collections are installed as expected. + */ + public function testCollectionInstallationCollections() { + $collections = array( + 'another_collection', + 'collection.test1', + 'collection.test2', + ); + // Set the event listener to return three possible collections. + // @see \Drupal\config_collection_install_test\EventSubscriber + \Drupal::state()->set('config_collection_install_test.collection_names', $collections); + // Install the test module. + $this->enableModules(array('config_collection_install_test')); + $this->installConfig(array('config_collection_install_test')); + /** @var \Drupal\Core\Config\StorageInterface $active_storage */ + $active_storage = \Drupal::service('config.storage'); + $this->assertEqual($collections, $active_storage->getAllCollectionNames()); + foreach ($collections as $collection) { + $collection_storage = $active_storage->createCollection($collection); + $data = $collection_storage->read('config_collection_install_test.test'); + $this->assertEqual($collection, $data['collection']); + } + + // Tests that clashing configuration in collections is detected. + try { + \Drupal::service('module_installer')->install(['config_collection_clash_install_test']); + $this->fail('Expected PreExistingConfigException not thrown.'); + } + catch (PreExistingConfigException $e) { + $this->assertEqual($e->getExtension(), 'config_collection_clash_install_test'); + $this->assertEqual($e->getConfigObjects(), [ + 'another_collection' => ['config_collection_install_test.test'], + 'collection.test1' => ['config_collection_install_test.test'], + 'collection.test2' => ['config_collection_install_test.test'], + ]); + $this->assertEqual($e->getMessage(), 'Configuration objects (another_collection/config_collection_install_test.test, collection/test1/config_collection_install_test.test, collection/test2/config_collection_install_test.test) provided by config_collection_clash_install_test already exist in active configuration'); + } + + // Test that the we can use the config installer to install all the + // available default configuration in a particular collection for enabled + // extensions. + \Drupal::service('config.installer')->installCollectionDefaultConfig('entity'); + // The 'entity' collection will not exist because the 'config_test' module + // is not enabled. + $this->assertEqual($collections, $active_storage->getAllCollectionNames()); + // Enable the 'config_test' module and try again. + $this->enableModules(array('config_test')); + \Drupal::service('config.installer')->installCollectionDefaultConfig('entity'); + $collections[] = 'entity'; + $this->assertEqual($collections, $active_storage->getAllCollectionNames()); + $collection_storage = $active_storage->createCollection('entity'); + $data = $collection_storage->read('config_test.dynamic.dotted.default'); + $this->assertIdentical(array('label' => 'entity'), $data); + + // Test that the config manager uninstalls configuration from collections + // as expected. + \Drupal::service('config.manager')->uninstall('module', 'config_collection_install_test'); + $this->assertEqual(array('entity'), $active_storage->getAllCollectionNames()); + \Drupal::service('config.manager')->uninstall('module', 'config_test'); + $this->assertEqual(array(), $active_storage->getAllCollectionNames()); + } + + /** + * Tests collections which do not support config entities install correctly. + * + * Config entity detection during config installation is done by matching + * config name prefixes. If a collection provides a configuration with a + * matching name but does not support config entities it should be created + * using simple configuration. + */ + public function testCollectionInstallationCollectionConfigEntity() { + $collections = array( + 'entity', + ); + \Drupal::state()->set('config_collection_install_test.collection_names', $collections); + // Install the test module. + $this->installModules(array('config_test', 'config_collection_install_test')); + /** @var \Drupal\Core\Config\StorageInterface $active_storage */ + $active_storage = \Drupal::service('config.storage'); + $this->assertEqual($collections, $active_storage->getAllCollectionNames()); + $collection_storage = $active_storage->createCollection('entity'); + + // The config_test.dynamic.dotted.default configuration object saved in the + // active store should be a configuration entity complete with UUID. Because + // the entity collection does not support configuration entities the + // configuration object stored there with the same name should only contain + // a label. + $name = 'config_test.dynamic.dotted.default'; + $data = $active_storage->read($name); + $this->assertTrue(isset($data['uuid'])); + $data = $collection_storage->read($name); + $this->assertIdentical(array('label' => 'entity'), $data); + } + + /** + * Tests the configuration with unmet dependencies is not installed. + */ + public function testDependencyChecking() { + $this->installModules(['config_test']); + try { + $this->installModules(['config_install_dependency_test']); + $this->fail('Expected UnmetDependenciesException not thrown.'); + } + catch (UnmetDependenciesException $e) { + $this->assertEqual($e->getExtension(), 'config_install_dependency_test'); + $this->assertEqual($e->getConfigObjects(), ['config_test.dynamic.other_module_test_with_dependency']); + $this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.other_module_test_with_dependency) provided by config_install_dependency_test have unmet dependencies'); + } + $this->installModules(['config_other_module_config_test']); + $this->installModules(['config_install_dependency_test']); + $entity = \Drupal::entityManager()->getStorage('config_test')->load('other_module_test_with_dependency'); + $this->assertTrue($entity, 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.'); + // Ensure that dependencies can be added during module installation by + // hooks. + $this->assertIdentical('config_install_dependency_test', $entity->getDependencies()['module'][0]); + } + + /** + * Tests imported configuration entities with and without language information. + */ + function testLanguage() { + $this->installModules(['config_test_language']); + // Test imported configuration with implicit language code. + $storage = new InstallStorage(); + $data = $storage->read('config_test.dynamic.dotted.english'); + $this->assertTrue(!isset($data['langcode'])); + $this->assertEqual( + $this->config('config_test.dynamic.dotted.english')->get('langcode'), + 'en' + ); + + // Test imported configuration with explicit language code. + $data = $storage->read('config_test.dynamic.dotted.french'); + $this->assertEqual($data['langcode'], 'fr'); + $this->assertEqual( + $this->config('config_test.dynamic.dotted.french')->get('langcode'), + 'fr' + ); + } + + /** + * Installs a module. + * + * @param array $modules + * The module names. + */ + protected function installModules(array $modules) { + $this->container->get('module_installer')->install($modules); + $this->container = \Drupal::getContainer(); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigLanguageOverrideTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigLanguageOverrideTest.php new file mode 100644 index 0000000..5dcb54a --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigLanguageOverrideTest.php @@ -0,0 +1,125 @@ +installConfig(array('config_test')); + } + + /** + * Tests locale override based on language. + */ + function testConfigLanguageOverride() { + // The language module implements a config factory override object that + // overrides configuration when the Language module is enabled. This test ensures that + // English overrides work. + \Drupal::languageManager()->setConfigOverrideLanguage(\Drupal::languageManager()->getLanguage('en')); + $config = \Drupal::config('config_test.system'); + $this->assertIdentical($config->get('foo'), 'en bar'); + + // Ensure that the raw data is not translated. + $raw = $config->getRawData(); + $this->assertIdentical($raw['foo'], 'bar'); + + ConfigurableLanguage::createFromLangcode('fr')->save(); + ConfigurableLanguage::createFromLangcode('de')->save(); + + \Drupal::languageManager()->setConfigOverrideLanguage(\Drupal::languageManager()->getLanguage('fr')); + $config = \Drupal::config('config_test.system'); + $this->assertIdentical($config->get('foo'), 'fr bar'); + + \Drupal::languageManager()->setConfigOverrideLanguage(\Drupal::languageManager()->getLanguage('de')); + $config = \Drupal::config('config_test.system'); + $this->assertIdentical($config->get('foo'), 'de bar'); + + // Test overrides of completely new configuration objects. In normal runtime + // this should only happen for configuration entities as we should not be + // creating simple configuration objects on the fly. + \Drupal::languageManager() + ->getLanguageConfigOverride('de', 'config_test.new') + ->set('language', 'override') + ->save(); + $config = \Drupal::config('config_test.new'); + $this->assertTrue($config->isNew(), 'The configuration object config_test.new is new'); + $this->assertIdentical($config->get('language'), 'override'); + $this->assertIdentical($config->getOriginal('language', FALSE), NULL); + + // Test how overrides react to base configuration changes. Set up some base + // values. + \Drupal::configFactory()->getEditable('config_test.foo') + ->set('value', array('key' => 'original')) + ->set('label', 'Original') + ->save(); + \Drupal::languageManager() + ->getLanguageConfigOverride('de', 'config_test.foo') + ->set('value', array('key' => 'override')) + ->set('label', 'Override') + ->save(); + \Drupal::languageManager() + ->getLanguageConfigOverride('fr', 'config_test.foo') + ->set('value', array('key' => 'override')) + ->save(); + \Drupal::configFactory()->clearStaticCache(); + $config = \Drupal::config('config_test.foo'); + $this->assertIdentical($config->get('value'), array('key' => 'override')); + + // Ensure renaming the config will rename the override. + \Drupal::languageManager()->setConfigOverrideLanguage(\Drupal::languageManager()->getLanguage('en')); + \Drupal::configFactory()->rename('config_test.foo', 'config_test.bar'); + $config = \Drupal::config('config_test.bar'); + $this->assertEqual($config->get('value'), array('key' => 'original')); + $override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'config_test.foo'); + $this->assertTrue($override->isNew()); + $this->assertEqual($override->get('value'), NULL); + $override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'config_test.bar'); + $this->assertFalse($override->isNew()); + $this->assertEqual($override->get('value'), array('key' => 'override')); + $override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'config_test.bar'); + $this->assertFalse($override->isNew()); + $this->assertEqual($override->get('value'), array('key' => 'override')); + + // Ensure changing data in the config will update the overrides. + $config = \Drupal::configFactory()->getEditable('config_test.bar')->clear('value.key')->save(); + $this->assertEqual($config->get('value'), array()); + $override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'config_test.bar'); + $this->assertFalse($override->isNew()); + $this->assertEqual($override->get('value'), NULL); + // The French override will become empty and therefore removed. + $override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'config_test.bar'); + $this->assertTrue($override->isNew()); + $this->assertEqual($override->get('value'), NULL); + + // Ensure deleting the config will delete the override. + \Drupal::configFactory()->getEditable('config_test.bar')->delete(); + $override = \Drupal::languageManager()->getLanguageConfigOverride('de', 'config_test.bar'); + $this->assertTrue($override->isNew()); + $this->assertEqual($override->get('value'), NULL); + } +} + diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigModuleOverridesTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigModuleOverridesTest.php new file mode 100644 index 0000000..443c428 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigModuleOverridesTest.php @@ -0,0 +1,55 @@ +container->get('config.factory'); + $config_factory + ->getEditable($name) + ->set('name', $non_overridden_name) + ->set('slogan', $non_overridden_slogan) + ->save(); + + $this->assertEqual($non_overridden_name, $config_factory->get('system.site')->getOriginal('name', FALSE)); + $this->assertEqual($non_overridden_slogan, $config_factory->get('system.site')->getOriginal('slogan', FALSE)); + $this->assertEqual($overridden_name, $config_factory->get('system.site')->get('name')); + $this->assertEqual($overridden_slogan, $config_factory->get('system.site')->get('slogan')); + + // Test overrides of completely new configuration objects. In normal runtime + // this should only happen for configuration entities as we should not be + // creating simple configuration objects on the fly. + $config = $config_factory->get('config_override_test.new'); + $this->assertTrue($config->isNew(), 'The configuration object config_override_test.new is new'); + $this->assertIdentical($config->get('module'), 'override'); + $this->assertIdentical($config->getOriginal('module', FALSE), NULL); + + unset($GLOBALS['config_test_run_module_overrides']); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigOverrideTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigOverrideTest.php new file mode 100644 index 0000000..a268be1 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigOverrideTest.php @@ -0,0 +1,143 @@ +copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync')); + } + + /** + * Tests configuration override. + */ + function testConfOverride() { + $expected_original_data = array( + 'foo' => 'bar', + 'baz' => NULL, + '404' => 'herp', + ); + + // Set globals before installing to prove that the installed file does not + // contain these values. + $overrides['config_test.system']['foo'] = 'overridden'; + $overrides['config_test.system']['baz'] = 'injected'; + $overrides['config_test.system']['404'] = 'derp'; + $GLOBALS['config'] = $overrides; + + $this->installConfig(array('config_test')); + + // Verify that the original configuration data exists. Have to read storage + // directly otherwise overrides will apply. + $active = $this->container->get('config.storage'); + $data = $active->read('config_test.system'); + $this->assertIdentical($data['foo'], $expected_original_data['foo']); + $this->assertFalse(isset($data['baz'])); + $this->assertIdentical($data['404'], $expected_original_data['404']); + + // Get the configuration object with overrides. + $config = \Drupal::configFactory()->get('config_test.system'); + + // Verify that it contains the overridden data from $config. + $this->assertIdentical($config->get('foo'), $overrides['config_test.system']['foo']); + $this->assertIdentical($config->get('baz'), $overrides['config_test.system']['baz']); + $this->assertIdentical($config->get('404'), $overrides['config_test.system']['404']); + + // Get the configuration object which does not have overrides. + $config = \Drupal::configFactory()->getEditable('config_test.system'); + + // Verify that it does not contains the overridden data from $config. + $this->assertIdentical($config->get('foo'), $expected_original_data['foo']); + $this->assertIdentical($config->get('baz'), NULL); + $this->assertIdentical($config->get('404'), $expected_original_data['404']); + + // Set the value for 'baz' (on the original data). + $expected_original_data['baz'] = 'original baz'; + $config->set('baz', $expected_original_data['baz']); + + // Set the value for '404' (on the original data). + $expected_original_data['404'] = 'original 404'; + $config->set('404', $expected_original_data['404']); + + // Save the configuration object (having overrides applied). + $config->save(); + + // Reload it and verify that it still contains overridden data from $config. + $config = \Drupal::config('config_test.system'); + $this->assertIdentical($config->get('foo'), $overrides['config_test.system']['foo']); + $this->assertIdentical($config->get('baz'), $overrides['config_test.system']['baz']); + $this->assertIdentical($config->get('404'), $overrides['config_test.system']['404']); + + // Verify that raw config data has changed. + $this->assertIdentical($config->getOriginal('foo', FALSE), $expected_original_data['foo']); + $this->assertIdentical($config->getOriginal('baz', FALSE), $expected_original_data['baz']); + $this->assertIdentical($config->getOriginal('404', FALSE), $expected_original_data['404']); + + // Write file to sync. + $sync = $this->container->get('config.storage.sync'); + $expected_new_data = array( + 'foo' => 'barbar', + '404' => 'herpderp', + ); + $sync->write('config_test.system', $expected_new_data); + + // Import changed data from sync to active. + $this->configImporter()->import(); + $data = $active->read('config_test.system'); + + // Verify that the new configuration data exists. Have to read storage + // directly otherwise overrides will apply. + $this->assertIdentical($data['foo'], $expected_new_data['foo']); + $this->assertFalse(isset($data['baz'])); + $this->assertIdentical($data['404'], $expected_new_data['404']); + + // Verify that the overrides are still working. + $config = \Drupal::config('config_test.system'); + $this->assertIdentical($config->get('foo'), $overrides['config_test.system']['foo']); + $this->assertIdentical($config->get('baz'), $overrides['config_test.system']['baz']); + $this->assertIdentical($config->get('404'), $overrides['config_test.system']['404']); + + // Test overrides of completely new configuration objects. In normal runtime + // this should only happen for configuration entities as we should not be + // creating simple configuration objects on the fly. + $GLOBALS['config']['config_test.new']['key'] = 'override'; + $config = \Drupal::config('config_test.new'); + $this->assertTrue($config->isNew(), 'The configuration object config_test.new is new'); + $this->assertIdentical($config->get('key'), 'override'); + $config_raw = \Drupal::configFactory()->getEditable('config_test.new'); + $this->assertIdentical($config_raw->get('key'), NULL); + $config_raw + ->set('key', 'raw') + ->set('new_key', 'new_value') + ->save(); + // Ensure override is preserved but all other data has been updated + // accordingly. + $config = \Drupal::config('config_test.new'); + $this->assertFalse($config->isNew(), 'The configuration object config_test.new is not new'); + $this->assertIdentical($config->get('key'), 'override'); + $this->assertIdentical($config->get('new_key'), 'new_value'); + $raw_data = $config->getRawData(); + $this->assertIdentical($raw_data['key'], 'raw'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigOverridesPriorityTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigOverridesPriorityTest.php new file mode 100644 index 0000000..3f64608 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigOverridesPriorityTest.php @@ -0,0 +1,100 @@ +container->get('config.factory'); + $config_factory + ->getEditable('system.site') + ->set('name', $non_overridden_name) + ->set('slogan', $non_overridden_slogan) + ->set('mail', $non_overridden_mail) + ->set('weight_select_max', 50) + ->save(); + + // Ensure that no overrides are applying. + $this->assertEqual($non_overridden_name, $config_factory->get('system.site')->get('name')); + $this->assertEqual($non_overridden_slogan, $config_factory->get('system.site')->get('slogan')); + $this->assertEqual($non_overridden_mail, $config_factory->get('system.site')->get('mail')); + $this->assertEqual(50, $config_factory->get('system.site')->get('weight_select_max')); + + // Override using language. + $language = new Language(array( + 'name' => 'French', + 'id' => 'fr', + )); + \Drupal::languageManager()->setConfigOverrideLanguage($language); + \Drupal::languageManager() + ->getLanguageConfigOverride($language->getId(), 'system.site') + ->set('name', $language_overridden_name) + ->set('mail', $language_overridden_mail) + ->save(); + + $this->assertEqual($language_overridden_name, $config_factory->get('system.site')->get('name')); + $this->assertEqual($non_overridden_slogan, $config_factory->get('system.site')->get('slogan')); + $this->assertEqual($language_overridden_mail, $config_factory->get('system.site')->get('mail')); + $this->assertEqual(50, $config_factory->get('system.site')->get('weight_select_max')); + + // Enable module overrides. Do not override system.site:mail to prove that + // the language override still applies. + $GLOBALS['config_test_run_module_overrides'] = TRUE; + $config_factory->reset('system.site'); + $this->assertEqual($module_overridden_name, $config_factory->get('system.site')->get('name')); + $this->assertEqual($module_overridden_slogan, $config_factory->get('system.site')->get('slogan')); + $this->assertEqual($language_overridden_mail, $config_factory->get('system.site')->get('mail')); + $this->assertEqual(50, $config_factory->get('system.site')->get('weight_select_max')); + + // Configure a global override to simulate overriding using settings.php. Do + // not override system.site:mail or system.site:slogan to prove that the + // language and module overrides still apply. + $GLOBALS['config']['system.site']['name'] = 'Site name global conf override'; + $config_factory->reset('system.site'); + $this->assertEqual('Site name global conf override', $config_factory->get('system.site')->get('name')); + $this->assertEqual($module_overridden_slogan, $config_factory->get('system.site')->get('slogan')); + $this->assertEqual($language_overridden_mail, $config_factory->get('system.site')->get('mail')); + $this->assertEqual(50, $config_factory->get('system.site')->get('weight_select_max')); + + $this->assertEqual($non_overridden_name, $config_factory->get('system.site')->getOriginal('name', FALSE)); + $this->assertEqual($non_overridden_slogan, $config_factory->get('system.site')->getOriginal('slogan', FALSE)); + $this->assertEqual($non_overridden_mail, $config_factory->get('system.site')->getOriginal('mail', FALSE)); + $this->assertEqual(50, $config_factory->get('system.site')->getOriginal('weight_select_max', FALSE)); + + unset($GLOBALS['config_test_run_module_overrides']); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php new file mode 100644 index 0000000..e962bc7 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigSchemaTest.php @@ -0,0 +1,641 @@ +installConfig(array('system', 'image', 'config_schema_test')); + } + + /** + * Tests the basic metadata retrieval layer. + */ + function testSchemaMapping() { + // Nonexistent configuration key will have Undefined as metadata. + $this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.no_such_key')); + $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.no_such_key'); + $expected = array(); + $expected['label'] = 'Undefined'; + $expected['class'] = '\Drupal\Core\Config\Schema\Undefined'; + $expected['type'] = 'undefined'; + $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition'; + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for nonexistent configuration.'); + + // Configuration file without schema will return Undefined as well. + $this->assertIdentical(FALSE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.noschema')); + $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.noschema'); + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with no schema.'); + + // Configuration file with only some schema. + $this->assertIdentical(TRUE, \Drupal::service('config.typed')->hasConfigSchema('config_schema_test.someschema')); + $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema'); + $expected = array(); + $expected['label'] = 'Schema test data'; + $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['mapping']['langcode']['type'] = 'string'; + $expected['mapping']['langcode']['label'] = 'Language code'; + $expected['mapping']['_core']['type'] = '_core_config_info'; + $expected['mapping']['testitem'] = array('label' => 'Test item'); + $expected['mapping']['testlist'] = array('label' => 'Test list'); + $expected['type'] = 'config_schema_test.someschema'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for configuration with only some schema.'); + + // Check type detection on elements with undefined types. + $config = \Drupal::service('config.typed')->get('config_schema_test.someschema'); + $definition = $config->get('testitem')->getDataDefinition()->toArray(); + $expected = array(); + $expected['label'] = 'Test item'; + $expected['class'] = '\Drupal\Core\Config\Schema\Undefined'; + $expected['type'] = 'undefined'; + $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition'; + $this->assertEqual($definition, $expected, 'Automatic type detected for a scalar is undefined.'); + $definition = $config->get('testlist')->getDataDefinition()->toArray(); + $expected = array(); + $expected['label'] = 'Test list'; + $expected['class'] = '\Drupal\Core\Config\Schema\Undefined'; + $expected['type'] = 'undefined'; + $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition'; + $this->assertEqual($definition, $expected, 'Automatic type detected for a list is undefined.'); + $definition = $config->get('testnoschema')->getDataDefinition()->toArray(); + $expected = array(); + $expected['label'] = 'Undefined'; + $expected['class'] = '\Drupal\Core\Config\Schema\Undefined'; + $expected['type'] = 'undefined'; + $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition'; + $this->assertEqual($definition, $expected, 'Automatic type detected for an undefined integer is undefined.'); + + // Simple case, straight metadata. + $definition = \Drupal::service('config.typed')->getDefinition('system.maintenance'); + $expected = array(); + $expected['label'] = 'Maintenance mode'; + $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['mapping']['message'] = array( + 'label' => 'Message to display when in maintenance mode', + 'type' => 'text', + ); + $expected['mapping']['langcode'] = array( + 'label' => 'Language code', + 'type' => 'string', + ); + $expected['mapping']['_core']['type'] = '_core_config_info'; + $expected['type'] = 'system.maintenance'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for system.maintenance'); + + // Mixed schema with ignore elements. + $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.ignore'); + $expected = array(); + $expected['label'] = 'Ignore test'; + $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; + $expected['mapping']['langcode'] = array( + 'type' => 'string', + 'label' => 'Language code', + ); + $expected['mapping']['_core']['type'] = '_core_config_info'; + $expected['mapping']['label'] = array( + 'label' => 'Label', + 'type' => 'label', + ); + $expected['mapping']['irrelevant'] = array( + 'label' => 'Irrelevant', + 'type' => 'ignore', + ); + $expected['mapping']['indescribable'] = array( + 'label' => 'Indescribable', + 'type' => 'ignore', + ); + $expected['mapping']['weight'] = array( + 'label' => 'Weight', + 'type' => 'integer', + ); + $expected['type'] = 'config_schema_test.ignore'; + + $this->assertEqual($definition, $expected); + + // The ignore elements themselves. + $definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('irrelevant')->getDataDefinition()->toArray(); + $expected = array(); + $expected['type'] = 'ignore'; + $expected['label'] = 'Irrelevant'; + $expected['class'] = '\Drupal\Core\Config\Schema\Ignore'; + $expected['definition_class'] = '\Drupal\Core\TypedData\DataDefinition'; + $this->assertEqual($definition, $expected); + $definition = \Drupal::service('config.typed')->get('config_schema_test.ignore')->get('indescribable')->getDataDefinition()->toArray(); + $expected['label'] = 'Indescribable'; + $this->assertEqual($definition, $expected); + + // More complex case, generic type. Metadata for image style. + $definition = \Drupal::service('config.typed')->getDefinition('image.style.large'); + $expected = array(); + $expected['label'] = 'Image style'; + $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; + $expected['mapping']['name']['type'] = 'string'; + $expected['mapping']['uuid']['type'] = 'string'; + $expected['mapping']['uuid']['label'] = 'UUID'; + $expected['mapping']['langcode']['type'] = 'string'; + $expected['mapping']['langcode']['label'] = 'Language code'; + $expected['mapping']['status']['type'] = 'boolean'; + $expected['mapping']['status']['label'] = 'Status'; + $expected['mapping']['dependencies']['type'] = 'config_dependencies'; + $expected['mapping']['dependencies']['label'] = 'Dependencies'; + $expected['mapping']['name']['type'] = 'string'; + $expected['mapping']['label']['type'] = 'label'; + $expected['mapping']['label']['label'] = 'Label'; + $expected['mapping']['effects']['type'] = 'sequence'; + $expected['mapping']['effects']['sequence']['type'] = 'mapping'; + $expected['mapping']['effects']['sequence']['mapping']['id']['type'] = 'string'; + $expected['mapping']['effects']['sequence']['mapping']['data']['type'] = 'image.effect.[%parent.id]'; + $expected['mapping']['effects']['sequence']['mapping']['weight']['type'] = 'integer'; + $expected['mapping']['effects']['sequence']['mapping']['uuid']['type'] = 'string'; + $expected['mapping']['third_party_settings']['type'] = 'sequence'; + $expected['mapping']['third_party_settings']['label'] = 'Third party settings'; + $expected['mapping']['third_party_settings']['sequence']['type'] = '[%parent.%parent.%type].third_party.[%key]'; + $expected['mapping']['_core']['type'] = '_core_config_info'; + $expected['type'] = 'image.style.*'; + + $this->assertEqual($definition, $expected); + + // More complex, type based on a complex one. + $definition = \Drupal::service('config.typed')->getDefinition('image.effect.image_scale'); + // This should be the schema for image.effect.image_scale. + $expected = array(); + $expected['label'] = 'Image scale'; + $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; + $expected['mapping']['width']['type'] = 'integer'; + $expected['mapping']['width']['label'] = 'Width'; + $expected['mapping']['height']['type'] = 'integer'; + $expected['mapping']['height']['label'] = 'Height'; + $expected['mapping']['upscale']['type'] = 'boolean'; + $expected['mapping']['upscale']['label'] = 'Upscale'; + $expected['type'] = 'image.effect.image_scale'; + + + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for image.effect.image_scale'); + + // Most complex case, get metadata for actual configuration element. + $effects = \Drupal::service('config.typed')->get('image.style.medium')->get('effects'); + $definition = $effects->get('bddf0d06-42f9-4c75-a700-a33cafa25ea0')->get('data')->getDataDefinition()->toArray(); + // This should be the schema for image.effect.image_scale, reuse previous one. + $expected['type'] = 'image.effect.image_scale'; + + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for the first effect of image.style.medium'); + + $a = \Drupal::config('config_test.dynamic.third_party'); + $test = \Drupal::service('config.typed')->get('config_test.dynamic.third_party')->get('third_party_settings.config_schema_test'); + $definition = $test->getDataDefinition()->toArray(); + $expected = array(); + $expected['type'] = 'config_test.dynamic.*.third_party.config_schema_test'; + $expected['label'] = 'Mapping'; + $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; + $expected['mapping'] = [ + 'integer' => ['type' => 'integer'], + 'string' => ['type' => 'string'], + ]; + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_test.dynamic.third_party:third_party_settings.config_schema_test'); + + // More complex, several level deep test. + $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema.somemodule.section_one.subsection'); + // This should be the schema of config_schema_test.someschema.somemodule.*.*. + $expected = array(); + $expected['label'] = 'Schema multiple filesystem marker test'; + $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['mapping']['langcode']['type'] = 'string'; + $expected['mapping']['langcode']['label'] = 'Language code'; + $expected['mapping']['_core']['type'] = '_core_config_info'; + $expected['mapping']['testid']['type'] = 'string'; + $expected['mapping']['testid']['label'] = 'ID'; + $expected['mapping']['testdescription']['type'] = 'text'; + $expected['mapping']['testdescription']['label'] = 'Description'; + $expected['type'] = 'config_schema_test.someschema.somemodule.*.*'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; + + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_one.subsection'); + + $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.someschema.somemodule.section_two.subsection'); + // The other file should have the same schema. + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.someschema.somemodule.section_two.subsection'); + } + + /** + * Tests metadata retrieval with several levels of %parent indirection. + */ + function testSchemaMappingWithParents() { + $config_data = \Drupal::service('config.typed')->get('config_schema_test.someschema.with_parents'); + + // Test fetching parent one level up. + $entry = $config_data->get('one_level'); + $definition = $entry->get('testitem')->getDataDefinition()->toArray(); + $expected = array( + 'type' => 'config_schema_test.someschema.with_parents.key_1', + 'label' => 'Test item nested one level', + 'class' => '\Drupal\Core\TypedData\Plugin\DataType\StringData', + 'definition_class' => '\Drupal\Core\TypedData\DataDefinition', + ); + $this->assertEqual($definition, $expected); + + // Test fetching parent two levels up. + $entry = $config_data->get('two_levels'); + $definition = $entry->get('wrapper')->get('testitem')->getDataDefinition()->toArray(); + $expected = array( + 'type' => 'config_schema_test.someschema.with_parents.key_2', + 'label' => 'Test item nested two levels', + 'class' => '\Drupal\Core\TypedData\Plugin\DataType\StringData', + 'definition_class' => '\Drupal\Core\TypedData\DataDefinition', + ); + $this->assertEqual($definition, $expected); + + // Test fetching parent three levels up. + $entry = $config_data->get('three_levels'); + $definition = $entry->get('wrapper_1')->get('wrapper_2')->get('testitem')->getDataDefinition()->toArray(); + $expected = array( + 'type' => 'config_schema_test.someschema.with_parents.key_3', + 'label' => 'Test item nested three levels', + 'class' => '\Drupal\Core\TypedData\Plugin\DataType\StringData', + 'definition_class' => '\Drupal\Core\TypedData\DataDefinition', + ); + $this->assertEqual($definition, $expected); + } + + /** + * Tests metadata applied to configuration objects. + */ + function testSchemaData() { + // Try a simple property. + $meta = \Drupal::service('config.typed')->get('system.site'); + $property = $meta->get('page')->get('front'); + $this->assertTrue($property instanceof StringInterface, 'Got the right wrapper fo the page.front property.'); + $this->assertEqual($property->getValue(), '/user/login', 'Got the right value for page.front data.'); + $definition = $property->getDataDefinition(); + $this->assertTrue(empty($definition['translatable']), 'Got the right translatability setting for page.front data.'); + + // Check nested array of properties. + $list = $meta->get('page')->getElements(); + $this->assertEqual(count($list), 3, 'Got a list with the right number of properties for site page data'); + $this->assertTrue(isset($list['front']) && isset($list['403']) && isset($list['404']), 'Got a list with the right properties for site page data.'); + $this->assertEqual($list['front']->getValue(), '/user/login', 'Got the right value for page.front data from the list.'); + + // And test some TypedConfigInterface methods. + $properties = $list; + $this->assertTrue(count($properties) == 3 && $properties['front'] == $list['front'], 'Got the right properties for site page.'); + $values = $meta->get('page')->toArray(); + $this->assertTrue(count($values) == 3 && $values['front'] == '/user/login', 'Got the right property values for site page.'); + + // Now let's try something more complex, with nested objects. + $wrapper = \Drupal::service('config.typed')->get('image.style.large'); + $effects = $wrapper->get('effects'); + $this->assertTrue(count($effects->toArray()) == 1, 'Got an array with effects for image.style.large data'); + $uuid = key($effects->getValue()); + $effect = $effects->get($uuid)->getElements(); + $this->assertTrue(!$effect['data']->isEmpty() && $effect['id']->getValue() == 'image_scale', 'Got data for the image scale effect from metadata.'); + $this->assertTrue($effect['data']->get('width') instanceof IntegerInterface, 'Got the right type for the scale effect width.'); + $this->assertEqual($effect['data']->get('width')->getValue(), 480, 'Got the right value for the scale effect width.' ); + } + + /** + * Test configuration value data type enforcement using schemas. + */ + public function testConfigSaveWithSchema() { + $untyped_values = array( + 'string' => 1, + 'empty_string' => '', + 'null_string' => NULL, + 'integer' => '100', + 'null_integer' => '', + 'boolean' => 1, + // If the config schema doesn't have a type it shouldn't be casted. + 'no_type' => 1, + 'mapping' => array( + 'string' => 1 + ), + 'float' => '3.14', + 'null_float' => '', + 'sequence' => array (1, 0, 1), + 'sequence_bc' => array(1, 0, 1), + // Not in schema and therefore should be left untouched. + 'not_present_in_schema' => TRUE, + // Test a custom type. + 'config_schema_test_integer' => '1', + 'config_schema_test_integer_empty_string' => '', + ); + $untyped_to_typed = $untyped_values; + + $typed_values = array( + 'string' => '1', + 'empty_string' => '', + 'null_string' => NULL, + 'integer' => 100, + 'null_integer' => NULL, + 'boolean' => TRUE, + 'no_type' => 1, + 'mapping' => array( + 'string' => '1' + ), + 'float' => 3.14, + 'null_float' => NULL, + 'sequence' => array (TRUE, FALSE, TRUE), + 'sequence_bc' => array(TRUE, FALSE, TRUE), + 'not_present_in_schema' => TRUE, + 'config_schema_test_integer' => 1, + 'config_schema_test_integer_empty_string' => NULL, + ); + + // Save config which has a schema that enforces types. + $this->config('config_schema_test.schema_data_types') + ->setData($untyped_to_typed) + ->save(); + $this->assertIdentical($this->config('config_schema_test.schema_data_types')->get(), $typed_values); + + // Save config which does not have a schema that enforces types. + $this->config('config_schema_test.no_schema_data_types') + ->setData($untyped_values) + ->save(); + $this->assertIdentical($this->config('config_schema_test.no_schema_data_types')->get(), $untyped_values); + + // Ensure that configuration objects with keys marked as ignored are not + // changed when saved. The 'config_schema_test.ignore' will have been saved + // during the installation of configuration in the setUp method. + $extension_path = __DIR__ . '/../../../../../modules/config/tests/config_schema_test/'; + $install_storage = new FileStorage($extension_path . InstallStorage::CONFIG_INSTALL_DIRECTORY); + $original_data = $install_storage->read('config_schema_test.ignore'); + $installed_data = $this->config('config_schema_test.ignore')->get(); + unset($installed_data['_core']); + $this->assertIdentical($installed_data, $original_data); + } + + /** + * Tests fallback to a greedy wildcard. + */ + function testSchemaFallback() { + $definition = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something'); + // This should be the schema of config_schema_test.wildcard_fallback.*. + $expected = array(); + $expected['label'] = 'Schema wildcard fallback test'; + $expected['class'] = '\Drupal\Core\Config\Schema\Mapping'; + $expected['definition_class'] = '\Drupal\Core\TypedData\MapDataDefinition'; + $expected['mapping']['langcode']['type'] = 'string'; + $expected['mapping']['langcode']['label'] = 'Language code'; + $expected['mapping']['_core']['type'] = '_core_config_info'; + $expected['mapping']['testid']['type'] = 'string'; + $expected['mapping']['testid']['label'] = 'ID'; + $expected['mapping']['testdescription']['type'] = 'text'; + $expected['mapping']['testdescription']['label'] = 'Description'; + $expected['type'] = 'config_schema_test.wildcard_fallback.*'; + + $this->assertEqual($definition, $expected, 'Retrieved the right metadata for config_schema_test.wildcard_fallback.something'); + + $definition2 = \Drupal::service('config.typed')->getDefinition('config_schema_test.wildcard_fallback.something.something'); + // This should be the schema of config_schema_test.wildcard_fallback.* as + //well. + $this->assertIdentical($definition, $definition2); + } + + /** + * Tests use of colons in schema type determination. + * + * @see \Drupal\Core\Config\TypedConfigManager::getFallbackName() + */ + function testColonsInSchemaTypeDetermination() { + $tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('tests')->getElements(); + $definition = $tests[0]->getDataDefinition()->toArray(); + $this->assertEqual($definition['type'], 'test.plugin_types.boolean'); + + $definition = $tests[1]->getDataDefinition()->toArray(); + $this->assertEqual($definition['type'], 'test.plugin_types.boolean:*'); + + $definition = $tests[2]->getDataDefinition()->toArray(); + $this->assertEqual($definition['type'], 'test.plugin_types.*'); + + $definition = $tests[3]->getDataDefinition()->toArray(); + $this->assertEqual($definition['type'], 'test.plugin_types.*'); + + $tests = \Drupal::service('config.typed')->get('config_schema_test.plugin_types')->get('test_with_parents')->getElements(); + $definition = $tests[0]->get('settings')->getDataDefinition()->toArray(); + $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean'); + + $definition = $tests[1]->get('settings')->getDataDefinition()->toArray(); + $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.boolean:*'); + + $definition = $tests[2]->get('settings')->getDataDefinition()->toArray(); + $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*'); + + $definition = $tests[3]->get('settings')->getDataDefinition()->toArray(); + $this->assertEqual($definition['type'], 'test_with_parents.plugin_types.*'); + } + + /** + * Tests hook_config_schema_info_alter(). + */ + public function testConfigSchemaInfoAlter() { + /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typed_config */ + $typed_config = \Drupal::service('config.typed'); + $typed_config->clearCachedDefinitions(); + + // Ensure that keys can not be added or removed by + // hook_config_schema_info_alter(). + \Drupal::state()->set('config_schema_test_exception_remove', TRUE); + $message = 'Expected ConfigSchemaAlterException thrown.'; + try { + $typed_config->getDefinitions(); + $this->fail($message); + } + catch (ConfigSchemaAlterException $e) { + $this->pass($message); + $this->assertEqual($e->getMessage(), 'Invoking hook_config_schema_info_alter() has removed (config_schema_test.hook) schema definitions'); + } + + \Drupal::state()->set('config_schema_test_exception_add', TRUE); + $message = 'Expected ConfigSchemaAlterException thrown.'; + try { + $typed_config->getDefinitions(); + $this->fail($message); + } + catch (ConfigSchemaAlterException $e) { + $this->pass($message); + $this->assertEqual($e->getMessage(), 'Invoking hook_config_schema_info_alter() has added (config_schema_test.hook_added_defintion) and removed (config_schema_test.hook) schema definitions'); + } + + \Drupal::state()->set('config_schema_test_exception_remove', FALSE); + $message = 'Expected ConfigSchemaAlterException thrown.'; + try { + $typed_config->getDefinitions(); + $this->fail($message); + } + catch (ConfigSchemaAlterException $e) { + $this->pass($message); + $this->assertEqual($e->getMessage(), 'Invoking hook_config_schema_info_alter() has added (config_schema_test.hook_added_defintion) schema definitions'); + } + + // Tests that hook_config_schema_info_alter() can add additional metadata to + // existing configuration schema. + \Drupal::state()->set('config_schema_test_exception_add', FALSE); + $definitions = $typed_config->getDefinitions(); + $this->assertEqual($definitions['config_schema_test.hook']['additional_metadata'], 'new schema info'); + } + + /** + * Tests saving config when the type is wrapped by a dynamic type. + */ + public function testConfigSaveWithWrappingSchema() { + $untyped_values = [ + 'tests' => [ + [ + 'wrapper_value' => 'foo', + 'plugin_id' => 'wrapper:foo', + 'internal_value' => 100, + ], + ], + ]; + + $typed_values = [ + 'tests' => [ + [ + 'wrapper_value' => 'foo', + 'plugin_id' => 'wrapper:foo', + 'internal_value' => '100', + ], + ], + ]; + + // Save config which has a schema that enforces types. + \Drupal::configFactory()->getEditable('wrapping.config_schema_test.plugin_types') + ->setData($untyped_values) + ->save(); + $this->assertIdentical(\Drupal::config('wrapping.config_schema_test.plugin_types') + ->get(), $typed_values); + } + + /** + * Tests dynamic config schema type with multiple sub-key references. + */ + public function testConfigSaveWithWrappingSchemaDoubleBrackets() { + $untyped_values = [ + 'tests' => [ + [ + 'wrapper_value' => 'foo', + 'foo' => 'turtle', + 'bar' => 'horse', + // Converted to a string by 'test.double_brackets.turtle.horse' + // schema. + 'another_key' => '100', + ], + ], + ]; + + $typed_values = [ + 'tests' => [ + [ + 'wrapper_value' => 'foo', + 'foo' => 'turtle', + 'bar' => 'horse', + 'another_key' => 100, + ], + ], + ]; + + // Save config which has a schema that enforces types. + \Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets') + ->setData($untyped_values) + ->save(); + $this->assertIdentical(\Drupal::config('wrapping.config_schema_test.double_brackets') + ->get(), $typed_values); + + $tests = \Drupal::service('config.typed')->get('wrapping.config_schema_test.double_brackets')->get('tests')->getElements(); + $definition = $tests[0]->getDataDefinition()->toArray(); + $this->assertEqual($definition['type'], 'wrapping.test.double_brackets.*||test.double_brackets.turtle.horse'); + + $untyped_values = [ + 'tests' => [ + [ + 'wrapper_value' => 'foo', + 'foo' => 'cat', + 'bar' => 'dog', + // Converted to a string by 'test.double_brackets.cat.dog' schema. + 'another_key' => 100, + ], + ], + ]; + + $typed_values = [ + 'tests' => [ + [ + 'wrapper_value' => 'foo', + 'foo' => 'cat', + 'bar' => 'dog', + 'another_key' => '100', + ], + ], + ]; + + // Save config which has a schema that enforces types. + \Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets') + ->setData($untyped_values) + ->save(); + $this->assertIdentical(\Drupal::config('wrapping.config_schema_test.double_brackets') + ->get(), $typed_values); + + $tests = \Drupal::service('config.typed')->get('wrapping.config_schema_test.double_brackets')->get('tests')->getElements(); + $definition = $tests[0]->getDataDefinition()->toArray(); + $this->assertEqual($definition['type'], 'wrapping.test.double_brackets.*||test.double_brackets.cat.dog'); + + // Combine everything in a single save. + $typed_values = [ + 'tests' => [ + [ + 'wrapper_value' => 'foo', + 'foo' => 'cat', + 'bar' => 'dog', + 'another_key' => 100, + ], + [ + 'wrapper_value' => 'foo', + 'foo' => 'turtle', + 'bar' => 'horse', + 'another_key' => '100', + ], + ], + ]; + \Drupal::configFactory()->getEditable('wrapping.config_schema_test.double_brackets') + ->setData($typed_values) + ->save(); + $tests = \Drupal::service('config.typed')->get('wrapping.config_schema_test.double_brackets')->get('tests')->getElements(); + $definition = $tests[0]->getDataDefinition()->toArray(); + $this->assertEqual($definition['type'], 'wrapping.test.double_brackets.*||test.double_brackets.cat.dog'); + $definition = $tests[1]->getDataDefinition()->toArray(); + $this->assertEqual($definition['type'], 'wrapping.test.double_brackets.*||test.double_brackets.turtle.horse'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/ConfigSnapshotTest.php b/core/tests/Drupal/KernelTests/Core/Config/ConfigSnapshotTest.php new file mode 100644 index 0000000..838cb61 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/ConfigSnapshotTest.php @@ -0,0 +1,91 @@ +createSnapshot(\Drupal::service('config.storage'), \Drupal::service('config.storage.snapshot')); + $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync')); + } + + /** + * Tests config snapshot creation and updating. + */ + function testSnapshot() { + $active = $this->container->get('config.storage'); + $sync = $this->container->get('config.storage.sync'); + $snapshot = $this->container->get('config.storage.snapshot'); + $config_manager = $this->container->get('config.manager'); + $config_name = 'config_test.system'; + $config_key = 'foo'; + $new_data = 'foobar'; + + $active_snapshot_comparer = new StorageComparer($active, $snapshot, $config_manager); + $sync_snapshot_comparer = new StorageComparer($sync, $snapshot, $config_manager); + + // Verify that we have an initial snapshot that matches the active + // configuration. This has to be true as no config should be installed. + $this->assertFalse($active_snapshot_comparer->createChangelist()->hasChanges()); + + // Install the default config. + $this->installConfig(array('config_test')); + // Although we have imported config this has not affected the snapshot. + $this->assertTrue($active_snapshot_comparer->reset()->hasChanges()); + + // Update the config snapshot. + \Drupal::service('config.manager')->createSnapshot($active, $snapshot); + + // The snapshot and active config should now contain the same config + // objects. + $this->assertFalse($active_snapshot_comparer->reset()->hasChanges()); + + // Change a configuration value in sync. + $sync_data = $this->config($config_name)->get(); + $sync_data[$config_key] = $new_data; + $sync->write($config_name, $sync_data); + + // Verify that active and snapshot match, and that sync doesn't match + // active. + $this->assertFalse($active_snapshot_comparer->reset()->hasChanges()); + $this->assertTrue($sync_snapshot_comparer->createChangelist()->hasChanges()); + + // Import changed data from sync to active. + $this->configImporter()->import(); + + // Verify changed config was properly imported. + \Drupal::configFactory()->reset($config_name); + $this->assertIdentical($this->config($config_name)->get($config_key), $new_data); + + // Verify that a new snapshot was created which and that it matches + // the active config. + $this->assertFalse($active_snapshot_comparer->reset()->hasChanges()); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/DefaultConfigTest.php b/core/tests/Drupal/KernelTests/Core/Config/DefaultConfigTest.php new file mode 100644 index 0000000..aa0111f --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/DefaultConfigTest.php @@ -0,0 +1,83 @@ +install($this->themes); + } + + /** + * {@inheritdoc} + */ + public function register(ContainerBuilder $container) { + parent::register($container); + $container->register('default_config_test.schema_storage') + ->setClass('\Drupal\config_test\TestInstallStorage') + ->addArgument(InstallStorage::CONFIG_SCHEMA_DIRECTORY); + + $definition = $container->getDefinition('config.typed'); + $definition->replaceArgument(1, new Reference('default_config_test.schema_storage')); + } + + /** + * Tests default configuration data type. + */ + public function testDefaultConfig() { + $typed_config = \Drupal::service('config.typed'); + // Create a configuration storage with access to default configuration in + // every module, profile and theme. + $default_config_storage = new TestInstallStorage(); + + foreach ($default_config_storage->listAll() as $config_name) { + // Skip files provided by the config_schema_test module since that module + // is explicitly for testing schema. + if (strpos($config_name, 'config_schema_test') === 0) { + continue; + } + + $data = $default_config_storage->read($config_name); + $this->assertConfigSchema($typed_config, $config_name, $data); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/SchemaCheckTraitTest.php b/core/tests/Drupal/KernelTests/Core/Config/SchemaCheckTraitTest.php new file mode 100644 index 0000000..510932f --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/SchemaCheckTraitTest.php @@ -0,0 +1,72 @@ +installConfig(array('config_test', 'config_schema_test')); + $this->typedConfig = \Drupal::service('config.typed'); + } + + /** + * Tests \Drupal\Core\Config\Schema\SchemaCheckTrait. + */ + public function testTrait() { + // Test a non existing schema. + $ret = $this->checkConfigSchema($this->typedConfig, 'config_schema_test.noschema', $this->config('config_schema_test.noschema')->get()); + $this->assertIdentical($ret, FALSE); + + // Test an existing schema with valid data. + $config_data = $this->config('config_test.types')->get(); + $ret = $this->checkConfigSchema($this->typedConfig, 'config_test.types', $config_data); + $this->assertIdentical($ret, TRUE); + + // Add a new key, a new array and overwrite boolean with array to test the + // error messages. + $config_data = array('new_key' => 'new_value', 'new_array' => array()) + $config_data; + $config_data['boolean'] = array(); + $ret = $this->checkConfigSchema($this->typedConfig, 'config_test.types', $config_data); + $expected = array( + 'config_test.types:new_key' => 'missing schema', + 'config_test.types:new_array' => 'missing schema', + 'config_test.types:boolean' => 'non-scalar value but not defined as an array (such as mapping or sequence)', + ); + $this->assertEqual($ret, $expected); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/SchemaConfigListenerTest.php b/core/tests/Drupal/KernelTests/Core/Config/SchemaConfigListenerTest.php new file mode 100644 index 0000000..c927fe8 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/SchemaConfigListenerTest.php @@ -0,0 +1,66 @@ +config('config_schema_test.schemaless')->set('foo', 'bar')->save(); + $this->fail($message); + } + catch (SchemaIncompleteException $e) { + $this->pass($message); + $this->assertEqual('No schema for config_schema_test.schemaless', $e->getMessage()); + } + + // Test a valid schema. + $message = 'Unexpected SchemaIncompleteException thrown'; + $config = $this->config('config_test.types')->set('int', 10); + try { + $config->save(); + $this->pass($message); + } + catch (SchemaIncompleteException $e) { + $this->fail($message); + } + + // Test an invalid schema. + $message = 'Expected SchemaIncompleteException thrown'; + $config = $this->config('config_test.types') + ->set('foo', 'bar') + ->set('array', 1); + try { + $config->save(); + $this->fail($message); + } + catch (SchemaIncompleteException $e) { + $this->pass($message); + $this->assertEqual('Schema errors for config_test.types with the following errors: config_test.types:foo missing schema, config_test.types:array variable type is integer but applied schema class is Drupal\Core\Config\Schema\Sequence', $e->getMessage()); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/Storage/CachedStorageTest.php b/core/tests/Drupal/KernelTests/Core/Config/Storage/CachedStorageTest.php new file mode 100644 index 0000000..546899a --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/Storage/CachedStorageTest.php @@ -0,0 +1,99 @@ +fileStorage = new FileStorage($dir); + $this->storage = new CachedStorage($this->fileStorage, \Drupal::service('cache.config')); + $this->cache = \Drupal::service('cache_factory')->get('config'); + // ::listAll() verifications require other configuration data to exist. + $this->storage->write('system.performance', array()); + } + + /** + * {@inheritdoc} + */ + public function testInvalidStorage() { + // No-op as this test does not make sense. + } + + /** + * {@inheritdoc} + */ + protected function read($name) { + $data = $this->cache->get($name); + // Cache misses fall through to the underlying storage. + return $data ? $data->data : $this->fileStorage->read($name); + } + + /** + * {@inheritdoc} + */ + protected function insert($name, $data) { + $this->fileStorage->write($name, $data); + $this->cache->set($name, $data); + } + + /** + * {@inheritdoc} + */ + protected function update($name, $data) { + $this->fileStorage->write($name, $data); + $this->cache->set($name, $data); + } + + /** + * {@inheritdoc} + */ + protected function delete($name) { + $this->cache->delete($name); + unlink($this->fileStorage->getFilePath($name)); + } + + /** + * {@inheritdoc} + */ + public function containerBuild(ContainerBuilder $container) { + parent::containerBuild($container); + // Use the regular database cache backend to aid testing. + $container->register('cache_factory', 'Drupal\Core\Cache\DatabaseBackendFactory') + ->addArgument(new Reference('database')) + ->addArgument(new Reference('cache_tags.invalidator.checksum')); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/Storage/ConfigStorageTestBase.php b/core/tests/Drupal/KernelTests/Core/Config/Storage/ConfigStorageTestBase.php new file mode 100644 index 0000000..5ddb987 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/Storage/ConfigStorageTestBase.php @@ -0,0 +1,271 @@ +assertIdentical($this->storage->exists($name), FALSE); + + // Reading a non-existing name returns FALSE. + $data = $this->storage->read($name); + $this->assertIdentical($data, FALSE); + + // Writing data returns TRUE and the data has been written. + $data = array('foo' => 'bar'); + $result = $this->storage->write($name, $data); + $this->assertIdentical($result, TRUE); + + $raw_data = $this->read($name); + $this->assertIdentical($raw_data, $data); + + // Checking whether an existing name exists returns TRUE. + $this->assertIdentical($this->storage->exists($name), TRUE); + + // Writing the identical data again still returns TRUE. + $result = $this->storage->write($name, $data); + $this->assertIdentical($result, TRUE); + + // Listing all names returns all. + $names = $this->storage->listAll(); + $this->assertTrue(in_array('system.performance', $names)); + $this->assertTrue(in_array($name, $names)); + + // Listing all names with prefix returns names with that prefix only. + $names = $this->storage->listAll('config_test.'); + $this->assertFalse(in_array('system.performance', $names)); + $this->assertTrue(in_array($name, $names)); + + // Rename the configuration storage object. + $new_name = 'config_test.storage_rename'; + $this->storage->rename($name, $new_name); + $raw_data = $this->read($new_name); + $this->assertIdentical($raw_data, $data); + // Rename it back so further tests work. + $this->storage->rename($new_name, $name); + + // Deleting an existing name returns TRUE. + $result = $this->storage->delete($name); + $this->assertIdentical($result, TRUE); + + // Deleting a non-existing name returns FALSE. + $result = $this->storage->delete($name); + $this->assertIdentical($result, FALSE); + + // Deleting all names with prefix deletes the appropriate data and returns + // TRUE. + $files = array( + 'config_test.test.biff', + 'config_test.test.bang', + 'config_test.test.pow', + ); + foreach ($files as $name) { + $this->storage->write($name, $data); + } + + $result = $this->storage->deleteAll('config_test.'); + $names = $this->storage->listAll('config_test.'); + $this->assertIdentical($result, TRUE); + $this->assertIdentical($names, array()); + + // Test renaming an object that does not exist throws an exception. + try { + $this->storage->rename('config_test.storage_does_not_exist', 'config_test.storage_does_not_exist_rename'); + } + catch (\Exception $e) { + $class = get_class($e); + $this->pass($class . ' thrown upon renaming a nonexistent storage bin.'); + } + + // Test renaming to an object that already exists throws an exception. + try { + $this->storage->rename('system.cron', 'system.performance'); + } + catch (\Exception $e) { + $class = get_class($e); + $this->pass($class . ' thrown upon renaming a nonexistent storage bin.'); + } + } + + /** + * Tests an invalid storage. + */ + public function testInvalidStorage() { + $name = 'config_test.storage'; + + // Write something to the valid storage to prove that the storages do not + // pollute one another. + $data = array('foo' => 'bar'); + $result = $this->storage->write($name, $data); + $this->assertIdentical($result, TRUE); + + $raw_data = $this->read($name); + $this->assertIdentical($raw_data, $data); + + // Reading from a non-existing storage bin returns FALSE. + $result = $this->invalidStorage->read($name); + $this->assertIdentical($result, FALSE); + + // Deleting from a non-existing storage bin throws an exception. + try { + $this->invalidStorage->delete($name); + $this->fail('Exception not thrown upon deleting from a non-existing storage bin.'); + } + catch (\Exception $e) { + $class = get_class($e); + $this->pass($class . ' thrown upon deleting from a non-existing storage bin.'); + } + + // Listing on a non-existing storage bin returns an empty array. + $result = $this->invalidStorage->listAll(); + $this->assertIdentical($result, array()); + // Writing to a non-existing storage bin creates the bin. + $this->invalidStorage->write($name, array('foo' => 'bar')); + $result = $this->invalidStorage->read($name); + $this->assertIdentical($result, array('foo' => 'bar')); + } + + /** + * Tests storage writing and reading data preserving data type. + */ + function testDataTypes() { + $name = 'config_test.types'; + $data = array( + 'array' => array(), + 'boolean' => TRUE, + 'exp' => 1.2e+34, + 'float' => 3.14159, + 'hex' => 0xC, + 'int' => 99, + 'octal' => 0775, + 'string' => 'string', + 'string_int' => '1', + ); + + $result = $this->storage->write($name, $data); + $this->assertIdentical($result, TRUE); + + $read_data = $this->storage->read($name); + $this->assertIdentical($read_data, $data); + } + + /** + * Tests that the storage supports collections. + */ + public function testCollection() { + $name = 'config_test.storage'; + $data = array('foo' => 'bar'); + $result = $this->storage->write($name, $data); + $this->assertIdentical($result, TRUE); + $this->assertIdentical($data, $this->storage->read($name)); + + // Create configuration in a new collection. + $new_storage = $this->storage->createCollection('collection.sub.new'); + $this->assertFalse($new_storage->exists($name)); + $this->assertEqual(array(), $new_storage->listAll()); + $new_storage->write($name, $data); + $this->assertIdentical($result, TRUE); + $this->assertIdentical($data, $new_storage->read($name)); + $this->assertEqual(array($name), $new_storage->listAll()); + $this->assertTrue($new_storage->exists($name)); + $new_data = array('foo' => 'baz'); + $new_storage->write($name, $new_data); + $this->assertIdentical($result, TRUE); + $this->assertIdentical($new_data, $new_storage->read($name)); + + // Create configuration in another collection. + $another_storage = $this->storage->createCollection('collection.sub.another'); + $this->assertFalse($another_storage->exists($name)); + $this->assertEqual(array(), $another_storage->listAll()); + $another_storage->write($name, $new_data); + $this->assertIdentical($result, TRUE); + $this->assertIdentical($new_data, $another_storage->read($name)); + $this->assertEqual(array($name), $another_storage->listAll()); + $this->assertTrue($another_storage->exists($name)); + + // Create configuration in yet another collection. + $alt_storage = $this->storage->createCollection('alternate'); + $alt_storage->write($name, $new_data); + $this->assertIdentical($result, TRUE); + $this->assertIdentical($new_data, $alt_storage->read($name)); + + // Switch back to the collection-less mode and check the data still exists + // add has not been touched. + $this->assertIdentical($data, $this->storage->read($name)); + + // Check that the getAllCollectionNames() method works. + $this->assertIdentical(array('alternate', 'collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames()); + + // Check that the collections are removed when they are empty. + $alt_storage->delete($name); + $this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames()); + + // Create configuration in collection called 'collection'. This ensures that + // FileStorage's collection storage works regardless of its use of + // subdirectories. + $parent_storage = $this->storage->createCollection('collection'); + $this->assertFalse($parent_storage->exists($name)); + $this->assertEqual(array(), $parent_storage->listAll()); + $parent_storage->write($name, $new_data); + $this->assertIdentical($result, TRUE); + $this->assertIdentical($new_data, $parent_storage->read($name)); + $this->assertEqual(array($name), $parent_storage->listAll()); + $this->assertTrue($parent_storage->exists($name)); + $this->assertIdentical(array('collection', 'collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames()); + $parent_storage->deleteAll(); + $this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames()); + + // Check that the having an empty collection-less storage does not break + // anything. Before deleting check that the previous delete did not affect + // data in another collection. + $this->assertIdentical($data, $this->storage->read($name)); + $this->storage->delete($name); + $this->assertIdentical(array('collection.sub.another', 'collection.sub.new'), $this->storage->getAllCollectionNames()); + } + + abstract protected function read($name); + + abstract protected function insert($name, $data); + + abstract protected function update($name, $data); + + abstract protected function delete($name); + +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/Storage/DatabaseStorageTest.php b/core/tests/Drupal/KernelTests/Core/Config/Storage/DatabaseStorageTest.php new file mode 100644 index 0000000..727ad72 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/Storage/DatabaseStorageTest.php @@ -0,0 +1,48 @@ +storage = new DatabaseStorage($this->container->get('database'), 'config'); + $this->invalidStorage = new DatabaseStorage($this->container->get('database'), 'invalid'); + + // ::listAll() verifications require other configuration data to exist. + $this->storage->write('system.performance', array()); + } + + protected function read($name) { + $data = db_query('SELECT data FROM {config} WHERE name = :name', array(':name' => $name))->fetchField(); + return unserialize($data); + } + + protected function insert($name, $data) { + db_insert('config')->fields(array('name' => $name, 'data' => $data))->execute(); + } + + protected function update($name, $data) { + db_update('config')->fields(array('data' => $data))->condition('name', $name)->execute(); + } + + protected function delete($name) { + db_delete('config')->condition('name', $name)->execute(); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Config/Storage/FileStorageTest.php b/core/tests/Drupal/KernelTests/Core/Config/Storage/FileStorageTest.php new file mode 100644 index 0000000..583e3a1 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Config/Storage/FileStorageTest.php @@ -0,0 +1,95 @@ +directory = PublicStream::basePath() . '/config'; + $this->storage = new FileStorage($this->directory); + $this->invalidStorage = new FileStorage($this->directory . '/nonexisting'); + + // FileStorage::listAll() requires other configuration data to exist. + $this->storage->write('system.performance', $this->config('system.performance')->get()); + $this->storage->write('core.extension', array('module' => array())); + } + + protected function read($name) { + $data = file_get_contents($this->storage->getFilePath($name)); + return Yaml::decode($data); + } + + protected function insert($name, $data) { + file_put_contents($this->storage->getFilePath($name), $data); + } + + protected function update($name, $data) { + file_put_contents($this->storage->getFilePath($name), $data); + } + + protected function delete($name) { + unlink($this->storage->getFilePath($name)); + } + + /** + * Tests the FileStorage::listAll method with a relative and absolute path. + */ + public function testlistAll() { + $expected_files = array( + 'core.extension', + 'system.performance', + ); + + $config_files = $this->storage->listAll(); + $this->assertIdentical($config_files, $expected_files, 'Relative path, two config files found.'); + + // @todo https://www.drupal.org/node/2666954 FileStorage::listAll() is + // case-sensitive. However, \Drupal\Core\Config\DatabaseStorage::listAll() + // is case-insensitive. + $this->assertIdentical(['system.performance'], $this->storage->listAll('system'), 'The FileStorage::listAll() with prefix works.'); + $this->assertIdentical([], $this->storage->listAll('System'), 'The FileStorage::listAll() is case sensitive.'); + } + + /** + * Test UnsupportedDataTypeConfigException displays path of + * erroneous file during read. + */ + public function testReadUnsupportedDataTypeConfigException() { + file_put_contents($this->storage->getFilePath('core.extension'), PHP_EOL . 'foo : [bar}', FILE_APPEND); + try { + $config_parsed = $this->storage->read('core.extension'); + } + catch (UnsupportedDataTypeConfigException $e) { + $this->pass('Exception thrown when trying to read a field containing invalid data type.'); + $this->assertTrue((strpos($e->getMessage(), $this->storage->getFilePath('core.extension')) !== FALSE), 'Erroneous file path is displayed.'); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/AlterTest.php b/core/tests/Drupal/KernelTests/Core/Database/AlterTest.php new file mode 100644 index 0000000..6079d33 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/AlterTest.php @@ -0,0 +1,155 @@ +addField('test', 'name'); + $query->addField('test', 'age', 'age'); + $query->addTag('database_test_alter_add_range'); + + $result = $query->execute()->fetchAll(); + + $this->assertEqual(count($result), 2, 'Returned the correct number of rows.'); + } + + /** + * Tests that we can alter the joins on a query. + */ + function testAlterWithJoin() { + $query = db_select('test_task'); + $tid_field = $query->addField('test_task', 'tid'); + $task_field = $query->addField('test_task', 'task'); + $query->orderBy($task_field); + $query->addTag('database_test_alter_add_join'); + + $result = $query->execute(); + + $records = $result->fetchAll(); + + $this->assertEqual(count($records), 2, 'Returned the correct number of rows.'); + + $this->assertEqual($records[0]->name, 'George', 'Correct data retrieved.'); + $this->assertEqual($records[0]->$tid_field, 4, 'Correct data retrieved.'); + $this->assertEqual($records[0]->$task_field, 'sing', 'Correct data retrieved.'); + $this->assertEqual($records[1]->name, 'George', 'Correct data retrieved.'); + $this->assertEqual($records[1]->$tid_field, 5, 'Correct data retrieved.'); + $this->assertEqual($records[1]->$task_field, 'sleep', 'Correct data retrieved.'); + } + + /** + * Tests that we can alter a query's conditionals. + */ + function testAlterChangeConditional() { + $query = db_select('test_task'); + $tid_field = $query->addField('test_task', 'tid'); + $pid_field = $query->addField('test_task', 'pid'); + $task_field = $query->addField('test_task', 'task'); + $people_alias = $query->join('test', 'people', "test_task.pid = people.id"); + $name_field = $query->addField($people_alias, 'name', 'name'); + $query->condition('test_task.tid', '1'); + $query->orderBy($tid_field); + $query->addTag('database_test_alter_change_conditional'); + + $result = $query->execute(); + + $records = $result->fetchAll(); + + $this->assertEqual(count($records), 1, 'Returned the correct number of rows.'); + $this->assertEqual($records[0]->$name_field, 'John', 'Correct data retrieved.'); + $this->assertEqual($records[0]->$tid_field, 2, 'Correct data retrieved.'); + $this->assertEqual($records[0]->$pid_field, 1, 'Correct data retrieved.'); + $this->assertEqual($records[0]->$task_field, 'sleep', 'Correct data retrieved.'); + } + + /** + * Tests that we can alter the fields of a query. + */ + function testAlterChangeFields() { + $query = db_select('test'); + $name_field = $query->addField('test', 'name'); + $age_field = $query->addField('test', 'age', 'age'); + $query->orderBy('name'); + $query->addTag('database_test_alter_change_fields'); + + $record = $query->execute()->fetch(); + $this->assertEqual($record->$name_field, 'George', 'Correct data retrieved.'); + $this->assertFalse(isset($record->$age_field), 'Age field not found, as intended.'); + } + + /** + * Tests that we can alter expressions in the query. + */ + function testAlterExpression() { + $query = db_select('test'); + $name_field = $query->addField('test', 'name'); + $age_field = $query->addExpression("age*2", 'double_age'); + $query->condition('age', 27); + $query->addTag('database_test_alter_change_expressions'); + $result = $query->execute(); + + // Ensure that we got the right record. + $record = $result->fetch(); + + $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.'); + $this->assertEqual($record->$age_field, 27*3, 'Fetched age expression is correct.'); + } + + /** + * Tests that we can remove a range() value from a query. + * + * This also tests hook_query_TAG_alter(). + */ + function testAlterRemoveRange() { + $query = db_select('test'); + $query->addField('test', 'name'); + $query->addField('test', 'age', 'age'); + $query->range(0, 2); + $query->addTag('database_test_alter_remove_range'); + + $num_records = count($query->execute()->fetchAll()); + + $this->assertEqual($num_records, 4, 'Returned the correct number of rows.'); + } + + /** + * Tests that we can do basic alters on subqueries. + */ + function testSimpleAlterSubquery() { + // Create a sub-query with an alter tag. + $subquery = db_select('test', 'p'); + $subquery->addField('p', 'name'); + $subquery->addField('p', 'id'); + // Pick out George. + $subquery->condition('age', 27); + $subquery->addExpression("age*2", 'double_age'); + // This query alter should change it to age * 3. + $subquery->addTag('database_test_alter_change_expressions'); + + // Create a main query and join to sub-query. + $query = db_select('test_task', 'tt'); + $query->join($subquery, 'pq', 'pq.id = tt.pid'); + $age_field = $query->addField('pq', 'double_age'); + $name_field = $query->addField('pq', 'name'); + + $record = $query->execute()->fetch(); + $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.'); + $this->assertEqual($record->$age_field, 27*3, 'Fetched age expression is correct.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/BasicSyntaxTest.php b/core/tests/Drupal/KernelTests/Core/Database/BasicSyntaxTest.php new file mode 100644 index 0000000..8d8f7cb --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/BasicSyntaxTest.php @@ -0,0 +1,141 @@ + 'This', + ':a2' => ' ', + ':a3' => 'is', + ':a4' => ' a ', + ':a5' => 'test.', + )); + $this->assertIdentical($result->fetchField(), 'This is a test.', 'Basic CONCAT works.'); + } + + /** + * Tests string concatenation with field values. + */ + function testConcatFields() { + $result = db_query('SELECT CONCAT(:a1, CONCAT(name, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age', array( + ':a1' => 'The age of ', + ':a2' => ' is ', + ':a3' => '.', + ':age' => 25, + )); + $this->assertIdentical($result->fetchField(), 'The age of John is 25.', 'Field CONCAT works.'); + } + + /** + * Tests string concatenation with separator. + */ + function testConcatWsLiterals() { + $result = db_query("SELECT CONCAT_WS(', ', :a1, NULL, :a2, :a3, :a4)", array( + ':a1' => 'Hello', + ':a2' => NULL, + ':a3' => '', + ':a4' => 'world.', + )); + $this->assertIdentical($result->fetchField(), 'Hello, , world.'); + } + + /** + * Tests string concatenation with separator, with field values. + */ + function testConcatWsFields() { + $result = db_query("SELECT CONCAT_WS('-', :a1, name, :a2, age) FROM {test} WHERE age = :age", array( + ':a1' => 'name', + ':a2' => 'age', + ':age' => 25, + )); + $this->assertIdentical($result->fetchField(), 'name-John-age-25'); + } + + /** + * Tests escaping of LIKE wildcards. + */ + function testLikeEscape() { + db_insert('test') + ->fields(array( + 'name' => 'Ring_', + )) + ->execute(); + + // Match both "Ringo" and "Ring_". + $num_matches = db_select('test', 't') + ->condition('name', 'Ring_', 'LIKE') + ->countQuery() + ->execute() + ->fetchField(); + $this->assertIdentical($num_matches, '2', 'Found 2 records.'); + // Match only "Ring_" using a LIKE expression with no wildcards. + $num_matches = db_select('test', 't') + ->condition('name', db_like('Ring_'), 'LIKE') + ->countQuery() + ->execute() + ->fetchField(); + $this->assertIdentical($num_matches, '1', 'Found 1 record.'); + } + + /** + * Tests a LIKE query containing a backslash. + */ + function testLikeBackslash() { + db_insert('test') + ->fields(array('name')) + ->values(array( + 'name' => 'abcde\f', + )) + ->values(array( + 'name' => 'abc%\_', + )) + ->execute(); + + // Match both rows using a LIKE expression with two wildcards and a verbatim + // backslash. + $num_matches = db_select('test', 't') + ->condition('name', 'abc%\\\\_', 'LIKE') + ->countQuery() + ->execute() + ->fetchField(); + $this->assertIdentical($num_matches, '2', 'Found 2 records.'); + // Match only the former using a LIKE expression with no wildcards. + $num_matches = db_select('test', 't') + ->condition('name', db_like('abc%\_'), 'LIKE') + ->countQuery() + ->execute() + ->fetchField(); + $this->assertIdentical($num_matches, '1', 'Found 1 record.'); + } + + /** + * Tests \Drupal\Core\Database\Connection::getFullQualifiedTableName(). + */ + public function testGetFullQualifiedTableName() { + $database = \Drupal::database(); + $num_matches = $database->select($database->getFullQualifiedTableName('test'), 't') + ->countQuery() + ->execute() + ->fetchField(); + $this->assertIdentical($num_matches, '4', 'Found 4 records.'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/CaseSensitivityTest.php b/core/tests/Drupal/KernelTests/Core/Database/CaseSensitivityTest.php new file mode 100644 index 0000000..2ecdfce --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/CaseSensitivityTest.php @@ -0,0 +1,35 @@ +fetchField(); + + db_insert('test') + ->fields(array( + 'name' => 'john', // <- A record already exists with name 'John'. + 'age' => 2, + 'job' => 'Baby', + )) + ->execute(); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); + $this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'john'))->fetchField(); + $this->assertIdentical($saved_age, '2', 'Can retrieve after inserting.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php b/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php new file mode 100644 index 0000000..adfadd8 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/ConnectionTest.php @@ -0,0 +1,180 @@ +assertNotNull($db1, 'default connection is a real connection object.'); + $this->assertNotNull($db2, 'replica connection is a real connection object.'); + $this->assertNotIdentical($db1, $db2, 'Each target refers to a different connection.'); + + // Try to open those targets another time, that should return the same objects. + $db1b = Database::getConnection('default', 'default'); + $db2b = Database::getConnection('replica', 'default'); + $this->assertIdentical($db1, $db1b, 'A second call to getConnection() returns the same object.'); + $this->assertIdentical($db2, $db2b, 'A second call to getConnection() returns the same object.'); + + // Try to open an unknown target. + $unknown_target = $this->randomMachineName(); + $db3 = Database::getConnection($unknown_target, 'default'); + $this->assertNotNull($db3, 'Opening an unknown target returns a real connection object.'); + $this->assertIdentical($db1, $db3, 'An unknown target opens the default connection.'); + + // Try to open that unknown target another time, that should return the same object. + $db3b = Database::getConnection($unknown_target, 'default'); + $this->assertIdentical($db3, $db3b, 'A second call to getConnection() returns the same object.'); + } + + /** + * Tests that connections return appropriate connection objects. + */ + function testConnectionRoutingOverride() { + // Clone the primary credentials to a replica connection. + // Note this will result in two independent connection objects that happen + // to point to the same place. + $connection_info = Database::getConnectionInfo('default'); + Database::addConnectionInfo('default', 'replica', $connection_info['default']); + + Database::ignoreTarget('default', 'replica'); + + $db1 = Database::getConnection('default', 'default'); + $db2 = Database::getConnection('replica', 'default'); + + $this->assertIdentical($db1, $db2, 'Both targets refer to the same connection.'); + } + + /** + * Tests the closing of a database connection. + */ + function testConnectionClosing() { + // Open the default target so we have an object to compare. + $db1 = Database::getConnection('default', 'default'); + + // Try to close the default connection, then open a new one. + Database::closeConnection('default', 'default'); + $db2 = Database::getConnection('default', 'default'); + + // Opening a connection after closing it should yield an object different than the original. + $this->assertNotIdentical($db1, $db2, 'Opening the default connection after it is closed returns a new object.'); + } + + /** + * Tests the connection options of the active database. + */ + function testConnectionOptions() { + $connection_info = Database::getConnectionInfo('default'); + + // Be sure we're connected to the default database. + $db = Database::getConnection('default', 'default'); + $connectionOptions = $db->getConnectionOptions(); + + // In the MySQL driver, the port can be different, so check individual + // options. + $this->assertEqual($connection_info['default']['driver'], $connectionOptions['driver'], 'The default connection info driver matches the current connection options driver.'); + $this->assertEqual($connection_info['default']['database'], $connectionOptions['database'], 'The default connection info database matches the current connection options database.'); + + // Set up identical replica and confirm connection options are identical. + Database::addConnectionInfo('default', 'replica', $connection_info['default']); + $db2 = Database::getConnection('replica', 'default'); + $connectionOptions2 = $db2->getConnectionOptions(); + + // Get a fresh copy of the default connection options. + $connectionOptions = $db->getConnectionOptions(); + $this->assertIdentical($connectionOptions, $connectionOptions2, 'The default and replica connection options are identical.'); + + // Set up a new connection with different connection info. + $test = $connection_info['default']; + $test['database'] .= 'test'; + Database::addConnectionInfo('test', 'default', $test); + $connection_info = Database::getConnectionInfo('test'); + + // Get a fresh copy of the default connection options. + $connectionOptions = $db->getConnectionOptions(); + $this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], 'The test connection info database does not match the current connection options database.'); + } + + /** + * Ensure that you cannot execute multiple statements on phpversion() > 5.5.21 or > 5.6.5. + */ + public function testMultipleStatementsForNewPhp() { + // This just tests mysql, as other PDO integrations don't allow disabling + // multiple statements. + if (Database::getConnection()->databaseType() !== 'mysql' || !defined('\PDO::MYSQL_ATTR_MULTI_STATEMENTS')) { + return; + } + + $db = Database::getConnection('default', 'default'); + // Disable the protection at the PHP level. + try { + $db->query('SELECT * FROM {test}; SELECT * FROM {test_people}', + [], + [ 'allow_delimiter_in_query' => TRUE ] + ); + $this->fail('No PDO exception thrown for multiple statements.'); + } + catch (DatabaseExceptionWrapper $e) { + $this->pass('PDO exception thrown for multiple statements.'); + } + } + + /** + * Ensure that you cannot execute multiple statements. + */ + public function testMultipleStatements() { + + $db = Database::getConnection('default', 'default'); + try { + $db->query('SELECT * FROM {test}; SELECT * FROM {test_people}'); + $this->fail('No exception thrown for multiple statements.'); + } + catch (\InvalidArgumentException $e) { + $this->pass('Exception thrown for multiple statements.'); + } + } + + /** + * Test the escapeTable(), escapeField() and escapeAlias() methods with all possible reserved words in PostgreSQL. + */ + public function testPostgresqlReservedWords() { + if (Database::getConnection()->databaseType() !== 'pgsql') { + return; + } + + $db = Database::getConnection('default', 'default'); + $stmt = $db->query("SELECT word FROM pg_get_keywords() WHERE catcode IN ('R', 'T')"); + $stmt->execute(); + foreach ($stmt->fetchAllAssoc('word') as $word => $row) { + $expected = '"' . $word . '"'; + $this->assertIdentical($db->escapeTable($word), $expected, format_string('The reserved word %word was correctly escaped when used as a table name.', array('%word' => $word))); + $this->assertIdentical($db->escapeField($word), $expected, format_string('The reserved word %word was correctly escaped when used as a column name.', array('%word' => $word))); + $this->assertIdentical($db->escapeAlias($word), $expected, format_string('The reserved word %word was correctly escaped when used as an alias.', array('%word' => $word))); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/ConnectionUnitTest.php b/core/tests/Drupal/KernelTests/Core/Database/ConnectionUnitTest.php new file mode 100644 index 0000000..78bae36 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/ConnectionUnitTest.php @@ -0,0 +1,248 @@ +key = 'default'; + $this->originalTarget = 'default'; + $this->target = 'DatabaseConnectionUnitTest'; + + // Determine whether the database driver is MySQL. If it is not, the test + // methods will not be executed. + // @todo Make this test driver-agnostic, or find a proper way to skip it. + // See https://www.drupal.org/node/1273478. + $connection_info = Database::getConnectionInfo('default'); + $this->skipTest = (bool) ($connection_info['default']['driver'] != 'mysql'); + if ($this->skipTest) { + // Insert an assertion to prevent Simpletest from interpreting the test + // as failure. + $this->pass('This test is only compatible with MySQL.'); + } + + // Create an additional connection to monitor the connections being opened + // and closed in this test. + // @see TestBase::changeDatabasePrefix() + Database::addConnectionInfo('default', 'monitor', $connection_info['default']); + $this->monitor = Database::getConnection('monitor'); + } + + /** + * Adds a new database connection info to Database. + */ + protected function addConnection() { + // Add a new target to the connection, by cloning the current connection. + $connection_info = Database::getConnectionInfo($this->key); + Database::addConnectionInfo($this->key, $this->target, $connection_info[$this->originalTarget]); + + // Verify that the new target exists. + $info = Database::getConnectionInfo($this->key); + // Note: Custom assertion message to not expose database credentials. + $this->assertIdentical($info[$this->target], $connection_info[$this->key], 'New connection info found.'); + } + + /** + * Returns the connection ID of the current test connection. + * + * @return int + */ + protected function getConnectionID() { + return (int) Database::getConnection($this->target, $this->key)->query('SELECT CONNECTION_ID()')->fetchField(); + } + + /** + * Asserts that a connection ID exists. + * + * @param int $id + * The connection ID to verify. + */ + protected function assertConnection($id) { + $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0); + return $this->assertTrue(isset($list[$id]), format_string('Connection ID @id found.', array('@id' => $id))); + } + + /** + * Asserts that a connection ID does not exist. + * + * @param int $id + * The connection ID to verify. + */ + protected function assertNoConnection($id) { + $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0); + return $this->assertFalse(isset($list[$id]), format_string('Connection ID @id not found.', array('@id' => $id))); + } + + /** + * Tests Database::closeConnection() without query. + * + * @todo getConnectionID() executes a query. + */ + function testOpenClose() { + if ($this->skipTest) { + return; + } + // Add and open a new connection. + $this->addConnection(); + $id = $this->getConnectionID(); + Database::getConnection($this->target, $this->key); + + // Verify that there is a new connection. + $this->assertConnection($id); + + // Close the connection. + Database::closeConnection($this->target, $this->key); + // Wait 20ms to give the database engine sufficient time to react. + usleep(20000); + + // Verify that we are back to the original connection count. + $this->assertNoConnection($id); + } + + /** + * Tests Database::closeConnection() with a query. + */ + function testOpenQueryClose() { + if ($this->skipTest) { + return; + } + // Add and open a new connection. + $this->addConnection(); + $id = $this->getConnectionID(); + Database::getConnection($this->target, $this->key); + + // Verify that there is a new connection. + $this->assertConnection($id); + + // Execute a query. + Database::getConnection($this->target, $this->key)->query('SHOW TABLES'); + + // Close the connection. + Database::closeConnection($this->target, $this->key); + // Wait 20ms to give the database engine sufficient time to react. + usleep(20000); + + // Verify that we are back to the original connection count. + $this->assertNoConnection($id); + } + + /** + * Tests Database::closeConnection() with a query and custom prefetch method. + */ + function testOpenQueryPrefetchClose() { + if ($this->skipTest) { + return; + } + // Add and open a new connection. + $this->addConnection(); + $id = $this->getConnectionID(); + Database::getConnection($this->target, $this->key); + + // Verify that there is a new connection. + $this->assertConnection($id); + + // Execute a query. + Database::getConnection($this->target, $this->key)->query('SHOW TABLES')->fetchCol(); + + // Close the connection. + Database::closeConnection($this->target, $this->key); + // Wait 20ms to give the database engine sufficient time to react. + usleep(20000); + + // Verify that we are back to the original connection count. + $this->assertNoConnection($id); + } + + /** + * Tests Database::closeConnection() with a select query. + */ + function testOpenSelectQueryClose() { + if ($this->skipTest) { + return; + } + // Add and open a new connection. + $this->addConnection(); + $id = $this->getConnectionID(); + Database::getConnection($this->target, $this->key); + + // Verify that there is a new connection. + $this->assertConnection($id); + + // Create a table. + $name = 'foo'; + Database::getConnection($this->target, $this->key)->schema()->createTable($name, array( + 'fields' => array( + 'name' => array( + 'type' => 'varchar', + 'length' => 255, + ), + ), + )); + + // Execute a query. + Database::getConnection($this->target, $this->key)->select('foo', 'f') + ->fields('f', array('name')) + ->execute() + ->fetchAll(); + + // Drop the table. + Database::getConnection($this->target, $this->key)->schema()->dropTable($name); + + // Close the connection. + Database::closeConnection($this->target, $this->key); + // Wait 20ms to give the database engine sufficient time to react. + usleep(20000); + + // Verify that we are back to the original connection count. + $this->assertNoConnection($id); + } + + /** + * Tests pdo options override. + */ + public function testConnectionOpen() { + $connection = Database::getConnection('default'); + $reflection = new \ReflectionObject($connection); + $connection_property = $reflection->getProperty('connection'); + $connection_property->setAccessible(TRUE); + $error_mode = $connection_property->getValue($connection) + ->getAttribute(\PDO::ATTR_ERRMODE); + $this->assertEqual($error_mode, \PDO::ERRMODE_EXCEPTION, 'Ensure the default error mode is set to exception.'); + + $connection = Database::getConnectionInfo('default'); + $connection['default']['pdo'][\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_SILENT; + Database::addConnectionInfo('test', 'default', $connection['default']); + $connection = Database::getConnection('default', 'test'); + + $reflection = new \ReflectionObject($connection); + $connection_property = $reflection->getProperty('connection'); + $connection_property->setAccessible(TRUE); + $error_mode = $connection_property->getValue($connection) + ->getAttribute(\PDO::ATTR_ERRMODE); + $this->assertEqual($error_mode, \PDO::ERRMODE_SILENT, 'Ensure PDO connection options can be overridden.'); + + Database::removeConnection('test'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/DatabaseExceptionWrapperTest.php b/core/tests/Drupal/KernelTests/Core/Database/DatabaseExceptionWrapperTest.php new file mode 100644 index 0000000..5b8a085 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/DatabaseExceptionWrapperTest.php @@ -0,0 +1,65 @@ +prepare('bananas'); + + // MySQL only validates the syntax upon trying to execute a query. + // @throws \Drupal\Core\Database\DatabaseExceptionWrapper + $connection->query($query); + + $this->fail('Expected PDOException or DatabaseExceptionWrapper, none was thrown.'); + } + catch (\PDOException $e) { + $this->pass('Expected PDOException was thrown.'); + } + catch (DatabaseExceptionWrapper $e) { + $this->pass('Expected DatabaseExceptionWrapper was thrown.'); + } + catch (\Exception $e) { + $this->fail("Thrown exception is not a PDOException:\n" . (string) $e); + } + } + + /** + * Tests the expected database exception thrown for inexistent tables. + */ + public function testQueryThrowsDatabaseExceptionWrapperException() { + $connection = Database::getConnection(); + try { + $connection->query('SELECT * FROM {does_not_exist}'); + $this->fail('Expected PDOException, none was thrown.'); + } + catch (DatabaseExceptionWrapper $e) { + $this->pass('Expected DatabaseExceptionWrapper was thrown.'); + } + catch (\Exception $e) { + $this->fail("Thrown exception is not a DatabaseExceptionWrapper:\n" . (string) $e); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/DatabaseTestBase.php b/core/tests/Drupal/KernelTests/Core/Database/DatabaseTestBase.php new file mode 100644 index 0000000..444e53b --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/DatabaseTestBase.php @@ -0,0 +1,151 @@ +installSchema('database_test', array( + 'test', + 'test_people', + 'test_people_copy', + 'test_one_blob', + 'test_two_blobs', + 'test_task', + 'test_null', + 'test_serialized', + 'test_special_columns', + 'TEST_UPPERCASE', + )); + self::addSampleData(); + } + + /** + * Sets up tables for NULL handling. + */ + function ensureSampleDataNull() { + db_insert('test_null') + ->fields(array('name', 'age')) + ->values(array( + 'name' => 'Kermit', + 'age' => 25, + )) + ->values(array( + 'name' => 'Fozzie', + 'age' => NULL, + )) + ->values(array( + 'name' => 'Gonzo', + 'age' => 27, + )) + ->execute(); + } + + /** + * Sets up our sample data. + */ + static function addSampleData() { + // We need the IDs, so we can't use a multi-insert here. + $john = db_insert('test') + ->fields(array( + 'name' => 'John', + 'age' => 25, + 'job' => 'Singer', + )) + ->execute(); + + $george = db_insert('test') + ->fields(array( + 'name' => 'George', + 'age' => 27, + 'job' => 'Singer', + )) + ->execute(); + + db_insert('test') + ->fields(array( + 'name' => 'Ringo', + 'age' => 28, + 'job' => 'Drummer', + )) + ->execute(); + + $paul = db_insert('test') + ->fields(array( + 'name' => 'Paul', + 'age' => 26, + 'job' => 'Songwriter', + )) + ->execute(); + + db_insert('test_people') + ->fields(array( + 'name' => 'Meredith', + 'age' => 30, + 'job' => 'Speaker', + )) + ->execute(); + + db_insert('test_task') + ->fields(array('pid', 'task', 'priority')) + ->values(array( + 'pid' => $john, + 'task' => 'eat', + 'priority' => 3, + )) + ->values(array( + 'pid' => $john, + 'task' => 'sleep', + 'priority' => 4, + )) + ->values(array( + 'pid' => $john, + 'task' => 'code', + 'priority' => 1, + )) + ->values(array( + 'pid' => $george, + 'task' => 'sing', + 'priority' => 2, + )) + ->values(array( + 'pid' => $george, + 'task' => 'sleep', + 'priority' => 2, + )) + ->values(array( + 'pid' => $paul, + 'task' => 'found new band', + 'priority' => 1, + )) + ->values(array( + 'pid' => $paul, + 'task' => 'perform at superbowl', + 'priority' => 3, + )) + ->execute(); + + db_insert('test_special_columns') + ->fields(array( + 'id' => 1, + 'offset' => 'Offset value 1', + )) + ->execute(); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/DeleteTruncateTest.php b/core/tests/Drupal/KernelTests/Core/Database/DeleteTruncateTest.php new file mode 100644 index 0000000..1cce4b8 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/DeleteTruncateTest.php @@ -0,0 +1,88 @@ +fetchField(); + $pid_to_delete = db_query("SELECT * FROM {test_task} WHERE task = 'sleep'")->fetchField(); + + $subquery = db_select('test', 't') + ->fields('t', array('id')) + ->condition('t.id', array($pid_to_delete), 'IN'); + $delete = db_delete('test_task') + ->condition('task', 'sleep') + ->condition('pid', $subquery, 'IN'); + + $num_deleted = $delete->execute(); + $this->assertEqual($num_deleted, 1, 'Deleted 1 record.'); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField(); + $this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.'); + } + + /** + * Confirms that we can delete a single record successfully. + */ + function testSimpleDelete() { + $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); + + $num_deleted = db_delete('test') + ->condition('id', 1) + ->execute(); + $this->assertIdentical($num_deleted, 1, 'Deleted 1 record.'); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); + $this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.'); + } + + /** + * Confirms that we can truncate a whole table successfully. + */ + function testTruncate() { + $num_records_before = db_query("SELECT COUNT(*) FROM {test}")->fetchField(); + $this->assertTrue($num_records_before > 0, 'The table is not empty.'); + + db_truncate('test')->execute(); + + $num_records_after = db_query("SELECT COUNT(*) FROM {test}")->fetchField(); + $this->assertEqual(0, $num_records_after, 'Truncate really deletes everything.'); + } + + /** + * Confirms that we can delete a single special column name record successfully. + */ + function testSpecialColumnDelete() { + $num_records_before = db_query('SELECT COUNT(*) FROM {test_special_columns}')->fetchField(); + + $num_deleted = db_delete('test_special_columns') + ->condition('id', 1) + ->execute(); + $this->assertIdentical($num_deleted, 1, 'Deleted 1 special column record.'); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test_special_columns}')->fetchField(); + $this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/FetchTest.php b/core/tests/Drupal/KernelTests/Core/Database/FetchTest.php new file mode 100644 index 0000000..987b810 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/FetchTest.php @@ -0,0 +1,151 @@ + 25)); + $this->assertTrue($result instanceof StatementInterface, 'Result set is a Drupal statement object.'); + foreach ($result as $record) { + $records[] = $record; + $this->assertTrue(is_object($record), 'Record is an object.'); + $this->assertIdentical($record->name, 'John', '25 year old is John.'); + } + + $this->assertIdentical(count($records), 1, 'There is only one record.'); + } + + /** + * Confirms that we can fetch a record to an object explicitly. + */ + function testQueryFetchObject() { + $records = array(); + $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => \PDO::FETCH_OBJ)); + foreach ($result as $record) { + $records[] = $record; + $this->assertTrue(is_object($record), 'Record is an object.'); + $this->assertIdentical($record->name, 'John', '25 year old is John.'); + } + + $this->assertIdentical(count($records), 1, 'There is only one record.'); + } + + /** + * Confirms that we can fetch a record to an associative array explicitly. + */ + function testQueryFetchArray() { + $records = array(); + $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => \PDO::FETCH_ASSOC)); + foreach ($result as $record) { + $records[] = $record; + if ($this->assertTrue(is_array($record), 'Record is an array.')) { + $this->assertIdentical($record['name'], 'John', 'Record can be accessed associatively.'); + } + } + + $this->assertIdentical(count($records), 1, 'There is only one record.'); + } + + /** + * Confirms that we can fetch a record into a new instance of a custom class. + * + * @see \Drupal\system\Tests\Database\FakeRecord + */ + function testQueryFetchClass() { + $records = array(); + $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => 'Drupal\system\Tests\Database\FakeRecord')); + foreach ($result as $record) { + $records[] = $record; + if ($this->assertTrue($record instanceof FakeRecord, 'Record is an object of class FakeRecord.')) { + $this->assertIdentical($record->name, 'John', '25 year old is John.'); + } + } + + $this->assertIdentical(count($records), 1, 'There is only one record.'); + } + + /** + * Confirms that we can fetch a record into an indexed array explicitly. + */ + function testQueryFetchNum() { + $records = array(); + $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => \PDO::FETCH_NUM)); + foreach ($result as $record) { + $records[] = $record; + if ($this->assertTrue(is_array($record), 'Record is an array.')) { + $this->assertIdentical($record[0], 'John', 'Record can be accessed numerically.'); + } + } + + $this->assertIdentical(count($records), 1, 'There is only one record'); + } + + /** + * Confirms that we can fetch a record into a doubly-keyed array explicitly. + */ + function testQueryFetchBoth() { + $records = array(); + $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => \PDO::FETCH_BOTH)); + foreach ($result as $record) { + $records[] = $record; + if ($this->assertTrue(is_array($record), 'Record is an array.')) { + $this->assertIdentical($record[0], 'John', 'Record can be accessed numerically.'); + $this->assertIdentical($record['name'], 'John', 'Record can be accessed associatively.'); + } + } + + $this->assertIdentical(count($records), 1, 'There is only one record.'); + } + + /** + * Confirms that we can fetch an entire column of a result set at once. + */ + function testQueryFetchCol() { + $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25)); + $column = $result->fetchCol(); + $this->assertIdentical(count($column), 3, 'fetchCol() returns the right number of records.'); + + $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25)); + $i = 0; + foreach ($result as $record) { + $this->assertIdentical($record->name, $column[$i++], 'Column matches direct access.'); + } + } + + /** + * Tests that rowCount() throws exception on SELECT query. + */ + public function testRowCount() { + $result = db_query('SELECT name FROM {test}'); + try { + $result->rowCount(); + $exception = FALSE; + } + catch (RowCountException $e) { + $exception = TRUE; + } + $this->assertTrue($exception, 'Exception was thrown'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/InsertDefaultsTest.php b/core/tests/Drupal/KernelTests/Core/Database/InsertDefaultsTest.php new file mode 100644 index 0000000..5256e4d --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/InsertDefaultsTest.php @@ -0,0 +1,64 @@ +useDefaults(array('job')); + $id = $query->execute(); + + $schema = drupal_get_module_schema('database_test', 'test'); + + $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField(); + $this->assertEqual($job, $schema['fields']['job']['default'], 'Default field value is set.'); + } + + /** + * Tests that no action will be preformed if no fields are specified. + */ + function testDefaultEmptyInsert() { + $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField(); + + try { + db_insert('test')->execute(); + // This is only executed if no exception has been thrown. + $this->fail('Expected exception NoFieldsException has not been thrown.'); + } catch (NoFieldsException $e) { + $this->pass('Expected exception NoFieldsException has been thrown.'); + } + + $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField(); + $this->assertIdentical($num_records_before, $num_records_after, 'Do nothing as no fields are specified.'); + } + + /** + * Tests that we can insert fields with values and defaults in the same query. + */ + function testDefaultInsertWithFields() { + $query = db_insert('test') + ->fields(array('name' => 'Bob')) + ->useDefaults(array('job')); + $id = $query->execute(); + + $schema = drupal_get_module_schema('database_test', 'test'); + + $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField(); + $this->assertEqual($job, $schema['fields']['job']['default'], 'Default field value is set.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/InsertLobTest.php b/core/tests/Drupal/KernelTests/Core/Database/InsertLobTest.php new file mode 100644 index 0000000..2037e69 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/InsertLobTest.php @@ -0,0 +1,43 @@ +assertTrue(strlen($data) === 15, 'Test data contains a NULL.'); + $id = db_insert('test_one_blob') + ->fields(array('blob1' => $data)) + ->execute(); + $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc(); + $this->assertTrue($r['blob1'] === $data, format_string('Can insert a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r)))); + } + + /** + * Tests that we can insert multiple blob fields in the same query. + */ + function testInsertMultipleBlob() { + $id = db_insert('test_two_blobs') + ->fields(array( + 'blob1' => 'This is', + 'blob2' => 'a test', + )) + ->execute(); + $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc(); + $this->assertTrue($r['blob1'] === 'This is' && $r['blob2'] === 'a test', 'Can insert multiple blobs per row.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/InsertTest.php b/core/tests/Drupal/KernelTests/Core/Database/InsertTest.php new file mode 100644 index 0000000..2f91c7f --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/InsertTest.php @@ -0,0 +1,214 @@ +fetchField(); + + $query = db_insert('test'); + $query->fields(array( + 'name' => 'Yoko', + 'age' => '29', + )); + + // Check how many records are queued for insertion. + $this->assertIdentical($query->count(), 1, 'One record is queued for insertion.'); + $query->execute(); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); + $this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Yoko'))->fetchField(); + $this->assertIdentical($saved_age, '29', 'Can retrieve after inserting.'); + } + + /** + * Tests that we can insert multiple records in one query object. + */ + function testMultiInsert() { + $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField(); + + $query = db_insert('test'); + $query->fields(array( + 'name' => 'Larry', + 'age' => '30', + )); + + // We should be able to specify values in any order if named. + $query->values(array( + 'age' => '31', + 'name' => 'Curly', + )); + + // Check how many records are queued for insertion. + $this->assertIdentical($query->count(), 2, 'Two records are queued for insertion.'); + + // We should be able to say "use the field order". + // This is not the recommended mechanism for most cases, but it should work. + $query->values(array('Moe', '32')); + + // Check how many records are queued for insertion. + $this->assertIdentical($query->count(), 3, 'Three records are queued for insertion.'); + $query->execute(); + + $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField(); + $this->assertIdentical($num_records_before + 3, $num_records_after, 'Record inserts correctly.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField(); + $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField(); + $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField(); + $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.'); + } + + /** + * Tests that an insert object can be reused with new data after it executes. + */ + function testRepeatedInsert() { + $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); + + $query = db_insert('test'); + + $query->fields(array( + 'name' => 'Larry', + 'age' => '30', + )); + // Check how many records are queued for insertion. + $this->assertIdentical($query->count(), 1, 'One record is queued for insertion.'); + $query->execute(); // This should run the insert, but leave the fields intact. + + // We should be able to specify values in any order if named. + $query->values(array( + 'age' => '31', + 'name' => 'Curly', + )); + // Check how many records are queued for insertion. + $this->assertIdentical($query->count(), 1, 'One record is queued for insertion.'); + $query->execute(); + + // We should be able to say "use the field order". + $query->values(array('Moe', '32')); + + // Check how many records are queued for insertion. + $this->assertIdentical($query->count(), 1, 'One record is queued for insertion.'); + $query->execute(); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField(); + $this->assertIdentical((int) $num_records_before + 3, (int) $num_records_after, 'Record inserts correctly.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField(); + $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField(); + $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField(); + $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.'); + } + + /** + * Tests that we can specify fields without values and specify values later. + */ + function testInsertFieldOnlyDefinition() { + // This is useful for importers, when we want to create a query and define + // its fields once, then loop over a multi-insert execution. + db_insert('test') + ->fields(array('name', 'age')) + ->values(array('Larry', '30')) + ->values(array('Curly', '31')) + ->values(array('Moe', '32')) + ->execute(); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField(); + $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField(); + $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField(); + $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.'); + } + + /** + * Tests that inserts return the proper auto-increment ID. + */ + function testInsertLastInsertID() { + $id = db_insert('test') + ->fields(array( + 'name' => 'Larry', + 'age' => '30', + )) + ->execute(); + + $this->assertIdentical($id, '5', 'Auto-increment ID returned successfully.'); + } + + /** + * Tests that the INSERT INTO ... SELECT (fields) ... syntax works. + */ + function testInsertSelectFields() { + $query = db_select('test_people', 'tp'); + // The query builder will always append expressions after fields. + // Add the expression first to test that the insert fields are correctly + // re-ordered. + $query->addExpression('tp.age', 'age'); + $query + ->fields('tp', array('name', 'job')) + ->condition('tp.name', 'Meredith'); + + // The resulting query should be equivalent to: + // INSERT INTO test (age, name, job) + // SELECT tp.age AS age, tp.name AS name, tp.job AS job + // FROM test_people tp + // WHERE tp.name = 'Meredith' + db_insert('test') + ->from($query) + ->execute(); + + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Meredith'))->fetchField(); + $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.'); + } + + /** + * Tests that the INSERT INTO ... SELECT * ... syntax works. + */ + function testInsertSelectAll() { + $query = db_select('test_people', 'tp') + ->fields('tp') + ->condition('tp.name', 'Meredith'); + + // The resulting query should be equivalent to: + // INSERT INTO test_people_copy + // SELECT * + // FROM test_people tp + // WHERE tp.name = 'Meredith' + db_insert('test_people_copy') + ->from($query) + ->execute(); + + $saved_age = db_query('SELECT age FROM {test_people_copy} WHERE name = :name', array(':name' => 'Meredith'))->fetchField(); + $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.'); + } + + /** + * Tests that we can INSERT INTO a special named column. + */ + function testSpecialColumnInsert() { + $id = db_insert('test_special_columns') + ->fields(array( + 'id' => 2, + 'offset' => 'Offset value 2', + )) + ->execute(); + $saved_value = db_query('SELECT "offset" FROM {test_special_columns} WHERE id = :id', array(':id' => 2))->fetchField(); + $this->assertIdentical($saved_value, 'Offset value 2', 'Can retrieve special column name value after inserting.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/InvalidDataTest.php b/core/tests/Drupal/KernelTests/Core/Database/InvalidDataTest.php new file mode 100644 index 0000000..8cb54de --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/InvalidDataTest.php @@ -0,0 +1,74 @@ +fields(array('name', 'age', 'job')) + ->values(array( + 'name' => 'Elvis', + 'age' => 63, + 'job' => 'Singer', + ))->values(array( + 'name' => 'John', // <-- Duplicate value on unique field. + 'age' => 17, + 'job' => 'Consultant', + )) + ->values(array( + 'name' => 'Frank', + 'age' => 75, + 'job' => 'Singer', + )) + ->execute(); + $this->fail('Insert succeeded when it should not have.'); + } + catch (IntegrityConstraintViolationException $e) { + // Check if the first record was inserted. + $name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 63))->fetchField(); + + if ($name == 'Elvis') { + if (!Database::getConnection()->supportsTransactions()) { + // This is an expected fail. + // Database engines that don't support transactions can leave partial + // inserts in place when an error occurs. This is the case for MySQL + // when running on a MyISAM table. + $this->pass("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions"); + } + else { + $this->fail('The whole transaction is rolled back when a duplicate key insert occurs.'); + } + } + else { + $this->pass('The whole transaction is rolled back when a duplicate key insert occurs.'); + } + + // Ensure the other values were not inserted. + $record = db_select('test') + ->fields('test', array('name', 'age')) + ->condition('age', array(17, 75), 'IN') + ->execute()->fetchObject(); + + $this->assertFalse($record, 'The rest of the insert aborted as expected.'); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/LargeQueryTest.php b/core/tests/Drupal/KernelTests/Core/Database/LargeQueryTest.php new file mode 100644 index 0000000..76451dd --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/LargeQueryTest.php @@ -0,0 +1,58 @@ +fetchField(); + if (Environment::checkMemoryLimit($max_allowed_packet + (16 * 1024 * 1024))) { + $long_name = str_repeat('a', $max_allowed_packet + 1); + try { + db_query('SELECT name FROM {test} WHERE name = :name', array(':name' => $long_name)); + $this->fail("An exception should be thrown for queries larger than 'max_allowed_packet'"); + } catch (DatabaseException $e) { + // Close and re-open the connection. Otherwise we will run into error + // 2006 "MySQL server had gone away" afterwards. + Database::closeConnection(); + Database::getConnection(); + $this->assertEqual($e->getPrevious()->errorInfo[1], 1153, "Got a packet bigger than 'max_allowed_packet' bytes exception thrown."); + // Use strlen() to count the bytes exactly, not the unicode chars. + $this->assertTrue(strlen($e->getMessage()) <= $max_allowed_packet, "'max_allowed_packet' exception message truncated."); + } + } + else { + $this->verbose('The configured max_allowed_packet exceeds the php memory limit. Therefore the test is skipped.'); + } + } + else { + $this->verbose('The test requires MySQL. Therefore the test is skipped.'); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/LoggingTest.php b/core/tests/Drupal/KernelTests/Core/Database/LoggingTest.php new file mode 100644 index 0000000..e98d069 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/LoggingTest.php @@ -0,0 +1,133 @@ + :age', array(':age' => 25))->fetchCol(); + db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol(); + + // Trigger a call that does not have file in the backtrace. + call_user_func_array('db_query', array('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo')))->fetchCol(); + + $queries = Database::getLog('testing', 'default'); + + $this->assertEqual(count($queries), 3, 'Correct number of queries recorded.'); + + foreach ($queries as $query) { + $this->assertEqual($query['caller']['function'], __FUNCTION__, 'Correct function in query log.'); + } + } + + /** + * Tests that we can run two logs in parallel. + */ + function testEnableMultiLogging() { + Database::startLog('testing1'); + + db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); + + Database::startLog('testing2'); + + db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol(); + + $queries1 = Database::getLog('testing1'); + $queries2 = Database::getLog('testing2'); + + $this->assertEqual(count($queries1), 2, 'Correct number of queries recorded for log 1.'); + $this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for log 2.'); + } + + /** + * Tests logging queries against multiple targets on the same connection. + */ + function testEnableTargetLogging() { + // Clone the primary credentials to a replica connection and to another fake + // connection. + $connection_info = Database::getConnectionInfo('default'); + Database::addConnectionInfo('default', 'replica', $connection_info['default']); + + Database::startLog('testing1'); + + db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); + + db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'replica'));//->fetchCol(); + + $queries1 = Database::getLog('testing1'); + + $this->assertEqual(count($queries1), 2, 'Recorded queries from all targets.'); + $this->assertEqual($queries1[0]['target'], 'default', 'First query used default target.'); + $this->assertEqual($queries1[1]['target'], 'replica', 'Second query used replica target.'); + } + + /** + * Tests that logs to separate targets use the same connection properly. + * + * This test is identical to the one above, except that it doesn't create + * a fake target so the query should fall back to running on the default + * target. + */ + function testEnableTargetLoggingNoTarget() { + Database::startLog('testing1'); + + db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); + + // We use "fake" here as a target because any non-existent target will do. + // However, because all of the tests in this class share a single page + // request there is likely to be a target of "replica" from one of the other + // unit tests, so we use a target here that we know with absolute certainty + // does not exist. + db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'fake'))->fetchCol(); + + $queries1 = Database::getLog('testing1'); + + $this->assertEqual(count($queries1), 2, 'Recorded queries from all targets.'); + $this->assertEqual($queries1[0]['target'], 'default', 'First query used default target.'); + $this->assertEqual($queries1[1]['target'], 'default', 'Second query used default target as fallback.'); + } + + /** + * Tests that we can log queries separately on different connections. + */ + function testEnableMultiConnectionLogging() { + // Clone the primary credentials to a fake connection. + // That both connections point to the same physical database is irrelevant. + $connection_info = Database::getConnectionInfo('default'); + Database::addConnectionInfo('test2', 'default', $connection_info['default']); + + Database::startLog('testing1'); + Database::startLog('testing1', 'test2'); + + db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol(); + + $old_key = db_set_active('test2'); + + db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'replica'))->fetchCol(); + + db_set_active($old_key); + + $queries1 = Database::getLog('testing1'); + $queries2 = Database::getLog('testing1', 'test2'); + + $this->assertEqual(count($queries1), 1, 'Correct number of queries recorded for first connection.'); + $this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for second connection.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/MergeTest.php b/core/tests/Drupal/KernelTests/Core/Database/MergeTest.php new file mode 100644 index 0000000..3e57857 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/MergeTest.php @@ -0,0 +1,237 @@ +fetchField(); + + $result = db_merge('test_people') + ->key('job', 'Presenter') + ->fields(array( + 'age' => 31, + 'name' => 'Tiffany', + )) + ->execute(); + + $this->assertEqual($result, Merge::STATUS_INSERT, 'Insert status returned.'); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + $this->assertEqual($num_records_before + 1, $num_records_after, 'Merge inserted properly.'); + + $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch(); + $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.'); + $this->assertEqual($person->age, 31, 'Age set correctly.'); + $this->assertEqual($person->job, 'Presenter', 'Job set correctly.'); + } + + /** + * Confirms that we can merge-update a record successfully. + */ + function testMergeUpdate() { + $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + + $result = db_merge('test_people') + ->key('job', 'Speaker') + ->fields(array( + 'age' => 31, + 'name' => 'Tiffany', + )) + ->execute(); + + $this->assertEqual($result, Merge::STATUS_UPDATE, 'Update status returned.'); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.'); + + $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); + $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.'); + $this->assertEqual($person->age, 31, 'Age set correctly.'); + $this->assertEqual($person->job, 'Speaker', 'Job set correctly.'); + } + + /** + * Confirms that we can merge-update a record successfully. + * + * This test varies from the previous test because it manually defines which + * fields are inserted, and which fields are updated. + */ + function testMergeUpdateExcept() { + $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + + db_merge('test_people') + ->key('job', 'Speaker') + ->insertFields(array('age' => 31)) + ->updateFields(array('name' => 'Tiffany')) + ->execute(); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.'); + + $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); + $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.'); + $this->assertEqual($person->age, 30, 'Age skipped correctly.'); + $this->assertEqual($person->job, 'Speaker', 'Job set correctly.'); + } + + /** + * Confirms that we can merge-update a record, with alternate replacement. + */ + function testMergeUpdateExplicit() { + $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + + db_merge('test_people') + ->key('job', 'Speaker') + ->insertFields(array( + 'age' => 31, + 'name' => 'Tiffany', + )) + ->updateFields(array( + 'name' => 'Joe', + )) + ->execute(); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.'); + + $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); + $this->assertEqual($person->name, 'Joe', 'Name set correctly.'); + $this->assertEqual($person->age, 30, 'Age skipped correctly.'); + $this->assertEqual($person->job, 'Speaker', 'Job set correctly.'); + } + + /** + * Confirms that we can merge-update a record successfully, with expressions. + */ + function testMergeUpdateExpression() { + $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + + $age_before = db_query('SELECT age FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetchField(); + + // This is a very contrived example, as I have no idea why you'd want to + // change age this way, but that's beside the point. + // Note that we are also double-setting age here, once as a literal and + // once as an expression. This test will only pass if the expression wins, + // which is what is supposed to happen. + db_merge('test_people') + ->key('job', 'Speaker') + ->fields(array('name' => 'Tiffany')) + ->insertFields(array('age' => 31)) + ->expression('age', 'age + :age', array(':age' => 4)) + ->execute(); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.'); + + $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); + $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.'); + $this->assertEqual($person->age, $age_before + 4, 'Age updated correctly.'); + $this->assertEqual($person->job, 'Speaker', 'Job set correctly.'); + } + + /** + * Tests that we can merge-insert without any update fields. + */ + function testMergeInsertWithoutUpdate() { + $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + + db_merge('test_people') + ->key('job', 'Presenter') + ->execute(); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + $this->assertEqual($num_records_before + 1, $num_records_after, 'Merge inserted properly.'); + + $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch(); + $this->assertEqual($person->name, '', 'Name set correctly.'); + $this->assertEqual($person->age, 0, 'Age set correctly.'); + $this->assertEqual($person->job, 'Presenter', 'Job set correctly.'); + } + + /** + * Confirms that we can merge-update without any update fields. + */ + function testMergeUpdateWithoutUpdate() { + $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + + db_merge('test_people') + ->key('job', 'Speaker') + ->execute(); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + $this->assertEqual($num_records_before, $num_records_after, 'Merge skipped properly.'); + + $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); + $this->assertEqual($person->name, 'Meredith', 'Name skipped correctly.'); + $this->assertEqual($person->age, 30, 'Age skipped correctly.'); + $this->assertEqual($person->job, 'Speaker', 'Job skipped correctly.'); + + db_merge('test_people') + ->key('job', 'Speaker') + ->insertFields(array('age' => 31)) + ->execute(); + + $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + $this->assertEqual($num_records_before, $num_records_after, 'Merge skipped properly.'); + + $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); + $this->assertEqual($person->name, 'Meredith', 'Name skipped correctly.'); + $this->assertEqual($person->age, 30, 'Age skipped correctly.'); + $this->assertEqual($person->job, 'Speaker', 'Job skipped correctly.'); + } + + /** + * Tests that an invalid merge query throws an exception. + */ + function testInvalidMerge() { + try { + // This query will fail because there is no key field specified. + // Normally it would throw an exception but we are suppressing it with + // the throw_exception option. + $options['throw_exception'] = FALSE; + db_merge('test_people', $options) + ->fields(array( + 'age' => 31, + 'name' => 'Tiffany', + )) + ->execute(); + $this->pass('$options[\'throw_exception\'] is FALSE, no InvalidMergeQueryException thrown.'); + } + catch (InvalidMergeQueryException $e) { + $this->fail('$options[\'throw_exception\'] is FALSE, but InvalidMergeQueryException thrown for invalid query.'); + return; + } + + try { + // This query will fail because there is no key field specified. + db_merge('test_people') + ->fields(array( + 'age' => 31, + 'name' => 'Tiffany', + )) + ->execute(); + } + catch (InvalidMergeQueryException $e) { + $this->pass('InvalidMergeQueryException thrown for invalid query.'); + return; + } + $this->fail('No InvalidMergeQueryException thrown'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/NextIdTest.php b/core/tests/Drupal/KernelTests/Core/Database/NextIdTest.php new file mode 100644 index 0000000..43754b0 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/NextIdTest.php @@ -0,0 +1,43 @@ +installSchema('system', 'sequences'); + } + + /** + * Tests that the sequences API works. + */ + function testDbNextId() { + $first = db_next_id(); + $second = db_next_id(); + // We can test for exact increase in here because we know there is no + // other process operating on these tables -- normally we could only + // expect $second > $first. + $this->assertEqual($first + 1, $second, 'The second call from a sequence provides a number increased by one.'); + $result = db_next_id(1000); + $this->assertEqual($result, 1001, 'Sequence provides a larger number than the existing ID.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/QueryTest.php b/core/tests/Drupal/KernelTests/Core/Database/QueryTest.php new file mode 100644 index 0000000..c56d2d5 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/QueryTest.php @@ -0,0 +1,153 @@ + array(25, 26, 27)))->fetchAll(); + $this->assertEqual(count($names), 3, 'Correct number of names returned'); + + $names = db_query('SELECT name FROM {test} WHERE age IN ( :ages[] ) ORDER BY age', array(':ages[]' => array(25)))->fetchAll(); + $this->assertEqual(count($names), 1, 'Correct number of names returned'); + } + + /** + * Tests that we can not pass a scalar value when an array is expected. + */ + function testScalarSubstitution() { + try { + $names = db_query('SELECT name FROM {test} WHERE age IN ( :ages[] ) ORDER BY age', array(':ages[]' => 25))->fetchAll(); + $this->fail('Array placeholder with scalar argument should result in an exception.'); + } + catch (\InvalidArgumentException $e) { + $this->pass('Array placeholder with scalar argument should result in an exception.'); + } + + } + + /** + * Tests SQL injection via database query array arguments. + */ + public function testArrayArgumentsSQLInjection() { + // Attempt SQL injection and verify that it does not work. + $condition = array( + "1 ;INSERT INTO {test} (name) VALUES ('test12345678'); -- " => '', + '1' => '', + ); + try { + db_query("SELECT * FROM {test} WHERE name = :name", array(':name' => $condition))->fetchObject(); + $this->fail('SQL injection attempt via array arguments should result in a database exception.'); + } + catch (\InvalidArgumentException $e) { + $this->pass('SQL injection attempt via array arguments should result in a database exception.'); + } + + // Test that the insert query that was used in the SQL injection attempt did + // not result in a row being inserted in the database. + $result = db_select('test') + ->condition('name', 'test12345678') + ->countQuery() + ->execute() + ->fetchField(); + $this->assertFalse($result, 'SQL injection attempt did not result in a row being inserted in the database table.'); + } + + /** + * Tests SQL injection via condition operator. + */ + public function testConditionOperatorArgumentsSQLInjection() { + $injection = "IS NOT NULL) ;INSERT INTO {test} (name) VALUES ('test12345678'); -- "; + + // Convert errors to exceptions for testing purposes below. + set_error_handler(function ($severity, $message, $filename, $lineno) { + throw new \ErrorException($message, 0, $severity, $filename, $lineno); + }); + try { + $result = db_select('test', 't') + ->fields('t') + ->condition('name', 1, $injection) + ->execute(); + $this->fail('Should not be able to attempt SQL injection via condition operator.'); + } + catch (\ErrorException $e) { + $this->pass('SQL injection attempt via condition arguments should result in a database exception.'); + } + + // Test that the insert query that was used in the SQL injection attempt did + // not result in a row being inserted in the database. + $result = db_select('test') + ->condition('name', 'test12345678') + ->countQuery() + ->execute() + ->fetchField(); + $this->assertFalse($result, 'SQL injection attempt did not result in a row being inserted in the database table.'); + + // Attempt SQLi via union query with no unsafe characters. + $this->enableModules(['user']); + $this->installEntitySchema('user'); + db_insert('test') + ->fields(['name' => '123456']) + ->execute(); + $injection = "= 1 UNION ALL SELECT password FROM user WHERE uid ="; + + try { + $result = db_select('test', 't') + ->fields('t', array('name', 'name')) + ->condition('name', 1, $injection) + ->execute(); + $this->fail('Should not be able to attempt SQL injection via operator.'); + } + catch (\ErrorException $e) { + $this->pass('SQL injection attempt via condition arguments should result in a database exception.'); + } + + // Attempt SQLi via union query - uppercase tablename. + db_insert('TEST_UPPERCASE') + ->fields(['name' => 'secrets']) + ->execute(); + $injection = "IS NOT NULL) UNION ALL SELECT name FROM {TEST_UPPERCASE} -- "; + + try { + $result = db_select('test', 't') + ->fields('t', array('name')) + ->condition('name', 1, $injection) + ->execute(); + $this->fail('Should not be able to attempt SQL injection via operator.'); + } + catch (\ErrorException $e) { + $this->pass('SQL injection attempt via condition arguments should result in a database exception.'); + } + restore_error_handler(); + } + + /** + * Tests numeric query parameter expansion in expressions. + * + * @see \Drupal\Core\Database\Driver\sqlite\Statement::getStatement() + * @see http://bugs.php.net/bug.php?id=45259 + */ + public function testNumericExpressionSubstitution() { + $count = db_query('SELECT COUNT(*) >= 3 FROM {test}')->fetchField(); + $this->assertEqual((bool) $count, TRUE); + + $count = db_query('SELECT COUNT(*) >= :count FROM {test}', array( + ':count' => 3, + ))->fetchField(); + $this->assertEqual((bool) $count, TRUE); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/RangeQueryTest.php b/core/tests/Drupal/KernelTests/Core/Database/RangeQueryTest.php new file mode 100644 index 0000000..13067f0 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/RangeQueryTest.php @@ -0,0 +1,37 @@ +fetchAll(); + $this->assertEqual(count($range_rows), 3, 'Range query work and return correct number of rows.'); + + // Test if return target data. + $raw_rows = db_query('SELECT name FROM {test} ORDER BY name')->fetchAll(); + $raw_rows = array_slice($raw_rows, 1, 3); + $this->assertEqual($range_rows, $raw_rows); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/RegressionTest.php b/core/tests/Drupal/KernelTests/Core/Database/RegressionTest.php new file mode 100644 index 0000000..c5d1753 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/RegressionTest.php @@ -0,0 +1,64 @@ +fields(array( + 'name' => $this->randomMachineName(), + 'age' => 20, + 'job' => $job, + ))->execute(); + + $from_database = db_query('SELECT job FROM {test} WHERE job = :job', array(':job' => $job))->fetchField(); + $this->assertIdentical($job, $from_database, 'The database handles UTF-8 characters cleanly.'); + } + + /** + * Tests the db_table_exists() function. + */ + function testDBTableExists() { + $this->assertIdentical(TRUE, db_table_exists('test'), 'Returns true for existent table.'); + $this->assertIdentical(FALSE, db_table_exists('nosuchtable'), 'Returns false for nonexistent table.'); + } + + /** + * Tests the db_field_exists() function. + */ + function testDBFieldExists() { + $this->assertIdentical(TRUE, db_field_exists('test', 'name'), 'Returns true for existent column.'); + $this->assertIdentical(FALSE, db_field_exists('test', 'nosuchcolumn'), 'Returns false for nonexistent column.'); + } + + /** + * Tests the db_index_exists() function. + */ + function testDBIndexExists() { + $this->assertIdentical(TRUE, db_index_exists('test', 'ages'), 'Returns true for existent index.'); + $this->assertIdentical(FALSE, db_index_exists('test', 'nosuchindex'), 'Returns false for nonexistent index.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php b/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php new file mode 100644 index 0000000..393dedf --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php @@ -0,0 +1,796 @@ + 'Schema table description may contain "quotes" and could be long—very long indeed.', + 'fields' => array( + 'id' => array( + 'type' => 'int', + 'default' => NULL, + ), + 'test_field' => array( + 'type' => 'int', + 'not null' => TRUE, + 'description' => 'Schema table description may contain "quotes" and could be long—very long indeed. There could be "multiple quoted regions".', + ), + 'test_field_string' => array( + 'type' => 'varchar', + 'length' => 20, + 'not null' => TRUE, + 'default' => "'\"funky default'\"", + 'description' => 'Schema column description for string.', + ), + 'test_field_string_ascii' => array( + 'type' => 'varchar_ascii', + 'length' => 255, + 'description' => 'Schema column description for ASCII string.', + ), + ), + ); + db_create_table('test_table', $table_specification); + + // Assert that the table exists. + $this->assertTrue(db_table_exists('test_table'), 'The table exists.'); + + // Assert that the table comment has been set. + $this->checkSchemaComment($table_specification['description'], 'test_table'); + + // Assert that the column comment has been set. + $this->checkSchemaComment($table_specification['fields']['test_field']['description'], 'test_table', 'test_field'); + + if (Database::getConnection()->databaseType() == 'mysql') { + // Make sure that varchar fields have the correct collation. + $columns = db_query('SHOW FULL COLUMNS FROM {test_table}'); + foreach ($columns as $column) { + if ($column->Field == 'test_field_string') { + $string_check = ($column->Collation == 'utf8mb4_general_ci'); + } + if ($column->Field == 'test_field_string_ascii') { + $string_ascii_check = ($column->Collation == 'ascii_general_ci'); + } + } + $this->assertTrue(!empty($string_check), 'string field has the right collation.'); + $this->assertTrue(!empty($string_ascii_check), 'ASCII string field has the right collation.'); + } + + // An insert without a value for the column 'test_table' should fail. + $this->assertFalse($this->tryInsert(), 'Insert without a default failed.'); + + // Add a default value to the column. + db_field_set_default('test_table', 'test_field', 0); + // The insert should now succeed. + $this->assertTrue($this->tryInsert(), 'Insert with a default succeeded.'); + + // Remove the default. + db_field_set_no_default('test_table', 'test_field'); + // The insert should fail again. + $this->assertFalse($this->tryInsert(), 'Insert without a default failed.'); + + // Test for fake index and test for the boolean result of indexExists(). + $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field'); + $this->assertIdentical($index_exists, FALSE, 'Fake index does not exists'); + // Add index. + db_add_index('test_table', 'test_field', array('test_field'), $table_specification); + // Test for created index and test for the boolean result of indexExists(). + $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field'); + $this->assertIdentical($index_exists, TRUE, 'Index created.'); + + // Rename the table. + db_rename_table('test_table', 'test_table2'); + + // Index should be renamed. + $index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field'); + $this->assertTrue($index_exists, 'Index was renamed.'); + + // We need the default so that we can insert after the rename. + db_field_set_default('test_table2', 'test_field', 0); + $this->assertFalse($this->tryInsert(), 'Insert into the old table failed.'); + $this->assertTrue($this->tryInsert('test_table2'), 'Insert into the new table succeeded.'); + + // We should have successfully inserted exactly two rows. + $count = db_query('SELECT COUNT(*) FROM {test_table2}')->fetchField(); + $this->assertEqual($count, 2, 'Two fields were successfully inserted.'); + + // Try to drop the table. + db_drop_table('test_table2'); + $this->assertFalse(db_table_exists('test_table2'), 'The dropped table does not exist.'); + + // Recreate the table. + db_create_table('test_table', $table_specification); + db_field_set_default('test_table', 'test_field', 0); + db_add_field('test_table', 'test_serial', array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Added column description.')); + + // Assert that the column comment has been set. + $this->checkSchemaComment('Added column description.', 'test_table', 'test_serial'); + + // Change the new field to a serial column. + db_change_field('test_table', 'test_serial', 'test_serial', array('type' => 'serial', 'not null' => TRUE, 'description' => 'Changed column description.'), array('primary key' => array('test_serial'))); + + // Assert that the column comment has been set. + $this->checkSchemaComment('Changed column description.', 'test_table', 'test_serial'); + + $this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.'); + $max1 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField(); + $this->assertTrue($this->tryInsert(), 'Insert with a serial succeeded.'); + $max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField(); + $this->assertTrue($max2 > $max1, 'The serial is monotone.'); + + $count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField(); + $this->assertEqual($count, 2, 'There were two rows.'); + + // Test renaming of keys and constraints. + db_drop_table('test_table'); + $table_specification = array( + 'fields' => array( + 'id' => array( + 'type' => 'serial', + 'not null' => TRUE, + ), + 'test_field' => array( + 'type' => 'int', + 'default' => 0, + ), + ), + 'primary key' => array('id'), + 'unique keys' => array( + 'test_field' => array('test_field'), + ), + ); + db_create_table('test_table', $table_specification); + + // Tests for indexes are Database specific. + $db_type = Database::getConnection()->databaseType(); + + // Test for existing primary and unique keys. + switch ($db_type) { + case 'pgsql': + $primary_key_exists = Database::getConnection()->schema()->constraintExists('test_table', '__pkey'); + $unique_key_exists = Database::getConnection()->schema()->constraintExists('test_table', 'test_field' . '__key'); + break; + case 'sqlite': + // SQLite does not create a standalone index for primary keys. + $primary_key_exists = TRUE; + $unique_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field'); + break; + default: + $primary_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'PRIMARY'); + $unique_key_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field'); + break; + } + $this->assertIdentical($primary_key_exists, TRUE, 'Primary key created.'); + $this->assertIdentical($unique_key_exists, TRUE, 'Unique key created.'); + + db_rename_table('test_table', 'test_table2'); + + // Test for renamed primary and unique keys. + switch ($db_type) { + case 'pgsql': + $renamed_primary_key_exists = Database::getConnection()->schema()->constraintExists('test_table2', '__pkey'); + $renamed_unique_key_exists = Database::getConnection()->schema()->constraintExists('test_table2', 'test_field' . '__key'); + break; + case 'sqlite': + // SQLite does not create a standalone index for primary keys. + $renamed_primary_key_exists = TRUE; + $renamed_unique_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field'); + break; + default: + $renamed_primary_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'PRIMARY'); + $renamed_unique_key_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field'); + break; + } + $this->assertIdentical($renamed_primary_key_exists, TRUE, 'Primary key was renamed.'); + $this->assertIdentical($renamed_unique_key_exists, TRUE, 'Unique key was renamed.'); + + // For PostgreSQL check in addition that sequence was renamed. + if ($db_type == 'pgsql') { + // Get information about new table. + $info = Database::getConnection()->schema()->queryTableInformation('test_table2'); + $sequence_name = Database::getConnection()->schema()->prefixNonTable('test_table2', 'id', 'seq'); + $this->assertEqual($sequence_name, current($info->sequences), 'Sequence was renamed.'); + } + + // Use database specific data type and ensure that table is created. + $table_specification = array( + 'description' => 'Schema table description.', + 'fields' => array( + 'timestamp' => array( + 'mysql_type' => 'timestamp', + 'pgsql_type' => 'timestamp', + 'sqlite_type' => 'datetime', + 'not null' => FALSE, + 'default' => NULL, + ), + ), + ); + try { + db_create_table('test_timestamp', $table_specification); + } + catch (\Exception $e) {} + $this->assertTrue(db_table_exists('test_timestamp'), 'Table with database specific datatype was created.'); + } + + /** + * Tests that indexes on string fields are limited to 191 characters on MySQL. + * + * @see \Drupal\Core\Database\Driver\mysql\Schema::getNormalizedIndexes() + */ + function testIndexLength() { + if (Database::getConnection()->databaseType() != 'mysql') { + return; + } + $table_specification = array( + 'fields' => array( + 'id' => array( + 'type' => 'int', + 'default' => NULL, + ), + 'test_field_text' => array( + 'type' => 'text', + 'not null' => TRUE, + ), + 'test_field_string_long' => array( + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + ), + 'test_field_string_ascii_long' => array( + 'type' => 'varchar_ascii', + 'length' => 255, + ), + 'test_field_string_short' => array( + 'type' => 'varchar', + 'length' => 128, + 'not null' => TRUE, + ), + ), + 'indexes' => array( + 'test_regular' => array( + 'test_field_text', + 'test_field_string_long', + 'test_field_string_ascii_long', + 'test_field_string_short', + ), + 'test_length' => array( + array('test_field_text', 128), + array('test_field_string_long', 128), + array('test_field_string_ascii_long', 128), + array('test_field_string_short', 128), + ), + 'test_mixed' => array( + array('test_field_text', 200), + 'test_field_string_long', + array('test_field_string_ascii_long', 200), + 'test_field_string_short', + ), + ), + ); + db_create_table('test_table_index_length', $table_specification); + + $schema_object = Database::getConnection()->schema(); + + // Ensure expected exception thrown when adding index with missing info. + $expected_exception_message = "MySQL needs the 'test_field_text' field specification in order to normalize the 'test_regular' index"; + $missing_field_spec = $table_specification; + unset($missing_field_spec['fields']['test_field_text']); + try { + $schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $missing_field_spec); + $this->fail('SchemaException not thrown when adding index with missing information.'); + } + catch (SchemaException $e) { + $this->assertEqual($expected_exception_message, $e->getMessage()); + } + + // Add a separate index. + $schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification); + $table_specification_with_new_index = $table_specification; + $table_specification_with_new_index['indexes']['test_separate'] = [['test_field_text', 200]]; + + // Ensure that the exceptions of addIndex are thrown as expected. + + try { + $schema_object->addIndex('test_table_index_length', 'test_separate', [['test_field_text', 200]], $table_specification); + $this->fail('\Drupal\Core\Database\SchemaObjectExistsException exception missed.'); + } + catch (SchemaObjectExistsException $e) { + $this->pass('\Drupal\Core\Database\SchemaObjectExistsException thrown when index already exists.'); + } + + try { + $schema_object->addIndex('test_table_non_existing', 'test_separate', [['test_field_text', 200]], $table_specification); + $this->fail('\Drupal\Core\Database\SchemaObjectDoesNotExistException exception missed.'); + } + catch (SchemaObjectDoesNotExistException $e) { + $this->pass('\Drupal\Core\Database\SchemaObjectDoesNotExistException thrown when index already exists.'); + } + + // Get index information. + $results = db_query('SHOW INDEX FROM {test_table_index_length}'); + $expected_lengths = array( + 'test_regular' => array( + 'test_field_text' => 191, + 'test_field_string_long' => 191, + 'test_field_string_ascii_long' => NULL, + 'test_field_string_short' => NULL, + ), + 'test_length' => array( + 'test_field_text' => 128, + 'test_field_string_long' => 128, + 'test_field_string_ascii_long' => 128, + 'test_field_string_short' => NULL, + ), + 'test_mixed' => array( + 'test_field_text' => 191, + 'test_field_string_long' => 191, + 'test_field_string_ascii_long' => 200, + 'test_field_string_short' => NULL, + ), + 'test_separate' => array( + 'test_field_text' => 191, + ), + ); + + // Count the number of columns defined in the indexes. + $column_count = 0; + foreach ($table_specification_with_new_index['indexes'] as $index) { + foreach ($index as $field) { + $column_count++; + } + } + $test_count = 0; + foreach ($results as $result) { + $this->assertEqual($result->Sub_part, $expected_lengths[$result->Key_name][$result->Column_name], 'Index length matches expected value.'); + $test_count++; + } + $this->assertEqual($test_count, $column_count, 'Number of tests matches expected value.'); + } + + /** + * Tests inserting data into an existing table. + * + * @param $table + * The database table to insert data into. + * + * @return + * TRUE if the insert succeeded, FALSE otherwise. + */ + function tryInsert($table = 'test_table') { + try { + db_insert($table) + ->fields(array('id' => mt_rand(10, 20))) + ->execute(); + return TRUE; + } + catch (\Exception $e) { + return FALSE; + } + } + + /** + * Checks that a table or column comment matches a given description. + * + * @param $description + * The asserted description. + * @param $table + * The table to test. + * @param $column + * Optional column to test. + */ + function checkSchemaComment($description, $table, $column = NULL) { + if (method_exists(Database::getConnection()->schema(), 'getComment')) { + $comment = Database::getConnection()->schema()->getComment($table, $column); + // The schema comment truncation for mysql is different. + if (Database::getConnection()->databaseType() == 'mysql') { + $max_length = $column ? 255 : 60; + $description = Unicode::truncate($description, $max_length, TRUE, TRUE); + } + $this->assertEqual($comment, $description, 'The comment matches the schema description.'); + } + } + + /** + * Tests creating unsigned columns and data integrity thereof. + */ + function testUnsignedColumns() { + // First create the table with just a serial column. + $table_name = 'unsigned_table'; + $table_spec = array( + 'fields' => array('serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE)), + 'primary key' => array('serial_column'), + ); + db_create_table($table_name, $table_spec); + + // Now set up columns for the other types. + $types = array('int', 'float', 'numeric'); + foreach ($types as $type) { + $column_spec = array('type' => $type, 'unsigned'=> TRUE); + if ($type == 'numeric') { + $column_spec += array('precision' => 10, 'scale' => 0); + } + $column_name = $type . '_column'; + $table_spec['fields'][$column_name] = $column_spec; + db_add_field($table_name, $column_name, $column_spec); + } + + // Finally, check each column and try to insert invalid values into them. + foreach ($table_spec['fields'] as $column_name => $column_spec) { + $this->assertTrue(db_field_exists($table_name, $column_name), format_string('Unsigned @type column was created.', array('@type' => $column_spec['type']))); + $this->assertFalse($this->tryUnsignedInsert($table_name, $column_name), format_string('Unsigned @type column rejected a negative value.', array('@type' => $column_spec['type']))); + } + } + + /** + * Tries to insert a negative value into columns defined as unsigned. + * + * @param $table_name + * The table to insert. + * @param $column_name + * The column to insert. + * + * @return + * TRUE if the insert succeeded, FALSE otherwise. + */ + function tryUnsignedInsert($table_name, $column_name) { + try { + db_insert($table_name) + ->fields(array($column_name => -1)) + ->execute(); + return TRUE; + } + catch (\Exception $e) { + return FALSE; + } + } + + /** + * Tests adding columns to an existing table. + */ + function testSchemaAddField() { + // Test varchar types. + foreach (array(1, 32, 128, 256, 512) as $length) { + $base_field_spec = array( + 'type' => 'varchar', + 'length' => $length, + ); + $variations = array( + array('not null' => FALSE), + array('not null' => FALSE, 'default' => '7'), + array('not null' => FALSE, 'default' => substr('"thing"', 0, $length)), + array('not null' => FALSE, 'default' => substr("\"'hing", 0, $length)), + array('not null' => TRUE, 'initial' => 'd'), + array('not null' => FALSE, 'default' => NULL), + array('not null' => TRUE, 'initial' => 'd', 'default' => '7'), + ); + + foreach ($variations as $variation) { + $field_spec = $variation + $base_field_spec; + $this->assertFieldAdditionRemoval($field_spec); + } + } + + // Test int and float types. + foreach (array('int', 'float') as $type) { + foreach (array('tiny', 'small', 'medium', 'normal', 'big') as $size) { + $base_field_spec = array( + 'type' => $type, + 'size' => $size, + ); + $variations = array( + array('not null' => FALSE), + array('not null' => FALSE, 'default' => 7), + array('not null' => TRUE, 'initial' => 1), + array('not null' => TRUE, 'initial' => 1, 'default' => 7), + ); + + foreach ($variations as $variation) { + $field_spec = $variation + $base_field_spec; + $this->assertFieldAdditionRemoval($field_spec); + } + } + } + + // Test numeric types. + foreach (array(1, 5, 10, 40, 65) as $precision) { + foreach (array(0, 2, 10, 30) as $scale) { + // Skip combinations where precision is smaller than scale. + if ($precision <= $scale) { + continue; + } + + $base_field_spec = array( + 'type' => 'numeric', + 'scale' => $scale, + 'precision' => $precision, + ); + $variations = array( + array('not null' => FALSE), + array('not null' => FALSE, 'default' => 7), + array('not null' => TRUE, 'initial' => 1), + array('not null' => TRUE, 'initial' => 1, 'default' => 7), + ); + + foreach ($variations as $variation) { + $field_spec = $variation + $base_field_spec; + $this->assertFieldAdditionRemoval($field_spec); + } + } + } + } + + /** + * Asserts that a given field can be added and removed from a table. + * + * The addition test covers both defining a field of a given specification + * when initially creating at table and extending an existing table. + * + * @param $field_spec + * The schema specification of the field. + */ + protected function assertFieldAdditionRemoval($field_spec) { + // Try creating the field on a new table. + $table_name = 'test_table_' . ($this->counter++); + $table_spec = array( + 'fields' => array( + 'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), + 'test_field' => $field_spec, + ), + 'primary key' => array('serial_column'), + ); + db_create_table($table_name, $table_spec); + $this->pass(format_string('Table %table created.', array('%table' => $table_name))); + + // Check the characteristics of the field. + $this->assertFieldCharacteristics($table_name, 'test_field', $field_spec); + + // Clean-up. + db_drop_table($table_name); + + // Try adding a field to an existing table. + $table_name = 'test_table_' . ($this->counter++); + $table_spec = array( + 'fields' => array( + 'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), + ), + 'primary key' => array('serial_column'), + ); + db_create_table($table_name, $table_spec); + $this->pass(format_string('Table %table created.', array('%table' => $table_name))); + + // Insert some rows to the table to test the handling of initial values. + for ($i = 0; $i < 3; $i++) { + db_insert($table_name) + ->useDefaults(array('serial_column')) + ->execute(); + } + + db_add_field($table_name, 'test_field', $field_spec); + $this->pass(format_string('Column %column created.', array('%column' => 'test_field'))); + + // Check the characteristics of the field. + $this->assertFieldCharacteristics($table_name, 'test_field', $field_spec); + + // Clean-up. + db_drop_field($table_name, 'test_field'); + + // Add back the field and then try to delete a field which is also a primary + // key. + db_add_field($table_name, 'test_field', $field_spec); + db_drop_field($table_name, 'serial_column'); + db_drop_table($table_name); + } + + /** + * Asserts that a newly added field has the correct characteristics. + */ + protected function assertFieldCharacteristics($table_name, $field_name, $field_spec) { + // Check that the initial value has been registered. + if (isset($field_spec['initial'])) { + // There should be no row with a value different then $field_spec['initial']. + $count = db_select($table_name) + ->fields($table_name, array('serial_column')) + ->condition($field_name, $field_spec['initial'], '<>') + ->countQuery() + ->execute() + ->fetchField(); + $this->assertEqual($count, 0, 'Initial values filled out.'); + } + + // Check that the default value has been registered. + if (isset($field_spec['default'])) { + // Try inserting a row, and check the resulting value of the new column. + $id = db_insert($table_name) + ->useDefaults(array('serial_column')) + ->execute(); + $field_value = db_select($table_name) + ->fields($table_name, array($field_name)) + ->condition('serial_column', $id) + ->execute() + ->fetchField(); + $this->assertEqual($field_value, $field_spec['default'], 'Default value registered.'); + } + } + + /** + * Tests changing columns between types. + */ + function testSchemaChangeField() { + $field_specs = array( + array('type' => 'int', 'size' => 'normal', 'not null' => FALSE), + array('type' => 'int', 'size' => 'normal', 'not null' => TRUE, 'initial' => 1, 'default' => 17), + array('type' => 'float', 'size' => 'normal', 'not null' => FALSE), + array('type' => 'float', 'size' => 'normal', 'not null' => TRUE, 'initial' => 1, 'default' => 7.3), + array('type' => 'numeric', 'scale' => 2, 'precision' => 10, 'not null' => FALSE), + array('type' => 'numeric', 'scale' => 2, 'precision' => 10, 'not null' => TRUE, 'initial' => 1, 'default' => 7), + ); + + foreach ($field_specs as $i => $old_spec) { + foreach ($field_specs as $j => $new_spec) { + if ($i === $j) { + // Do not change a field into itself. + continue; + } + $this->assertFieldChange($old_spec, $new_spec); + } + } + + $field_specs = array( + array('type' => 'varchar_ascii', 'length' => '255'), + array('type' => 'varchar', 'length' => '255'), + array('type' => 'text'), + array('type' => 'blob', 'size' => 'big'), + ); + + foreach ($field_specs as $i => $old_spec) { + foreach ($field_specs as $j => $new_spec) { + if ($i === $j) { + // Do not change a field into itself. + continue; + } + // Note if the serialized data contained an object this would fail on + // Postgres. + // @see https://www.drupal.org/node/1031122 + $this->assertFieldChange($old_spec, $new_spec, serialize(['string' => "This \n has \\\\ some backslash \"*string action.\\n"])); + } + } + + } + + /** + * Asserts that a field can be changed from one spec to another. + * + * @param $old_spec + * The beginning field specification. + * @param $new_spec + * The ending field specification. + */ + protected function assertFieldChange($old_spec, $new_spec, $test_data = NULL) { + $table_name = 'test_table_' . ($this->counter++); + $table_spec = array( + 'fields' => array( + 'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), + 'test_field' => $old_spec, + ), + 'primary key' => array('serial_column'), + ); + db_create_table($table_name, $table_spec); + $this->pass(format_string('Table %table created.', array('%table' => $table_name))); + + // Check the characteristics of the field. + $this->assertFieldCharacteristics($table_name, 'test_field', $old_spec); + + // Remove inserted rows. + db_truncate($table_name)->execute(); + + if ($test_data) { + $id = db_insert($table_name) + ->fields(['test_field'], [$test_data]) + ->execute(); + } + + // Change the field. + db_change_field($table_name, 'test_field', 'test_field', $new_spec); + + if ($test_data) { + $field_value = db_select($table_name) + ->fields($table_name, ['test_field']) + ->condition('serial_column', $id) + ->execute() + ->fetchField(); + $this->assertIdentical($field_value, $test_data); + } + + // Check the field was changed. + $this->assertFieldCharacteristics($table_name, 'test_field', $new_spec); + + // Clean-up. + db_drop_table($table_name); + } + + /** + * Tests the findTables() method. + */ + public function testFindTables() { + // We will be testing with three tables, two of them using the default + // prefix and the third one with an individually specified prefix. + + // Set up a new connection with different connection info. + $connection_info = Database::getConnectionInfo(); + + // Add per-table prefix to the second table. + $new_connection_info = $connection_info['default']; + $new_connection_info['prefix']['test_2_table'] = $new_connection_info['prefix']['default'] . '_shared_'; + Database::addConnectionInfo('test', 'default', $new_connection_info); + + Database::setActiveConnection('test'); + + // Create the tables. + $table_specification = [ + 'description' => 'Test table.', + 'fields' => [ + 'id' => [ + 'type' => 'int', + 'default' => NULL, + ], + ], + ]; + Database::getConnection()->schema()->createTable('test_1_table', $table_specification); + Database::getConnection()->schema()->createTable('test_2_table', $table_specification); + Database::getConnection()->schema()->createTable('the_third_table', $table_specification); + + // Check the "all tables" syntax. + $tables = Database::getConnection()->schema()->findTables('%'); + sort($tables); + $expected = [ + // The 'config' table is added by + // \Drupal\KernelTests\KernelTestBase::containerBuild(). + 'config', + 'test_1_table', + // This table uses a per-table prefix, yet it is returned as un-prefixed. + 'test_2_table', + 'the_third_table', + ]; + $this->assertEqual($tables, $expected, 'All tables were found.'); + + // Check the restrictive syntax. + $tables = Database::getConnection()->schema()->findTables('test_%'); + sort($tables); + $expected = [ + 'test_1_table', + 'test_2_table', + ]; + $this->assertEqual($tables, $expected, 'Two tables were found.'); + + // Go back to the initial connection. + Database::setActiveConnection('default'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/SelectCloneTest.php b/core/tests/Drupal/KernelTests/Core/Database/SelectCloneTest.php new file mode 100644 index 0000000..3e57ff5 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/SelectCloneTest.php @@ -0,0 +1,41 @@ +addField('t', 'id', 'id'); + $subquery->condition('age', 28, '<'); + + $query = db_select('test', 't'); + $query->addField('t', 'name', 'name'); + $query->condition('id', $subquery, 'IN'); + + $clone = clone $query; + // Cloned query should not be altered by the following modification + // happening on original query. + $subquery->condition('age', 25, '>'); + + $clone_result = $clone->countQuery()->execute()->fetchField(); + $query_result = $query->countQuery()->execute()->fetchField(); + + // Make sure the cloned query has not been modified + $this->assertEqual(3, $clone_result, 'The cloned query returns the expected number of rows'); + $this->assertEqual(2, $query_result, 'The query returns the expected number of rows'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/SelectComplexTest.php b/core/tests/Drupal/KernelTests/Core/Database/SelectComplexTest.php new file mode 100644 index 0000000..17ad747 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/SelectComplexTest.php @@ -0,0 +1,383 @@ +join('test', 'p', 't.pid = p.id'); + $name_field = $query->addField($people_alias, 'name', 'name'); + $query->addField('t', 'task', 'task'); + $priority_field = $query->addField('t', 'priority', 'priority'); + + $query->orderBy($priority_field); + $result = $query->execute(); + + $num_records = 0; + $last_priority = 0; + foreach ($result as $record) { + $num_records++; + $this->assertTrue($record->$priority_field >= $last_priority, 'Results returned in correct order.'); + $this->assertNotEqual($record->$name_field, 'Ringo', 'Taskless person not selected.'); + $last_priority = $record->$priority_field; + } + + $this->assertEqual($num_records, 7, 'Returned the correct number of rows.'); + } + + /** + * Tests LEFT OUTER joins. + */ + function testLeftOuterJoin() { + $query = db_select('test', 'p'); + $people_alias = $query->leftJoin('test_task', 't', 't.pid = p.id'); + $name_field = $query->addField('p', 'name', 'name'); + $query->addField($people_alias, 'task', 'task'); + $query->addField($people_alias, 'priority', 'priority'); + + $query->orderBy($name_field); + $result = $query->execute(); + + $num_records = 0; + $last_name = 0; + + foreach ($result as $record) { + $num_records++; + $this->assertTrue(strcmp($record->$name_field, $last_name) >= 0, 'Results returned in correct order.'); + } + + $this->assertEqual($num_records, 8, 'Returned the correct number of rows.'); + } + + /** + * Tests GROUP BY clauses. + */ + function testGroupBy() { + $query = db_select('test_task', 't'); + $count_field = $query->addExpression('COUNT(task)', 'num'); + $task_field = $query->addField('t', 'task'); + $query->orderBy($count_field); + $query->groupBy($task_field); + $result = $query->execute(); + + $num_records = 0; + $last_count = 0; + $records = array(); + foreach ($result as $record) { + $num_records++; + $this->assertTrue($record->$count_field >= $last_count, 'Results returned in correct order.'); + $last_count = $record->$count_field; + $records[$record->$task_field] = $record->$count_field; + } + + $correct_results = array( + 'eat' => 1, + 'sleep' => 2, + 'code' => 1, + 'found new band' => 1, + 'perform at superbowl' => 1, + ); + + foreach ($correct_results as $task => $count) { + $this->assertEqual($records[$task], $count, format_string("Correct number of '@task' records found.", array('@task' => $task))); + } + + $this->assertEqual($num_records, 6, 'Returned the correct number of total rows.'); + } + + /** + * Tests GROUP BY and HAVING clauses together. + */ + function testGroupByAndHaving() { + $query = db_select('test_task', 't'); + $count_field = $query->addExpression('COUNT(task)', 'num'); + $task_field = $query->addField('t', 'task'); + $query->orderBy($count_field); + $query->groupBy($task_field); + $query->having('COUNT(task) >= 2'); + $result = $query->execute(); + + $num_records = 0; + $last_count = 0; + $records = array(); + foreach ($result as $record) { + $num_records++; + $this->assertTrue($record->$count_field >= 2, 'Record has the minimum count.'); + $this->assertTrue($record->$count_field >= $last_count, 'Results returned in correct order.'); + $last_count = $record->$count_field; + $records[$record->$task_field] = $record->$count_field; + } + + $correct_results = array( + 'sleep' => 2, + ); + + foreach ($correct_results as $task => $count) { + $this->assertEqual($records[$task], $count, format_string("Correct number of '@task' records found.", array('@task' => $task))); + } + + $this->assertEqual($num_records, 1, 'Returned the correct number of total rows.'); + } + + /** + * Tests range queries. + * + * The SQL clause varies with the database. + */ + function testRange() { + $query = db_select('test'); + $query->addField('test', 'name'); + $query->addField('test', 'age', 'age'); + $query->range(0, 2); + $query_result = $query->countQuery()->execute()->fetchField(); + + $this->assertEqual($query_result, 2, 'Returned the correct number of rows.'); + } + + /** + * Tests distinct queries. + */ + function testDistinct() { + $query = db_select('test_task'); + $query->addField('test_task', 'task'); + $query->distinct(); + $query_result = $query->countQuery()->execute()->fetchField(); + + $this->assertEqual($query_result, 6, 'Returned the correct number of rows.'); + } + + /** + * Tests that we can generate a count query from a built query. + */ + function testCountQuery() { + $query = db_select('test'); + $name_field = $query->addField('test', 'name'); + $age_field = $query->addField('test', 'age', 'age'); + $query->orderBy('name'); + + $count = $query->countQuery()->execute()->fetchField(); + + $this->assertEqual($count, 4, 'Counted the correct number of records.'); + + // Now make sure we didn't break the original query! We should still have + // all of the fields we asked for. + $record = $query->execute()->fetch(); + $this->assertEqual($record->$name_field, 'George', 'Correct data retrieved.'); + $this->assertEqual($record->$age_field, 27, 'Correct data retrieved.'); + } + + /** + * Tests having queries. + */ + function testHavingCountQuery() { + $query = db_select('test') + ->extend('Drupal\Core\Database\Query\PagerSelectExtender') + ->groupBy('age') + ->having('age + 1 > 0'); + $query->addField('test', 'age'); + $query->addExpression('age + 1'); + $count = count($query->execute()->fetchCol()); + $this->assertEqual($count, 4, 'Counted the correct number of records.'); + } + + /** + * Tests that countQuery removes 'all_fields' statements and ordering clauses. + */ + function testCountQueryRemovals() { + $query = db_select('test'); + $query->fields('test'); + $query->orderBy('name'); + $count = $query->countQuery(); + + // Check that the 'all_fields' statement is handled properly. + $tables = $query->getTables(); + $this->assertEqual($tables['test']['all_fields'], 1, 'Query correctly sets \'all_fields\' statement.'); + $tables = $count->getTables(); + $this->assertFalse(isset($tables['test']['all_fields']), 'Count query correctly unsets \'all_fields\' statement.'); + + // Check that the ordering clause is handled properly. + $orderby = $query->getOrderBy(); + // The orderby string is different for PostgreSQL. + // @see Drupal\Core\Database\Driver\pgsql\Select::orderBy() + $db_type = Database::getConnection()->databaseType(); + $this->assertEqual($orderby['name'], ($db_type == 'pgsql' ? 'ASC NULLS FIRST' : 'ASC'), 'Query correctly sets ordering clause.'); + $orderby = $count->getOrderBy(); + $this->assertFalse(isset($orderby['name']), 'Count query correctly unsets ordering clause.'); + + // Make sure that the count query works. + $count = $count->execute()->fetchField(); + + $this->assertEqual($count, 4, 'Counted the correct number of records.'); + } + + + /** + * Tests that countQuery properly removes fields and expressions. + */ + function testCountQueryFieldRemovals() { + // countQuery should remove all fields and expressions, so this can be + // tested by adding a non-existent field and expression: if it ends + // up in the query, an error will be thrown. If not, it will return the + // number of records, which in this case happens to be 4 (there are four + // records in the {test} table). + $query = db_select('test'); + $query->fields('test', array('fail')); + $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), 'Count Query removed fields'); + + $query = db_select('test'); + $query->addExpression('fail'); + $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), 'Count Query removed expressions'); + } + + /** + * Tests that we can generate a count query from a query with distinct. + */ + function testCountQueryDistinct() { + $query = db_select('test_task'); + $query->addField('test_task', 'task'); + $query->distinct(); + + $count = $query->countQuery()->execute()->fetchField(); + + $this->assertEqual($count, 6, 'Counted the correct number of records.'); + } + + /** + * Tests that we can generate a count query from a query with GROUP BY. + */ + function testCountQueryGroupBy() { + $query = db_select('test_task'); + $query->addField('test_task', 'pid'); + $query->groupBy('pid'); + + $count = $query->countQuery()->execute()->fetchField(); + + $this->assertEqual($count, 3, 'Counted the correct number of records.'); + + // Use a column alias as, without one, the query can succeed for the wrong + // reason. + $query = db_select('test_task'); + $query->addField('test_task', 'pid', 'pid_alias'); + $query->addExpression('COUNT(test_task.task)', 'count'); + $query->groupBy('pid_alias'); + $query->orderBy('pid_alias', 'asc'); + + $count = $query->countQuery()->execute()->fetchField(); + + $this->assertEqual($count, 3, 'Counted the correct number of records.'); + } + + /** + * Confirms that we can properly nest conditional clauses. + */ + function testNestedConditions() { + // This query should translate to: + // "SELECT job FROM {test} WHERE name = 'Paul' AND (age = 26 OR age = 27)" + // That should find only one record. Yes it's a non-optimal way of writing + // that query but that's not the point! + $query = db_select('test'); + $query->addField('test', 'job'); + $query->condition('name', 'Paul'); + $query->condition(db_or()->condition('age', 26)->condition('age', 27)); + + $job = $query->execute()->fetchField(); + $this->assertEqual($job, 'Songwriter', 'Correct data retrieved.'); + } + + /** + * Confirms we can join on a single table twice with a dynamic alias. + */ + function testJoinTwice() { + $query = db_select('test')->fields('test'); + $alias = $query->join('test', 'test', 'test.job = %alias.job'); + $query->addField($alias, 'name', 'othername'); + $query->addField($alias, 'job', 'otherjob'); + $query->where("$alias.name <> test.name"); + $crowded_job = $query->execute()->fetch(); + $this->assertEqual($crowded_job->job, $crowded_job->otherjob, 'Correctly joined same table twice.'); + $this->assertNotEqual($crowded_job->name, $crowded_job->othername, 'Correctly joined same table twice.'); + } + + /** + * Tests that we can join on a query. + */ + function testJoinSubquery() { + $this->installSchema('system', 'sequences'); + + $account = User::create([ + 'name' => $this->randomMachineName(), + 'mail' => $this->randomMachineName() . '@example.com', + ]); + + $query = db_select('test_task', 'tt', array('target' => 'replica')); + $query->addExpression('tt.pid + 1', 'abc'); + $query->condition('priority', 1, '>'); + $query->condition('priority', 100, '<'); + + $subquery = db_select('test', 'tp'); + $subquery->join('test_one_blob', 'tpb', 'tp.id = tpb.id'); + $subquery->join('node', 'n', 'tp.id = n.nid'); + $subquery->addTag('node_access'); + $subquery->addMetaData('account', $account); + $subquery->addField('tp', 'id'); + $subquery->condition('age', 5, '>'); + $subquery->condition('age', 500, '<'); + + $query->leftJoin($subquery, 'sq', 'tt.pid = sq.id'); + $query->join('test_one_blob', 'tb3', 'tt.pid = tb3.id'); + + // Construct the query string. + // This is the same sequence that SelectQuery::execute() goes through. + $query->preExecute(); + $query->getArguments(); + $str = (string) $query; + + // Verify that the string only has one copy of condition placeholder 0. + $pos = strpos($str, 'db_condition_placeholder_0', 0); + $pos2 = strpos($str, 'db_condition_placeholder_0', $pos + 1); + $this->assertFalse($pos2, 'Condition placeholder is not repeated.'); + } + + /** + * Tests that rowCount() throws exception on SELECT query. + */ + function testSelectWithRowCount() { + $query = db_select('test'); + $query->addField('test', 'name'); + $result = $query->execute(); + try { + $result->rowCount(); + $exception = FALSE; + } + catch (RowCountException $e) { + $exception = TRUE; + } + $this->assertTrue($exception, 'Exception was thrown'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/SelectOrderedTest.php b/core/tests/Drupal/KernelTests/Core/Database/SelectOrderedTest.php new file mode 100644 index 0000000..6715b95 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/SelectOrderedTest.php @@ -0,0 +1,89 @@ +addField('test', 'name'); + $age_field = $query->addField('test', 'age', 'age'); + $query->orderBy($age_field); + $result = $query->execute(); + + $num_records = 0; + $last_age = 0; + foreach ($result as $record) { + $num_records++; + $this->assertTrue($record->age >= $last_age, 'Results returned in correct order.'); + $last_age = $record->age; + } + + $this->assertEqual($num_records, 4, 'Returned the correct number of rows.'); + } + + /** + * Tests multiple ORDER BY. + */ + function testSimpleSelectMultiOrdered() { + $query = db_select('test'); + $query->addField('test', 'name'); + $age_field = $query->addField('test', 'age', 'age'); + $job_field = $query->addField('test', 'job'); + $query->orderBy($job_field); + $query->orderBy($age_field); + $result = $query->execute(); + + $num_records = 0; + $expected = array( + array('Ringo', 28, 'Drummer'), + array('John', 25, 'Singer'), + array('George', 27, 'Singer'), + array('Paul', 26, 'Songwriter'), + ); + $results = $result->fetchAll(\PDO::FETCH_NUM); + foreach ($expected as $k => $record) { + $num_records++; + foreach ($record as $kk => $col) { + if ($expected[$k][$kk] != $results[$k][$kk]) { + $this->assertTrue(FALSE, 'Results returned in correct order.'); + } + } + } + $this->assertEqual($num_records, 4, 'Returned the correct number of rows.'); + } + + /** + * Tests ORDER BY descending. + */ + function testSimpleSelectOrderedDesc() { + $query = db_select('test'); + $query->addField('test', 'name'); + $age_field = $query->addField('test', 'age', 'age'); + $query->orderBy($age_field, 'DESC'); + $result = $query->execute(); + + $num_records = 0; + $last_age = 100000000; + foreach ($result as $record) { + $num_records++; + $this->assertTrue($record->age <= $last_age, 'Results returned in correct order.'); + $last_age = $record->age; + } + + $this->assertEqual($num_records, 4, 'Returned the correct number of rows.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/SelectSubqueryTest.php b/core/tests/Drupal/KernelTests/Core/Database/SelectSubqueryTest.php new file mode 100644 index 0000000..d81e7c5 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/SelectSubqueryTest.php @@ -0,0 +1,183 @@ +addField('tt', 'pid', 'pid'); + $subquery->addField('tt', 'task', 'task'); + $subquery->condition('priority', 1); + + for ($i = 0; $i < 2; $i++) { + // Create another query that joins against the virtual table resulting + // from the subquery. + $select = db_select($subquery, 'tt2'); + $select->join('test', 't', 't.id=tt2.pid'); + $select->addField('t', 'name'); + if ($i) { + // Use a different number of conditions here to confuse the subquery + // placeholder counter, testing https://www.drupal.org/node/1112854. + $select->condition('name', 'John'); + } + $select->condition('task', 'code'); + + // The resulting query should be equivalent to: + // SELECT t.name + // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt WHERE priority=1) tt + // INNER JOIN test t ON t.id=tt.pid + // WHERE tt.task = 'code' + $people = $select->execute()->fetchCol(); + + $this->assertEqual(count($people), 1, 'Returned the correct number of rows.'); + } + } + + /** + * Tests that we can use a subquery in a FROM clause with a LIMIT. + */ + function testFromSubquerySelectWithLimit() { + // Create a subquery, which is just a normal query object. + $subquery = db_select('test_task', 'tt'); + $subquery->addField('tt', 'pid', 'pid'); + $subquery->addField('tt', 'task', 'task'); + $subquery->orderBy('priority', 'DESC'); + $subquery->range(0, 1); + + // Create another query that joins against the virtual table resulting + // from the subquery. + $select = db_select($subquery, 'tt2'); + $select->join('test', 't', 't.id=tt2.pid'); + $select->addField('t', 'name'); + + // The resulting query should be equivalent to: + // SELECT t.name + // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt ORDER BY priority DESC LIMIT 1 OFFSET 0) tt + // INNER JOIN test t ON t.id=tt.pid + $people = $select->execute()->fetchCol(); + + $this->assertEqual(count($people), 1, 'Returned the correct number of rows.'); + } + + /** + * Tests that we can use a subquery in a WHERE clause. + */ + function testConditionSubquerySelect() { + // Create a subquery, which is just a normal query object. + $subquery = db_select('test_task', 'tt'); + $subquery->addField('tt', 'pid', 'pid'); + $subquery->condition('tt.priority', 1); + + // Create another query that joins against the virtual table resulting + // from the subquery. + $select = db_select('test_task', 'tt2'); + $select->addField('tt2', 'task'); + $select->condition('tt2.pid', $subquery, 'IN'); + + // The resulting query should be equivalent to: + // SELECT tt2.name + // FROM test tt2 + // WHERE tt2.pid IN (SELECT tt.pid AS pid FROM test_task tt WHERE tt.priority=1) + $people = $select->execute()->fetchCol(); + $this->assertEqual(count($people), 5, 'Returned the correct number of rows.'); + } + + /** + * Tests that we can use a subquery in a JOIN clause. + */ + function testJoinSubquerySelect() { + // Create a subquery, which is just a normal query object. + $subquery = db_select('test_task', 'tt'); + $subquery->addField('tt', 'pid', 'pid'); + $subquery->condition('priority', 1); + + // Create another query that joins against the virtual table resulting + // from the subquery. + $select = db_select('test', 't'); + $select->join($subquery, 'tt', 't.id=tt.pid'); + $select->addField('t', 'name'); + + // The resulting query should be equivalent to: + // SELECT t.name + // FROM test t + // INNER JOIN (SELECT tt.pid AS pid FROM test_task tt WHERE priority=1) tt ON t.id=tt.pid + $people = $select->execute()->fetchCol(); + + $this->assertEqual(count($people), 2, 'Returned the correct number of rows.'); + } + + /** + * Tests EXISTS subquery conditionals on SELECT statements. + * + * We essentially select all rows from the {test} table that have matching + * rows in the {test_people} table based on the shared name column. + */ + function testExistsSubquerySelect() { + // Put George into {test_people}. + db_insert('test_people') + ->fields(array( + 'name' => 'George', + 'age' => 27, + 'job' => 'Singer', + )) + ->execute(); + // Base query to {test}. + $query = db_select('test', 't') + ->fields('t', array('name')); + // Subquery to {test_people}. + $subquery = db_select('test_people', 'tp') + ->fields('tp', array('name')) + ->where('tp.name = t.name'); + $query->exists($subquery); + $result = $query->execute(); + + // Ensure that we got the right record. + $record = $result->fetch(); + $this->assertEqual($record->name, 'George', 'Fetched name is correct using EXISTS query.'); + } + + /** + * Tests NOT EXISTS subquery conditionals on SELECT statements. + * + * We essentially select all rows from the {test} table that don't have + * matching rows in the {test_people} table based on the shared name column. + */ + function testNotExistsSubquerySelect() { + // Put George into {test_people}. + db_insert('test_people') + ->fields(array( + 'name' => 'George', + 'age' => 27, + 'job' => 'Singer', + )) + ->execute(); + + // Base query to {test}. + $query = db_select('test', 't') + ->fields('t', array('name')); + // Subquery to {test_people}. + $subquery = db_select('test_people', 'tp') + ->fields('tp', array('name')) + ->where('tp.name = t.name'); + $query->notExists($subquery); + + // Ensure that we got the right number of records. + $people = $query->execute()->fetchCol(); + $this->assertEqual(count($people), 3, 'NOT EXISTS query returned the correct results.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/SelectTest.php b/core/tests/Drupal/KernelTests/Core/Database/SelectTest.php new file mode 100644 index 0000000..abd363d --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/SelectTest.php @@ -0,0 +1,535 @@ +addField('test', 'name'); + $query->addField('test', 'age', 'age'); + $num_records = $query->countQuery()->execute()->fetchField(); + + $this->assertEqual($num_records, 4, 'Returned the correct number of rows.'); + } + + /** + * Tests rudimentary SELECT statement with a COMMENT. + */ + function testSimpleComment() { + $query = db_select('test')->comment('Testing query comments'); + $query->addField('test', 'name'); + $query->addField('test', 'age', 'age'); + $result = $query->execute(); + + $records = $result->fetchAll(); + + $query = (string) $query; + $expected = "/* Testing query comments */"; + + $this->assertEqual(count($records), 4, 'Returned the correct number of rows.'); + $this->assertNotIdentical(FALSE, strpos($query, $expected), 'The flattened query contains the comment string.'); + } + + /** + * Tests query COMMENT system against vulnerabilities. + */ + function testVulnerableComment() { + $query = db_select('test')->comment('Testing query comments */ SELECT nid FROM {node}; --'); + $query->addField('test', 'name'); + $query->addField('test', 'age', 'age'); + $result = $query->execute(); + + $records = $result->fetchAll(); + + $query = (string) $query; + $expected = "/* Testing query comments * / SELECT nid FROM {node}. -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test"; + + $this->assertEqual(count($records), 4, 'Returned the correct number of rows.'); + $this->assertNotIdentical(FALSE, strpos($query, $expected), 'The flattened query contains the sanitised comment string.'); + + $connection = Database::getConnection(); + foreach ($this->makeCommentsProvider() as $test_set) { + list($expected, $comments) = $test_set; + $this->assertEqual($expected, $connection->makeComment($comments)); + } + } + + /** + * Provides expected and input values for testVulnerableComment(). + */ + function makeCommentsProvider() { + return [ + [ + '/* */ ', + [''], + ], + // Try and close the comment early. + [ + '/* Exploit * / DROP TABLE node. -- */ ', + ['Exploit */ DROP TABLE node; --'], + ], + // Variations on comment closing. + [ + '/* Exploit * / * / DROP TABLE node. -- */ ', + ['Exploit */*/ DROP TABLE node; --'], + ], + [ + '/* Exploit * * // DROP TABLE node. -- */ ', + ['Exploit **// DROP TABLE node; --'], + ], + // Try closing the comment in the second string which is appended. + [ + '/* Exploit * / DROP TABLE node. --. Another try * / DROP TABLE node. -- */ ', + ['Exploit */ DROP TABLE node; --', 'Another try */ DROP TABLE node; --'], + ], + ]; + } + + /** + * Tests basic conditionals on SELECT statements. + */ + function testSimpleSelectConditional() { + $query = db_select('test'); + $name_field = $query->addField('test', 'name'); + $age_field = $query->addField('test', 'age', 'age'); + $query->condition('age', 27); + $result = $query->execute(); + + // Check that the aliases are being created the way we want. + $this->assertEqual($name_field, 'name', 'Name field alias is correct.'); + $this->assertEqual($age_field, 'age', 'Age field alias is correct.'); + + // Ensure that we got the right record. + $record = $result->fetch(); + $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.'); + $this->assertEqual($record->$age_field, 27, 'Fetched age is correct.'); + } + + /** + * Tests SELECT statements with expressions. + */ + function testSimpleSelectExpression() { + $query = db_select('test'); + $name_field = $query->addField('test', 'name'); + $age_field = $query->addExpression("age*2", 'double_age'); + $query->condition('age', 27); + $result = $query->execute(); + + // Check that the aliases are being created the way we want. + $this->assertEqual($name_field, 'name', 'Name field alias is correct.'); + $this->assertEqual($age_field, 'double_age', 'Age field alias is correct.'); + + // Ensure that we got the right record. + $record = $result->fetch(); + $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.'); + $this->assertEqual($record->$age_field, 27*2, 'Fetched age expression is correct.'); + } + + /** + * Tests SELECT statements with multiple expressions. + */ + function testSimpleSelectExpressionMultiple() { + $query = db_select('test'); + $name_field = $query->addField('test', 'name'); + $age_double_field = $query->addExpression("age*2"); + $age_triple_field = $query->addExpression("age*3"); + $query->condition('age', 27); + $result = $query->execute(); + + // Check that the aliases are being created the way we want. + $this->assertEqual($age_double_field, 'expression', 'Double age field alias is correct.'); + $this->assertEqual($age_triple_field, 'expression_2', 'Triple age field alias is correct.'); + + // Ensure that we got the right record. + $record = $result->fetch(); + $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.'); + $this->assertEqual($record->$age_double_field, 27*2, 'Fetched double age expression is correct.'); + $this->assertEqual($record->$age_triple_field, 27*3, 'Fetched triple age expression is correct.'); + } + + /** + * Tests adding multiple fields to a SELECT statement at the same time. + */ + function testSimpleSelectMultipleFields() { + $record = db_select('test') + ->fields('test', array('id', 'name', 'age', 'job')) + ->condition('age', 27) + ->execute()->fetchObject(); + + // Check that all fields we asked for are present. + $this->assertNotNull($record->id, 'ID field is present.'); + $this->assertNotNull($record->name, 'Name field is present.'); + $this->assertNotNull($record->age, 'Age field is present.'); + $this->assertNotNull($record->job, 'Job field is present.'); + + // Ensure that we got the right record. + // Check that all fields we asked for are present. + $this->assertEqual($record->id, 2, 'ID field has the correct value.'); + $this->assertEqual($record->name, 'George', 'Name field has the correct value.'); + $this->assertEqual($record->age, 27, 'Age field has the correct value.'); + $this->assertEqual($record->job, 'Singer', 'Job field has the correct value.'); + } + + /** + * Tests adding all fields from a given table to a SELECT statement. + */ + function testSimpleSelectAllFields() { + $record = db_select('test') + ->fields('test') + ->condition('age', 27) + ->execute()->fetchObject(); + + // Check that all fields we asked for are present. + $this->assertNotNull($record->id, 'ID field is present.'); + $this->assertNotNull($record->name, 'Name field is present.'); + $this->assertNotNull($record->age, 'Age field is present.'); + $this->assertNotNull($record->job, 'Job field is present.'); + + // Ensure that we got the right record. + // Check that all fields we asked for are present. + $this->assertEqual($record->id, 2, 'ID field has the correct value.'); + $this->assertEqual($record->name, 'George', 'Name field has the correct value.'); + $this->assertEqual($record->age, 27, 'Age field has the correct value.'); + $this->assertEqual($record->job, 'Singer', 'Job field has the correct value.'); + } + + /** + * Tests that a comparison with NULL is always FALSE. + */ + function testNullCondition() { + $this->ensureSampleDataNull(); + + $names = db_select('test_null', 'tn') + ->fields('tn', array('name')) + ->condition('age', NULL) + ->execute()->fetchCol(); + + $this->assertEqual(count($names), 0, 'No records found when comparing to NULL.'); + } + + /** + * Tests that we can find a record with a NULL value. + */ + function testIsNullCondition() { + $this->ensureSampleDataNull(); + + $names = db_select('test_null', 'tn') + ->fields('tn', array('name')) + ->isNull('age') + ->execute()->fetchCol(); + + $this->assertEqual(count($names), 1, 'Correct number of records found with NULL age.'); + $this->assertEqual($names[0], 'Fozzie', 'Correct record returned for NULL age.'); + } + + /** + * Tests that we can find a record without a NULL value. + */ + function testIsNotNullCondition() { + $this->ensureSampleDataNull(); + + $names = db_select('test_null', 'tn') + ->fields('tn', array('name')) + ->isNotNull('tn.age') + ->orderBy('name') + ->execute()->fetchCol(); + + $this->assertEqual(count($names), 2, 'Correct number of records found withNOT NULL age.'); + $this->assertEqual($names[0], 'Gonzo', 'Correct record returned for NOT NULL age.'); + $this->assertEqual($names[1], 'Kermit', 'Correct record returned for NOT NULL age.'); + } + + /** + * Tests that we can UNION multiple Select queries together. + * + * This is semantically equal to UNION DISTINCT, so we don't explicitly test + * that. + */ + function testUnion() { + $query_1 = db_select('test', 't') + ->fields('t', array('name')) + ->condition('age', array(27, 28), 'IN'); + + $query_2 = db_select('test', 't') + ->fields('t', array('name')) + ->condition('age', 28); + + $query_1->union($query_2); + + $names = $query_1->execute()->fetchCol(); + + // Ensure we only get 2 records. + $this->assertEqual(count($names), 2, 'UNION correctly discarded duplicates.'); + + $this->assertEqual($names[0], 'George', 'First query returned correct name.'); + $this->assertEqual($names[1], 'Ringo', 'Second query returned correct name.'); + } + + /** + * Tests that we can UNION ALL multiple SELECT queries together. + */ + function testUnionAll() { + $query_1 = db_select('test', 't') + ->fields('t', array('name')) + ->condition('age', array(27, 28), 'IN'); + + $query_2 = db_select('test', 't') + ->fields('t', array('name')) + ->condition('age', 28); + + $query_1->union($query_2, 'ALL'); + + $names = $query_1->execute()->fetchCol(); + + // Ensure we get all 3 records. + $this->assertEqual(count($names), 3, 'UNION ALL correctly preserved duplicates.'); + + $this->assertEqual($names[0], 'George', 'First query returned correct first name.'); + $this->assertEqual($names[1], 'Ringo', 'Second query returned correct second name.'); + $this->assertEqual($names[2], 'Ringo', 'Third query returned correct name.'); + } + + /** + * Tests that we can get a count query for a UNION Select query. + */ + function testUnionCount() { + $query_1 = db_select('test', 't') + ->fields('t', array('name', 'age')) + ->condition('age', array(27, 28), 'IN'); + + $query_2 = db_select('test', 't') + ->fields('t', array('name', 'age')) + ->condition('age', 28); + + $query_1->union($query_2, 'ALL'); + $names = $query_1->execute()->fetchCol(); + + $query_3 = $query_1->countQuery(); + $count = $query_3->execute()->fetchField(); + + // Ensure the counts match. + $this->assertEqual(count($names), $count, "The count query's result matched the number of rows in the UNION query."); + } + + /** + * Tests that random ordering of queries works. + * + * We take the approach of testing the Drupal layer only, rather than trying + * to test that the database's random number generator actually produces + * random queries (which is very difficult to do without an unacceptable risk + * of the test failing by accident). + * + * Therefore, in this test we simply run the same query twice and assert that + * the two results are reordered versions of each other (as well as of the + * same query without the random ordering). It is reasonable to assume that + * if we run the same select query twice and the results are in a different + * order each time, the only way this could happen is if we have successfully + * triggered the database's random ordering functionality. + */ + function testRandomOrder() { + // Use 52 items, so the chance that this test fails by accident will be the + // same as the chance that a deck of cards will come out in the same order + // after shuffling it (in other words, nearly impossible). + $number_of_items = 52; + while (db_query("SELECT MAX(id) FROM {test}")->fetchField() < $number_of_items) { + db_insert('test')->fields(array('name' => $this->randomMachineName()))->execute(); + } + + // First select the items in order and make sure we get an ordered list. + $expected_ids = range(1, $number_of_items); + $ordered_ids = db_select('test', 't') + ->fields('t', array('id')) + ->range(0, $number_of_items) + ->orderBy('id') + ->execute() + ->fetchCol(); + $this->assertEqual($ordered_ids, $expected_ids, 'A query without random ordering returns IDs in the correct order.'); + + // Now perform the same query, but instead choose a random ordering. We + // expect this to contain a differently ordered version of the original + // result. + $randomized_ids = db_select('test', 't') + ->fields('t', array('id')) + ->range(0, $number_of_items) + ->orderRandom() + ->execute() + ->fetchCol(); + $this->assertNotEqual($randomized_ids, $ordered_ids, 'A query with random ordering returns an unordered set of IDs.'); + $sorted_ids = $randomized_ids; + sort($sorted_ids); + $this->assertEqual($sorted_ids, $ordered_ids, 'After sorting the random list, the result matches the original query.'); + + // Now perform the exact same query again, and make sure the order is + // different. + $randomized_ids_second_set = db_select('test', 't') + ->fields('t', array('id')) + ->range(0, $number_of_items) + ->orderRandom() + ->execute() + ->fetchCol(); + $this->assertNotEqual($randomized_ids_second_set, $randomized_ids, 'Performing the query with random ordering a second time returns IDs in a different order.'); + $sorted_ids_second_set = $randomized_ids_second_set; + sort($sorted_ids_second_set); + $this->assertEqual($sorted_ids_second_set, $sorted_ids, 'After sorting the second random list, the result matches the sorted version of the first random list.'); + } + + /** + * Tests that filter by a regular expression works as expected. + */ + public function testRegexCondition() { + + $test_groups[] = array( + 'regex' => 'hn$', + 'expected' => array( + 'John', + ), + ); + $test_groups[] = array( + 'regex' => '^Pau', + 'expected' => array( + 'Paul', + ), + ); + $test_groups[] = array( + 'regex' => 'Ringo|George', + 'expected' => array( + 'Ringo', 'George', + ), + ); + + + $database = $this->container->get('database'); + foreach ($test_groups as $test_group) { + $query = $database->select('test', 't'); + $query->addField('t', 'name'); + $query->condition('t.name', $test_group['regex'], 'REGEXP'); + $result = $query->execute()->fetchCol(); + + $this->assertEqual(count($result), count($test_group['expected']), 'Returns the expected number of rows.'); + $this->assertEqual(sort($result), sort($test_group['expected']), 'Returns the expected rows.'); + } + + // Ensure that filter by "#" still works due to the quoting. + $database->insert('test') + ->fields(array( + 'name' => 'Pete', + 'age' => 26, + 'job' => '#Drummer', + )) + ->execute(); + + $test_groups = array(); + $test_groups[] = array( + 'regex' => '#Drummer', + 'expected' => array( + 'Pete', + ), + ); + $test_groups[] = array( + 'regex' => '#Singer', + 'expected' => array( + ), + ); + + foreach ($test_groups as $test_group) { + $query = $database->select('test', 't'); + $query->addField('t', 'name'); + $query->condition('t.job', $test_group['regex'], 'REGEXP'); + $result = $query->execute()->fetchCol(); + + $this->assertEqual(count($result), count($test_group['expected']), 'Returns the expected number of rows.'); + $this->assertEqual(sort($result), sort($test_group['expected']), 'Returns the expected rows.'); + } + } + + /** + * Tests that aliases are renamed when they are duplicates. + */ + function testSelectDuplicateAlias() { + $query = db_select('test', 't'); + $alias1 = $query->addField('t', 'name', 'the_alias'); + $alias2 = $query->addField('t', 'age', 'the_alias'); + $this->assertNotIdentical($alias1, $alias2, 'Duplicate aliases are renamed.'); + } + + /** + * Tests that an invalid merge query throws an exception. + */ + function testInvalidSelectCount() { + try { + // This query will fail because the table does not exist. + // Normally it would throw an exception but we are suppressing + // it with the throw_exception option. + $options['throw_exception'] = FALSE; + db_select('some_table_that_doesnt_exist', 't', $options) + ->fields('t') + ->countQuery() + ->execute(); + + $this->pass('$options[\'throw_exception\'] is FALSE, no Exception thrown.'); + } + catch (\Exception $e) { + $this->fail('$options[\'throw_exception\'] is FALSE, but Exception thrown for invalid query.'); + return; + } + + try { + // This query will fail because the table does not exist. + db_select('some_table_that_doesnt_exist', 't') + ->fields('t') + ->countQuery() + ->execute(); + } + catch (\Exception $e) { + $this->pass('Exception thrown for invalid query.'); + return; + } + $this->fail('No Exception thrown.'); + } + + /** + * Tests thrown exception for IN query conditions with an empty array. + */ + function testEmptyInCondition() { + try { + db_select('test', 't') + ->fields('t') + ->condition('age', array(), 'IN') + ->execute(); + + $this->fail('Expected exception not thrown'); + } + catch (InvalidQueryException $e) { + $this->assertEqual("Query condition 'age IN ()' cannot be empty.", $e->getMessage()); + } + + try { + db_select('test', 't') + ->fields('t') + ->condition('age', array(), 'NOT IN') + ->execute(); + + $this->fail('Expected exception not thrown'); + } + catch (InvalidQueryException $e) { + $this->assertEqual("Query condition 'age NOT IN ()' cannot be empty.", $e->getMessage()); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/SerializeQueryTest.php b/core/tests/Drupal/KernelTests/Core/Database/SerializeQueryTest.php new file mode 100644 index 0000000..9199eb2 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/SerializeQueryTest.php @@ -0,0 +1,29 @@ +addField('test', 'age'); + $query->condition('name', 'Ringo'); + // If this doesn't work, it will throw an exception, so no need for an + // assertion. + $query = unserialize(serialize($query)); + $results = $query->execute()->fetchCol(); + $this->assertEqual($results[0], 28, 'Query properly executed after unserialization.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/TaggingTest.php b/core/tests/Drupal/KernelTests/Core/Database/TaggingTest.php new file mode 100644 index 0000000..0de1413 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/TaggingTest.php @@ -0,0 +1,132 @@ +addField('test', 'name'); + $query->addField('test', 'age', 'age'); + + $query->addTag('test'); + + $this->assertTrue($query->hasTag('test'), 'hasTag() returned true.'); + $this->assertFalse($query->hasTag('other'), 'hasTag() returned false.'); + } + + /** + * Tests query tagging "has all of these tags" functionality. + */ + function testHasAllTags() { + $query = db_select('test'); + $query->addField('test', 'name'); + $query->addField('test', 'age', 'age'); + + $query->addTag('test'); + $query->addTag('other'); + + $this->assertTrue($query->hasAllTags('test', 'other'), 'hasAllTags() returned true.'); + $this->assertFalse($query->hasAllTags('test', 'stuff'), 'hasAllTags() returned false.'); + } + + /** + * Tests query tagging "has at least one of these tags" functionality. + */ + function testHasAnyTag() { + $query = db_select('test'); + $query->addField('test', 'name'); + $query->addField('test', 'age', 'age'); + + $query->addTag('test'); + + $this->assertTrue($query->hasAnyTag('test', 'other'), 'hasAnyTag() returned true.'); + $this->assertFalse($query->hasAnyTag('other', 'stuff'), 'hasAnyTag() returned false.'); + } + + /** + * Confirms that an extended query has a tag added to it. + */ + function testExtenderHasTag() { + $query = db_select('test') + ->extend('Drupal\Core\Database\Query\SelectExtender'); + $query->addField('test', 'name'); + $query->addField('test', 'age', 'age'); + + $query->addTag('test'); + + $this->assertTrue($query->hasTag('test'), 'hasTag() returned true.'); + $this->assertFalse($query->hasTag('other'), 'hasTag() returned false.'); + } + + /** + * Tests extended query tagging "has all of these tags" functionality. + */ + function testExtenderHasAllTags() { + $query = db_select('test') + ->extend('Drupal\Core\Database\Query\SelectExtender'); + $query->addField('test', 'name'); + $query->addField('test', 'age', 'age'); + + $query->addTag('test'); + $query->addTag('other'); + + $this->assertTrue($query->hasAllTags('test', 'other'), 'hasAllTags() returned true.'); + $this->assertFalse($query->hasAllTags('test', 'stuff'), 'hasAllTags() returned false.'); + } + + /** + * Tests extended query tagging "has at least one of these tags" functionality. + */ + function testExtenderHasAnyTag() { + $query = db_select('test') + ->extend('Drupal\Core\Database\Query\SelectExtender'); + $query->addField('test', 'name'); + $query->addField('test', 'age', 'age'); + + $query->addTag('test'); + + $this->assertTrue($query->hasAnyTag('test', 'other'), 'hasAnyTag() returned true.'); + $this->assertFalse($query->hasAnyTag('other', 'stuff'), 'hasAnyTag() returned false.'); + } + + /** + * Tests that we can attach metadata to a query object. + * + * This is how we pass additional context to alter hooks. + */ + function testMetaData() { + $query = db_select('test'); + $query->addField('test', 'name'); + $query->addField('test', 'age', 'age'); + + $data = array( + 'a' => 'A', + 'b' => 'B', + ); + + $query->addMetaData('test', $data); + + $return = $query->getMetaData('test'); + $this->assertEqual($data, $return, 'Correct metadata returned.'); + + $return = $query->getMetaData('nothere'); + $this->assertNull($return, 'Non-existent key returned NULL.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php b/core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php new file mode 100644 index 0000000..abf41bd --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/TransactionTest.php @@ -0,0 +1,615 @@ +transactionDepth(); + $txn = db_transaction(); + + // Insert a single row into the testing table. + db_insert('test') + ->fields(array( + 'name' => 'David' . $suffix, + 'age' => '24', + )) + ->execute(); + + $this->assertTrue($connection->inTransaction(), 'In transaction before calling nested transaction.'); + + // We're already in a transaction, but we call ->transactionInnerLayer + // to nest another transaction inside the current one. + $this->transactionInnerLayer($suffix, $rollback, $ddl_statement); + + $this->assertTrue($connection->inTransaction(), 'In transaction after calling nested transaction.'); + + if ($rollback) { + // Roll back the transaction, if requested. + // This rollback should propagate to the last savepoint. + $txn->rollback(); + $this->assertTrue(($connection->transactionDepth() == $depth), 'Transaction has rolled back to the last savepoint after calling rollback().'); + } + } + + /** + * Creates an "inner layer" transaction. + * + * This "inner layer" transaction is either used alone or nested inside of the + * "outer layer" transaction. + * + * @param $suffix + * Suffix to add to field values to differentiate tests. + * @param $rollback + * Whether or not to try rolling back the transaction when we're done. + * @param $ddl_statement + * Whether to execute a DDL statement during the transaction. + */ + protected function transactionInnerLayer($suffix, $rollback = FALSE, $ddl_statement = FALSE) { + $connection = Database::getConnection(); + + $depth = $connection->transactionDepth(); + // Start a transaction. If we're being called from ->transactionOuterLayer, + // then we're already in a transaction. Normally, that would make starting + // a transaction here dangerous, but the database API handles this problem + // for us by tracking the nesting and avoiding the danger. + $txn = db_transaction(); + + $depth2 = $connection->transactionDepth(); + $this->assertTrue($depth < $depth2, 'Transaction depth is has increased with new transaction.'); + + // Insert a single row into the testing table. + db_insert('test') + ->fields(array( + 'name' => 'Daniel' . $suffix, + 'age' => '19', + )) + ->execute(); + + $this->assertTrue($connection->inTransaction(), 'In transaction inside nested transaction.'); + + if ($ddl_statement) { + $table = array( + 'fields' => array( + 'id' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + ), + 'primary key' => array('id'), + ); + db_create_table('database_test_1', $table); + + $this->assertTrue($connection->inTransaction(), 'In transaction inside nested transaction.'); + } + + if ($rollback) { + // Roll back the transaction, if requested. + // This rollback should propagate to the last savepoint. + $txn->rollback(); + $this->assertTrue(($connection->transactionDepth() == $depth), 'Transaction has rolled back to the last savepoint after calling rollback().'); + } + } + + /** + * Tests transaction rollback on a database that supports transactions. + * + * If the active connection does not support transactions, this test does + * nothing. + */ + function testTransactionRollBackSupported() { + // This test won't work right if transactions are not supported. + if (!Database::getConnection()->supportsTransactions()) { + return; + } + try { + // Create two nested transactions. Roll back from the inner one. + $this->transactionOuterLayer('B', TRUE); + + // Neither of the rows we inserted in the two transaction layers + // should be present in the tables post-rollback. + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField(); + $this->assertNotIdentical($saved_age, '24', 'Cannot retrieve DavidB row after commit.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField(); + $this->assertNotIdentical($saved_age, '19', 'Cannot retrieve DanielB row after commit.'); + } + catch (\Exception $e) { + $this->fail($e->getMessage()); + } + } + + /** + * Tests transaction rollback on a database that doesn't support transactions. + * + * If the active driver supports transactions, this test does nothing. + */ + function testTransactionRollBackNotSupported() { + // This test won't work right if transactions are supported. + if (Database::getConnection()->supportsTransactions()) { + return; + } + try { + // Create two nested transactions. Attempt to roll back from the inner one. + $this->transactionOuterLayer('B', TRUE); + + // Because our current database claims to not support transactions, + // the inserted rows should be present despite the attempt to roll back. + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField(); + $this->assertIdentical($saved_age, '24', 'DavidB not rolled back, since transactions are not supported.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField(); + $this->assertIdentical($saved_age, '19', 'DanielB not rolled back, since transactions are not supported.'); + } + catch (\Exception $e) { + $this->fail($e->getMessage()); + } + } + + /** + * Tests a committed transaction. + * + * The behavior of this test should be identical for connections that support + * transactions and those that do not. + */ + function testCommittedTransaction() { + try { + // Create two nested transactions. The changes should be committed. + $this->transactionOuterLayer('A'); + + // Because we committed, both of the inserted rows should be present. + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidA'))->fetchField(); + $this->assertIdentical($saved_age, '24', 'Can retrieve DavidA row after commit.'); + $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielA'))->fetchField(); + $this->assertIdentical($saved_age, '19', 'Can retrieve DanielA row after commit.'); + } + catch (\Exception $e) { + $this->fail($e->getMessage()); + } + } + + /** + * Tests the compatibility of transactions with DDL statements. + */ + function testTransactionWithDdlStatement() { + // First, test that a commit works normally, even with DDL statements. + $transaction = db_transaction(); + $this->insertRow('row'); + $this->executeDDLStatement(); + unset($transaction); + $this->assertRowPresent('row'); + + // Even in different order. + $this->cleanUp(); + $transaction = db_transaction(); + $this->executeDDLStatement(); + $this->insertRow('row'); + unset($transaction); + $this->assertRowPresent('row'); + + // Even with stacking. + $this->cleanUp(); + $transaction = db_transaction(); + $transaction2 = db_transaction(); + $this->executeDDLStatement(); + unset($transaction2); + $transaction3 = db_transaction(); + $this->insertRow('row'); + unset($transaction3); + unset($transaction); + $this->assertRowPresent('row'); + + // A transaction after a DDL statement should still work the same. + $this->cleanUp(); + $transaction = db_transaction(); + $transaction2 = db_transaction(); + $this->executeDDLStatement(); + unset($transaction2); + $transaction3 = db_transaction(); + $this->insertRow('row'); + $transaction3->rollback(); + unset($transaction3); + unset($transaction); + $this->assertRowAbsent('row'); + + // The behavior of a rollback depends on the type of database server. + if (Database::getConnection()->supportsTransactionalDDL()) { + // For database servers that support transactional DDL, a rollback + // of a transaction including DDL statements should be possible. + $this->cleanUp(); + $transaction = db_transaction(); + $this->insertRow('row'); + $this->executeDDLStatement(); + $transaction->rollback(); + unset($transaction); + $this->assertRowAbsent('row'); + + // Including with stacking. + $this->cleanUp(); + $transaction = db_transaction(); + $transaction2 = db_transaction(); + $this->executeDDLStatement(); + unset($transaction2); + $transaction3 = db_transaction(); + $this->insertRow('row'); + unset($transaction3); + $transaction->rollback(); + unset($transaction); + $this->assertRowAbsent('row'); + } + else { + // For database servers that do not support transactional DDL, + // the DDL statement should commit the transaction stack. + $this->cleanUp(); + $transaction = db_transaction(); + $this->insertRow('row'); + $this->executeDDLStatement(); + // Rollback the outer transaction. + try { + $transaction->rollback(); + unset($transaction); + // @TODO: an exception should be triggered here, but is not, because + // "ROLLBACK" fails silently in MySQL if there is no transaction active. + // $this->fail(t('Rolling back a transaction containing DDL should fail.')); + } + catch (TransactionNoActiveException $e) { + $this->pass('Rolling back a transaction containing DDL should fail.'); + } + $this->assertRowPresent('row'); + } + } + + /** + * Inserts a single row into the testing table. + */ + protected function insertRow($name) { + db_insert('test') + ->fields(array( + 'name' => $name, + )) + ->execute(); + } + + /** + * Executes a DDL statement. + */ + protected function executeDDLStatement() { + static $count = 0; + $table = array( + 'fields' => array( + 'id' => array( + 'type' => 'serial', + 'unsigned' => TRUE, + 'not null' => TRUE, + ), + ), + 'primary key' => array('id'), + ); + db_create_table('database_test_' . ++$count, $table); + } + + /** + * Starts over for a new test. + */ + protected function cleanUp() { + db_truncate('test') + ->execute(); + } + + /** + * Asserts that a given row is present in the test table. + * + * @param $name + * The name of the row. + * @param $message + * The message to log for the assertion. + */ + function assertRowPresent($name, $message = NULL) { + if (!isset($message)) { + $message = format_string('Row %name is present.', array('%name' => $name)); + } + $present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField(); + return $this->assertTrue($present, $message); + } + + /** + * Asserts that a given row is absent from the test table. + * + * @param $name + * The name of the row. + * @param $message + * The message to log for the assertion. + */ + function assertRowAbsent($name, $message = NULL) { + if (!isset($message)) { + $message = format_string('Row %name is absent.', array('%name' => $name)); + } + $present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField(); + return $this->assertFalse($present, $message); + } + + /** + * Tests transaction stacking, commit, and rollback. + */ + function testTransactionStacking() { + // This test won't work right if transactions are not supported. + if (!Database::getConnection()->supportsTransactions()) { + return; + } + + $database = Database::getConnection(); + + // Standard case: pop the inner transaction before the outer transaction. + $transaction = db_transaction(); + $this->insertRow('outer'); + $transaction2 = db_transaction(); + $this->insertRow('inner'); + // Pop the inner transaction. + unset($transaction2); + $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the inner transaction'); + // Pop the outer transaction. + unset($transaction); + $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the outer transaction'); + $this->assertRowPresent('outer'); + $this->assertRowPresent('inner'); + + // Pop the transaction in a different order they have been pushed. + $this->cleanUp(); + $transaction = db_transaction(); + $this->insertRow('outer'); + $transaction2 = db_transaction(); + $this->insertRow('inner'); + // Pop the outer transaction, nothing should happen. + unset($transaction); + $this->insertRow('inner-after-outer-commit'); + $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction'); + // Pop the inner transaction, the whole transaction should commit. + unset($transaction2); + $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction'); + $this->assertRowPresent('outer'); + $this->assertRowPresent('inner'); + $this->assertRowPresent('inner-after-outer-commit'); + + // Rollback the inner transaction. + $this->cleanUp(); + $transaction = db_transaction(); + $this->insertRow('outer'); + $transaction2 = db_transaction(); + $this->insertRow('inner'); + // Now rollback the inner transaction. + $transaction2->rollback(); + unset($transaction2); + $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction'); + // Pop the outer transaction, it should commit. + $this->insertRow('outer-after-inner-rollback'); + unset($transaction); + $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction'); + $this->assertRowPresent('outer'); + $this->assertRowAbsent('inner'); + $this->assertRowPresent('outer-after-inner-rollback'); + + // Rollback the inner transaction after committing the outer one. + $this->cleanUp(); + $transaction = db_transaction(); + $this->insertRow('outer'); + $transaction2 = db_transaction(); + $this->insertRow('inner'); + // Pop the outer transaction, nothing should happen. + unset($transaction); + $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction'); + // Now rollback the inner transaction, it should rollback. + $transaction2->rollback(); + unset($transaction2); + $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction'); + $this->assertRowPresent('outer'); + $this->assertRowAbsent('inner'); + + // Rollback the outer transaction while the inner transaction is active. + // In that case, an exception will be triggered because we cannot + // ensure that the final result will have any meaning. + $this->cleanUp(); + $transaction = db_transaction(); + $this->insertRow('outer'); + $transaction2 = db_transaction(); + $this->insertRow('inner'); + $transaction3 = db_transaction(); + $this->insertRow('inner2'); + // Rollback the outer transaction. + try { + $transaction->rollback(); + unset($transaction); + $this->fail('Rolling back the outer transaction while the inner transaction is active resulted in an exception.'); + } + catch (TransactionOutOfOrderException $e) { + $this->pass('Rolling back the outer transaction while the inner transaction is active resulted in an exception.'); + } + $this->assertFalse($database->inTransaction(), 'No more in a transaction after rolling back the outer transaction'); + // Try to commit one inner transaction. + unset($transaction3); + $this->pass('Trying to commit an inner transaction resulted in an exception.'); + // Try to rollback one inner transaction. + try { + $transaction->rollback(); + unset($transaction2); + $this->fail('Trying to commit an inner transaction resulted in an exception.'); + } + catch (TransactionNoActiveException $e) { + $this->pass('Trying to commit an inner transaction resulted in an exception.'); + } + $this->assertRowAbsent('outer'); + $this->assertRowAbsent('inner'); + $this->assertRowAbsent('inner2'); + } + + /** + * Tests that transactions can continue to be used if a query fails. + */ + public function testQueryFailureInTransaction() { + $connection = Database::getConnection(); + $transaction = $connection->startTransaction('test_transaction'); + $connection->schema()->dropTable('test'); + + // Test a failed query using the query() method. + try { + $connection->query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'David'))->fetchField(); + $this->fail('Using the query method failed.'); + } + catch (\Exception $e) { + $this->pass('Using the query method failed.'); + } + + // Test a failed select query. + try { + $connection->select('test') + ->fields('test', ['name']) + ->execute(); + + $this->fail('Select query failed.'); + } + catch (\Exception $e) { + $this->pass('Select query failed.'); + } + + // Test a failed insert query. + try { + $connection->insert('test') + ->fields([ + 'name' => 'David', + 'age' => '24', + ]) + ->execute(); + + $this->fail('Insert query failed.'); + } + catch (\Exception $e) { + $this->pass('Insert query failed.'); + } + + // Test a failed update query. + try { + $connection->update('test') + ->fields(['name' => 'Tiffany']) + ->condition('id', 1) + ->execute(); + + $this->fail('Update query failed.'); + } + catch (\Exception $e) { + $this->pass('Update query failed.'); + } + + // Test a failed delete query. + try { + $connection->delete('test') + ->condition('id', 1) + ->execute(); + + $this->fail('Delete query failed.'); + } + catch (\Exception $e) { + $this->pass('Delete query failed.'); + } + + // Test a failed merge query. + try { + $connection->merge('test') + ->key('job', 'Presenter') + ->fields([ + 'age' => '31', + 'name' => 'Tiffany', + ]) + ->execute(); + + $this->fail('Merge query failed.'); + } + catch (\Exception $e) { + $this->pass('Merge query failed.'); + } + + // Test a failed upsert query. + try { + $connection->upsert('test') + ->key('job') + ->fields(['job', 'age', 'name']) + ->values([ + 'job' => 'Presenter', + 'age' => 31, + 'name' => 'Tiffany', + ]) + ->execute(); + + $this->fail('Upset query failed.'); + } + catch (\Exception $e) { + $this->pass('Upset query failed.'); + } + + // Create the missing schema and insert a row. + $this->installSchema('database_test', ['test']); + $connection->insert('test') + ->fields(array( + 'name' => 'David', + 'age' => '24', + )) + ->execute(); + + // Commit the transaction. + unset($transaction); + + $saved_age = $connection->query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'David'))->fetchField(); + $this->assertEqual('24', $saved_age); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/UpdateComplexTest.php b/core/tests/Drupal/KernelTests/Core/Database/UpdateComplexTest.php new file mode 100644 index 0000000..e74de9c --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/UpdateComplexTest.php @@ -0,0 +1,150 @@ +fields(array('job' => 'Musician')) + ->condition(db_or() + ->condition('name', 'John') + ->condition('name', 'Paul') + ); + $num_updated = $update->execute(); + $this->assertIdentical($num_updated, 2, 'Updated 2 records.'); + + $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); + $this->assertIdentical($num_matches, '2', 'Updated fields successfully.'); + } + + /** + * Tests WHERE IN clauses. + */ + function testInConditionUpdate() { + $num_updated = db_update('test') + ->fields(array('job' => 'Musician')) + ->condition('name', array('John', 'Paul'), 'IN') + ->execute(); + $this->assertIdentical($num_updated, 2, 'Updated 2 records.'); + + $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); + $this->assertIdentical($num_matches, '2', 'Updated fields successfully.'); + } + + /** + * Tests WHERE NOT IN clauses. + */ + function testNotInConditionUpdate() { + // The o is lowercase in the 'NoT IN' operator, to make sure the operators + // work in mixed case. + $num_updated = db_update('test') + ->fields(array('job' => 'Musician')) + ->condition('name', array('John', 'Paul', 'George'), 'NoT IN') + ->execute(); + $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); + + $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); + $this->assertIdentical($num_matches, '1', 'Updated fields successfully.'); + } + + /** + * Tests BETWEEN conditional clauses. + */ + function testBetweenConditionUpdate() { + $num_updated = db_update('test') + ->fields(array('job' => 'Musician')) + ->condition('age', array(25, 26), 'BETWEEN') + ->execute(); + $this->assertIdentical($num_updated, 2, 'Updated 2 records.'); + + $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); + $this->assertIdentical($num_matches, '2', 'Updated fields successfully.'); + } + + /** + * Tests LIKE conditionals. + */ + function testLikeConditionUpdate() { + $num_updated = db_update('test') + ->fields(array('job' => 'Musician')) + ->condition('name', '%ge%', 'LIKE') + ->execute(); + $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); + + $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); + $this->assertIdentical($num_matches, '1', 'Updated fields successfully.'); + } + + /** + * Tests UPDATE with expression values. + */ + function testUpdateExpression() { + $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField(); + $GLOBALS['larry_test'] = 1; + $num_updated = db_update('test') + ->condition('name', 'Ringo') + ->fields(array('job' => 'Musician')) + ->expression('age', 'age + :age', array(':age' => 4)) + ->execute(); + $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); + + $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); + $this->assertIdentical($num_matches, '1', 'Updated fields successfully.'); + + $person = db_query('SELECT * FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetch(); + $this->assertEqual($person->name, 'Ringo', 'Name set correctly.'); + $this->assertEqual($person->age, $before_age + 4, 'Age set correctly.'); + $this->assertEqual($person->job, 'Musician', 'Job set correctly.'); + $GLOBALS['larry_test'] = 0; + } + + /** + * Tests UPDATE with only expression values. + */ + function testUpdateOnlyExpression() { + $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField(); + $num_updated = db_update('test') + ->condition('name', 'Ringo') + ->expression('age', 'age + :age', array(':age' => 4)) + ->execute(); + $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); + + $after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField(); + $this->assertEqual($before_age + 4, $after_age, 'Age updated correctly'); + } + + /** + * Test UPDATE with a subselect value. + */ + function testSubSelectUpdate() { + $subselect = db_select('test_task', 't'); + $subselect->addExpression('MAX(priority) + :increment', 'max_priority', array(':increment' => 30)); + // Clone this to make sure we are running a different query when + // asserting. + $select = clone $subselect; + $query = db_update('test') + ->expression('age', $subselect) + ->condition('name', 'Ringo'); + // Save the number of rows that updated for assertion later. + $num_updated = $query->execute(); + $after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField(); + $expected_age = $select->execute()->fetchField(); + $this->assertEqual($after_age, $expected_age); + $this->assertEqual(1, $num_updated, t('Expected 1 row to be updated in subselect update query.')); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/UpdateLobTest.php b/core/tests/Drupal/KernelTests/Core/Database/UpdateLobTest.php new file mode 100644 index 0000000..bfe51ec --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/UpdateLobTest.php @@ -0,0 +1,56 @@ +assertTrue(strlen($data) === 15, 'Test data contains a NULL.'); + $id = db_insert('test_one_blob') + ->fields(array('blob1' => $data)) + ->execute(); + + $data .= $data; + db_update('test_one_blob') + ->condition('id', $id) + ->fields(array('blob1' => $data)) + ->execute(); + + $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc(); + $this->assertTrue($r['blob1'] === $data, format_string('Can update a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r)))); + } + + /** + * Confirms that we can update two blob columns in the same table. + */ + function testUpdateMultipleBlob() { + $id = db_insert('test_two_blobs') + ->fields(array( + 'blob1' => 'This is', + 'blob2' => 'a test', + )) + ->execute(); + + db_update('test_two_blobs') + ->condition('id', $id) + ->fields(array('blob1' => 'and so', 'blob2' => 'is this')) + ->execute(); + + $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc(); + $this->assertTrue($r['blob1'] === 'and so' && $r['blob2'] === 'is this', 'Can update multiple blobs per row.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/UpdateTest.php b/core/tests/Drupal/KernelTests/Core/Database/UpdateTest.php new file mode 100644 index 0000000..638cdc3 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/UpdateTest.php @@ -0,0 +1,162 @@ +fields(array('name' => 'Tiffany')) + ->condition('id', 1) + ->execute(); + $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); + + $saved_name = db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 1))->fetchField(); + $this->assertIdentical($saved_name, 'Tiffany', 'Updated name successfully.'); + } + + /** + * Confirms updating to NULL. + */ + function testSimpleNullUpdate() { + $this->ensureSampleDataNull(); + $num_updated = db_update('test_null') + ->fields(array('age' => NULL)) + ->condition('name', 'Kermit') + ->execute(); + $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); + + $saved_age = db_query('SELECT age FROM {test_null} WHERE name = :name', array(':name' => 'Kermit'))->fetchField(); + $this->assertNull($saved_age, 'Updated name successfully.'); + } + + /** + * Confirms that we can update multiple records successfully. + */ + function testMultiUpdate() { + $num_updated = db_update('test') + ->fields(array('job' => 'Musician')) + ->condition('job', 'Singer') + ->execute(); + $this->assertIdentical($num_updated, 2, 'Updated 2 records.'); + + $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); + $this->assertIdentical($num_matches, '2', 'Updated fields successfully.'); + } + + /** + * Confirms that we can update multiple records with a non-equality condition. + */ + function testMultiGTUpdate() { + $num_updated = db_update('test') + ->fields(array('job' => 'Musician')) + ->condition('age', 26, '>') + ->execute(); + $this->assertIdentical($num_updated, 2, 'Updated 2 records.'); + + $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); + $this->assertIdentical($num_matches, '2', 'Updated fields successfully.'); + } + + /** + * Confirms that we can update multiple records with a where call. + */ + function testWhereUpdate() { + $num_updated = db_update('test') + ->fields(array('job' => 'Musician')) + ->where('age > :age', array(':age' => 26)) + ->execute(); + $this->assertIdentical($num_updated, 2, 'Updated 2 records.'); + + $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); + $this->assertIdentical($num_matches, '2', 'Updated fields successfully.'); + } + + /** + * Confirms that we can stack condition and where calls. + */ + function testWhereAndConditionUpdate() { + $update = db_update('test') + ->fields(array('job' => 'Musician')) + ->where('age > :age', array(':age' => 26)) + ->condition('name', 'Ringo'); + $num_updated = $update->execute(); + $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); + + $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField(); + $this->assertIdentical($num_matches, '1', 'Updated fields successfully.'); + } + + /** + * Tests updating with expressions. + */ + function testExpressionUpdate() { + // Ensure that expressions are handled properly. This should set every + // record's age to a square of itself. + $num_rows = db_update('test') + ->expression('age', 'age * age') + ->execute(); + $this->assertIdentical($num_rows, 4, 'Updated 4 records.'); + + $saved_name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => pow(26, 2)))->fetchField(); + $this->assertIdentical($saved_name, 'Paul', 'Successfully updated values using an algebraic expression.'); + } + + /** + * Tests return value on update. + */ + function testUpdateAffectedRows() { + // At 5am in the morning, all band members but those with a priority 1 task + // are sleeping. So we set their tasks to 'sleep'. 5 records match the + // condition and therefore are affected by the query, even though two of + // them actually don't have to be changed because their value was already + // 'sleep'. Still, execute() should return 5 affected rows, not only 3, + // because that's cross-db expected behavior. + $num_rows = db_update('test_task') + ->condition('priority', 1, '<>') + ->fields(array('task' => 'sleep')) + ->execute(); + $this->assertIdentical($num_rows, 5, 'Correctly returned 5 affected rows.'); + } + + /** + * Confirm that we can update the primary key of a record successfully. + */ + function testPrimaryKeyUpdate() { + $num_updated = db_update('test') + ->fields(array('id' => 42, 'name' => 'John')) + ->condition('id', 1) + ->execute(); + $this->assertIdentical($num_updated, 1, 'Updated 1 record.'); + + $saved_name= db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 42))->fetchField(); + $this->assertIdentical($saved_name, 'John', 'Updated primary key successfully.'); + } + + /** + * Confirm that we can update values in a column with special name. + */ + function testSpecialColumnUpdate() { + $num_updated = db_update('test_special_columns') + ->fields(array('offset' => 'New offset value')) + ->condition('id', 1) + ->execute(); + $this->assertIdentical($num_updated, 1, 'Updated 1 special column record.'); + + $saved_value = db_query('SELECT "offset" FROM {test_special_columns} WHERE id = :id', array(':id' => 1))->fetchField(); + $this->assertIdentical($saved_value, 'New offset value', 'Updated special column name value successfully.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Database/UpsertTest.php b/core/tests/Drupal/KernelTests/Core/Database/UpsertTest.php new file mode 100644 index 0000000..b8f440e --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Database/UpsertTest.php @@ -0,0 +1,61 @@ +query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + + $upsert = $connection->upsert('test_people') + ->key('job') + ->fields(['job', 'age', 'name']); + + // Add a new row. + $upsert->values([ + 'job' => 'Presenter', + 'age' => 31, + 'name' => 'Tiffany', + ]); + + // Update an existing row. + $upsert->values([ + 'job' => 'Speaker', + // The initial age was 30. + 'age' => 32, + 'name' => 'Meredith', + ]); + + $upsert->execute(); + + $num_records_after = $connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField(); + $this->assertEqual($num_records_before + 1, $num_records_after, 'Rows were inserted and updated properly.'); + + $person = $connection->query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch(); + $this->assertEqual($person->job, 'Presenter', 'Job set correctly.'); + $this->assertEqual($person->age, 31, 'Age set correctly.'); + $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.'); + + $person = $connection->query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch(); + $this->assertEqual($person->job, 'Speaker', 'Job was not changed.'); + $this->assertEqual($person->age, 32, 'Age updated correctly.'); + $this->assertEqual($person->name, 'Meredith', 'Name was not changed.'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/BundleConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Entity/BundleConstraintValidatorTest.php new file mode 100644 index 0000000..53120e0 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/BundleConstraintValidatorTest.php @@ -0,0 +1,76 @@ +installEntitySchema('user'); + $this->typedData = $this->container->get('typed_data_manager'); + } + + /** + * Tests bundle constraint validation. + */ + public function testValidation() { + // Test with multiple values. + $this->assertValidation(array('foo', 'bar')); + // Test with a single string value as well. + $this->assertValidation('foo'); + } + + /** + * Executes the BundleConstraintValidator test for a given bundle. + * + * @param string|array $bundle + * Bundle/bundles to use as constraint option. + */ + protected function assertValidation($bundle) { + // Create a typed data definition with a Bundle constraint. + $definition = DataDefinition::create('entity_reference') + ->addConstraint('Bundle', $bundle); + + // Test the validation. + $node = $this->container->get('entity.manager')->getStorage('node')->create(array('type' => 'foo')); + + $typed_data = $this->typedData->create($definition, $node); + $violations = $typed_data->validate(); + $this->assertEqual($violations->count(), 0, 'Validation passed for correct value.'); + + // Test the validation when an invalid value is passed. + $page_node = $this->container->get('entity.manager')->getStorage('node')->create(array('type' => 'baz')); + + $typed_data = $this->typedData->create($definition, $page_node); + $violations = $typed_data->validate(); + $this->assertEqual($violations->count(), 1, 'Validation failed for incorrect value.'); + + // Make sure the information provided by a violation is correct. + $violation = $violations[0]; + $this->assertEqual($violation->getMessage(), t('The entity must be of bundle %bundle.', array('%bundle' => implode(', ', (array) $bundle))), 'The message for invalid value is correct.'); + $this->assertEqual($violation->getRoot(), $typed_data, 'Violation root is correct.'); + $this->assertEqual($violation->getInvalidValue(), $page_node, 'The invalid value is set correctly in the violation.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/ConfigEntityQueryTest.php b/core/tests/Drupal/KernelTests/Core/Entity/ConfigEntityQueryTest.php new file mode 100644 index 0000000..3687f34 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/ConfigEntityQueryTest.php @@ -0,0 +1,670 @@ +entities = array(); + $this->factory = $this->container->get('entity.query'); + + // These two are here to make sure that matchArray needs to go over several + // non-matches on every levels. + $array['level1']['level2a'] = 9; + $array['level1a']['level2'] = 9; + // The tests match array.level1.level2. + $array['level1']['level2'] = 1; + $entity = ConfigQueryTest::create(array( + 'label' => $this->randomMachineName(), + 'id' => '1', + 'number' => 31, + 'array' => $array, + )); + $this->entities[] = $entity; + $entity->enforceIsNew(); + $entity->save(); + + $array['level1']['level2'] = 2; + $entity = ConfigQueryTest::create(array( + 'label' => $this->randomMachineName(), + 'id' => '2', + 'number' => 41, + 'array' => $array, + )); + $this->entities[] = $entity; + $entity->enforceIsNew(); + $entity->save(); + + $array['level1']['level2'] = 1; + $entity = ConfigQueryTest::create(array( + 'label' => 'test_prefix_' . $this->randomMachineName(), + 'id' => '3', + 'number' => 59, + 'array' => $array, + )); + $this->entities[] = $entity; + $entity->enforceIsNew(); + $entity->save(); + + $array['level1']['level2'] = 2; + $entity = ConfigQueryTest::create(array( + 'label' => $this->randomMachineName() . '_test_suffix', + 'id' => '4', + 'number' => 26, + 'array' => $array, + )); + $this->entities[] = $entity; + $entity->enforceIsNew(); + $entity->save(); + + $array['level1']['level2'] = 3; + $entity = ConfigQueryTest::create(array( + 'label' => $this->randomMachineName() . '_TEST_contains_' . $this->randomMachineName(), + 'id' => '5', + 'number' => 53, + 'array' => $array, + )); + $this->entities[] = $entity; + $entity->enforceIsNew(); + $entity->save(); + } + + /** + * Tests basic functionality. + */ + public function testConfigEntityQuery() { + // Run a test without any condition. + $this->queryResults = $this->factory->get('config_query_test') + ->execute(); + $this->assertResults(array('1', '2', '3', '4', '5')); + // No conditions, OR. + $this->queryResults = $this->factory->get('config_query_test', 'OR') + ->execute(); + $this->assertResults(array('1', '2', '3', '4', '5')); + + // Filter by ID with equality. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', '3') + ->execute(); + $this->assertResults(array('3')); + + // Filter by label with a known prefix. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('label', 'test_prefix', 'STARTS_WITH') + ->execute(); + $this->assertResults(array('3')); + + // Filter by label with a known suffix. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('label', 'test_suffix', 'ENDS_WITH') + ->execute(); + $this->assertResults(array('4')); + + // Filter by label with a known containing word. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('label', 'test_contains', 'CONTAINS') + ->execute(); + $this->assertResults(array('5')); + + // Filter by ID with the IN operator. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', array('2', '3'), 'IN') + ->execute(); + $this->assertResults(array('2', '3')); + + // Filter by ID with the implicit IN operator. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', array('2', '3')) + ->execute(); + $this->assertResults(array('2', '3')); + + // Filter by ID with the > operator. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', '3', '>') + ->execute(); + $this->assertResults(array('4', '5')); + + // Filter by ID with the >= operator. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', '3', '>=') + ->execute(); + $this->assertResults(array('3', '4', '5')); + + // Filter by ID with the <> operator. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', '3', '<>') + ->execute(); + $this->assertResults(array('1', '2', '4', '5')); + + // Filter by ID with the < operator. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', '3', '<') + ->execute(); + $this->assertResults(array('1', '2')); + + // Filter by ID with the <= operator. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', '3', '<=') + ->execute(); + $this->assertResults(array('1', '2', '3')); + + // Filter by two conditions on the same field. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('label', 'test_pref', 'STARTS_WITH') + ->condition('label', 'test_prefix', 'STARTS_WITH') + ->execute(); + $this->assertResults(array('3')); + + // Filter by two conditions on different fields. The first query matches for + // a different ID, so the result is empty. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('label', 'test_prefix', 'STARTS_WITH') + ->condition('id', '5') + ->execute(); + $this->assertResults(array()); + + // Filter by two different conditions on different fields. This time the + // first condition matches on one item, but the second one does as well. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('label', 'test_prefix', 'STARTS_WITH') + ->condition('id', '3') + ->execute(); + $this->assertResults(array('3')); + + // Filter by two different conditions, of which the first one matches for + // every entry, the second one as well, but just the third one filters so + // that just two are left. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', '1', '>=') + ->condition('number', 10, '>=') + ->condition('number', 50, '>=') + ->execute(); + $this->assertResults(array('3', '5')); + + // Filter with an OR condition group. + $this->queryResults = $this->factory->get('config_query_test', 'OR') + ->condition('id', 1) + ->condition('id', '2') + ->execute(); + $this->assertResults(array('1', '2')); + + // Simplify it with IN. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', array('1', '2')) + ->execute(); + $this->assertResults(array('1', '2')); + // Try explicit IN. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', array('1', '2'), 'IN') + ->execute(); + $this->assertResults(array('1', '2')); + // Try not IN. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', array('1', '2'), 'NOT IN') + ->execute(); + $this->assertResults(array('3', '4', '5')); + + // Filter with an OR condition group on different fields. + $this->queryResults = $this->factory->get('config_query_test', 'OR') + ->condition('id', 1) + ->condition('number', 41) + ->execute(); + $this->assertResults(array('1', '2')); + + // Filter with an OR condition group on different fields but matching on the + // same entity. + $this->queryResults = $this->factory->get('config_query_test', 'OR') + ->condition('id', 1) + ->condition('number', 31) + ->execute(); + $this->assertResults(array('1')); + + // NO simple conditions, YES complex conditions, 'AND'. + $query = $this->factory->get('config_query_test', 'AND'); + $and_condition_1 = $query->orConditionGroup() + ->condition('id', '2') + ->condition('label', $this->entities[0]->label); + $and_condition_2 = $query->orConditionGroup() + ->condition('id', 1) + ->condition('label', $this->entities[3]->label); + $this->queryResults = $query + ->condition($and_condition_1) + ->condition($and_condition_2) + ->execute(); + $this->assertResults(array('1')); + + // NO simple conditions, YES complex conditions, 'OR'. + $query = $this->factory->get('config_query_test', 'OR'); + $and_condition_1 = $query->andConditionGroup() + ->condition('id', 1) + ->condition('label', $this->entities[0]->label); + $and_condition_2 = $query->andConditionGroup() + ->condition('id', '2') + ->condition('label', $this->entities[1]->label); + $this->queryResults = $query + ->condition($and_condition_1) + ->condition($and_condition_2) + ->execute(); + $this->assertResults(array('1', '2')); + + // YES simple conditions, YES complex conditions, 'AND'. + $query = $this->factory->get('config_query_test', 'AND'); + $and_condition_1 = $query->orConditionGroup() + ->condition('id', '2') + ->condition('label', $this->entities[0]->label); + $and_condition_2 = $query->orConditionGroup() + ->condition('id', 1) + ->condition('label', $this->entities[3]->label); + $this->queryResults = $query + ->condition('number', 31) + ->condition($and_condition_1) + ->condition($and_condition_2) + ->execute(); + $this->assertResults(array('1')); + + // YES simple conditions, YES complex conditions, 'OR'. + $query = $this->factory->get('config_query_test', 'OR'); + $and_condition_1 = $query->orConditionGroup() + ->condition('id', '2') + ->condition('label', $this->entities[0]->label); + $and_condition_2 = $query->orConditionGroup() + ->condition('id', 1) + ->condition('label', $this->entities[3]->label); + $this->queryResults = $query + ->condition('number', 53) + ->condition($and_condition_1) + ->condition($and_condition_2) + ->execute(); + $this->assertResults(array('1', '2', '4', '5')); + + // Test the exists and notExists conditions. + $this->queryResults = $this->factory->get('config_query_test') + ->exists('id') + ->execute(); + $this->assertResults(array('1', '2', '3', '4', '5')); + + $this->queryResults = $this->factory->get('config_query_test') + ->exists('non-existent') + ->execute(); + $this->assertResults(array()); + + $this->queryResults = $this->factory->get('config_query_test') + ->notExists('id') + ->execute(); + $this->assertResults(array()); + + $this->queryResults = $this->factory->get('config_query_test') + ->notExists('non-existent') + ->execute(); + $this->assertResults(array('1', '2', '3', '4', '5')); + } + + /** + * Tests ID conditions. + */ + public function testStringIdConditions() { + // We need an entity with a non-numeric ID. + $entity = ConfigQueryTest::create(array( + 'label' => $this->randomMachineName(), + 'id' => 'foo.bar', + )); + $this->entities[] = $entity; + $entity->enforceIsNew(); + $entity->save(); + + // Test 'STARTS_WITH' condition. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', 'foo.bar', 'STARTS_WITH') + ->execute(); + $this->assertResults(array('foo.bar')); + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', 'f', 'STARTS_WITH') + ->execute(); + $this->assertResults(array('foo.bar')); + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', 'miss', 'STARTS_WITH') + ->execute(); + $this->assertResults(array()); + + // Test 'CONTAINS' condition. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', 'foo.bar', 'CONTAINS') + ->execute(); + $this->assertResults(array('foo.bar')); + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', 'oo.ba', 'CONTAINS') + ->execute(); + $this->assertResults(array('foo.bar')); + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', 'miss', 'CONTAINS') + ->execute(); + $this->assertResults(array()); + + // Test 'ENDS_WITH' condition. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', 'foo.bar', 'ENDS_WITH') + ->execute(); + $this->assertResults(array('foo.bar')); + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', 'r', 'ENDS_WITH') + ->execute(); + $this->assertResults(array('foo.bar')); + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', 'miss', 'ENDS_WITH') + ->execute(); + $this->assertResults(array()); + } + + /** + * Tests count query. + */ + public function testCount() { + // Test count on no conditions. + $count = $this->factory->get('config_query_test') + ->count() + ->execute(); + $this->assertIdentical($count, count($this->entities)); + + // Test count on a complex query. + $query = $this->factory->get('config_query_test', 'OR'); + $and_condition_1 = $query->andConditionGroup() + ->condition('id', 1) + ->condition('label', $this->entities[0]->label); + $and_condition_2 = $query->andConditionGroup() + ->condition('id', '2') + ->condition('label', $this->entities[1]->label); + $count = $query + ->condition($and_condition_1) + ->condition($and_condition_2) + ->count() + ->execute(); + $this->assertIdentical($count, 2); + } + + /** + * Tests sorting and range on config entity queries. + */ + public function testSortRange() { + // Sort by simple ascending/descending. + $this->queryResults = $this->factory->get('config_query_test') + ->sort('number', 'DESC') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('3', '5', '2', '1', '4')); + + $this->queryResults = $this->factory->get('config_query_test') + ->sort('number', 'ASC') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('4', '1', '2', '5', '3')); + + // Apply some filters and sort. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', '3', '>') + ->sort('number', 'DESC') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('5', '4')); + + $this->queryResults = $this->factory->get('config_query_test') + ->condition('id', '3', '>') + ->sort('number', 'ASC') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('4', '5')); + + // Apply a pager and sort. + $this->queryResults = $this->factory->get('config_query_test') + ->sort('number', 'DESC') + ->range('2', '2') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('2', '1')); + + $this->queryResults = $this->factory->get('config_query_test') + ->sort('number', 'ASC') + ->range('2', '2') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('2', '5')); + + // Add a range to a query without a start parameter. + $this->queryResults = $this->factory->get('config_query_test') + ->range(0, '3') + ->sort('id', 'ASC') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('1', '2', '3')); + + // Apply a pager with limit 4. + $this->queryResults = $this->factory->get('config_query_test') + ->pager('4', 0) + ->sort('id', 'ASC') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('1', '2', '3', '4')); + } + + /** + * Tests sorting with tableSort on config entity queries. + */ + public function testTableSort() { + $header = array( + array('data' => t('ID'), 'specifier' => 'id'), + array('data' => t('Number'), 'specifier' => 'number'), + ); + + // Sort key: id + // Sorting with 'DESC' upper case + $this->queryResults = $this->factory->get('config_query_test') + ->tableSort($header) + ->sort('id', 'DESC') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('5', '4', '3', '2', '1')); + + // Sorting with 'ASC' upper case + $this->queryResults = $this->factory->get('config_query_test') + ->tableSort($header) + ->sort('id', 'ASC') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('1', '2', '3', '4', '5')); + + // Sorting with 'desc' lower case + $this->queryResults = $this->factory->get('config_query_test') + ->tableSort($header) + ->sort('id', 'desc') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('5', '4', '3', '2', '1')); + + // Sorting with 'asc' lower case + $this->queryResults = $this->factory->get('config_query_test') + ->tableSort($header) + ->sort('id', 'asc') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('1', '2', '3', '4', '5')); + + // Sort key: number + // Sorting with 'DeSc' mixed upper and lower case + $this->queryResults = $this->factory->get('config_query_test') + ->tableSort($header) + ->sort('number', 'DeSc') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('3', '5', '2', '1', '4')); + + // Sorting with 'AsC' mixed upper and lower case + $this->queryResults = $this->factory->get('config_query_test') + ->tableSort($header) + ->sort('number', 'AsC') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('4', '1', '2', '5', '3')); + + // Sorting with 'dEsC' mixed upper and lower case + $this->queryResults = $this->factory->get('config_query_test') + ->tableSort($header) + ->sort('number', 'dEsC') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('3', '5', '2', '1', '4')); + + // Sorting with 'aSc' mixed upper and lower case + $this->queryResults = $this->factory->get('config_query_test') + ->tableSort($header) + ->sort('number', 'aSc') + ->execute(); + $this->assertIdentical(array_values($this->queryResults), array('4', '1', '2', '5', '3')); + } + + /** + * Tests dotted path matching. + */ + public function testDotted() { + $this->queryResults = $this->factory->get('config_query_test') + ->condition('array.level1.*', 1) + ->execute(); + $this->assertResults(array('1', '3')); + $this->queryResults = $this->factory->get('config_query_test') + ->condition('*.level1.level2', 2) + ->execute(); + $this->assertResults(array('2', '4')); + $this->queryResults = $this->factory->get('config_query_test') + ->condition('array.level1.*', 3) + ->execute(); + $this->assertResults(array('5')); + $this->queryResults = $this->factory->get('config_query_test') + ->condition('array.level1.level2', 3) + ->execute(); + $this->assertResults(array('5')); + // Make sure that values on the wildcard level do not match if there are + // sub-keys defined. This must not find anything even if entity 2 has a + // top-level key number with value 41. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('*.level1.level2', 41) + ->execute(); + $this->assertResults(array()); + } + + /** + * Tests case sensitivity. + */ + public function testCaseSensitivity() { + // Filter by label with a known containing case-sensitive word. + $this->queryResults = $this->factory->get('config_query_test') + ->condition('label', 'TEST', 'CONTAINS') + ->execute(); + $this->assertResults(array('3', '4', '5')); + + $this->queryResults = $this->factory->get('config_query_test') + ->condition('label', 'test', 'CONTAINS') + ->execute(); + $this->assertResults(array('3', '4', '5')); + } + + /** + * Tests lookup keys are added to the key value store. + */ + public function testLookupKeys() { + \Drupal::service('state')->set('config_test.lookup_keys', TRUE); + \Drupal::entityManager()->clearCachedDefinitions(); + $key_value = $this->container->get('keyvalue')->get(QueryFactory::CONFIG_LOOKUP_PREFIX . 'config_test'); + + $test_entities = []; + $entity = entity_create('config_test', array( + 'label' => $this->randomMachineName(), + 'id' => '1', + 'style' => 'test', + )); + $test_entities[$entity->getConfigDependencyName()] = $entity; + $entity->enforceIsNew(); + $entity->save(); + + + $expected[] = $entity->getConfigDependencyName(); + $this->assertEqual($expected, $key_value->get('style:test')); + + $entity = entity_create('config_test', array( + 'label' => $this->randomMachineName(), + 'id' => '2', + 'style' => 'test', + )); + $test_entities[$entity->getConfigDependencyName()] = $entity; + $entity->enforceIsNew(); + $entity->save(); + $expected[] = $entity->getConfigDependencyName(); + $this->assertEqual($expected, $key_value->get('style:test')); + + $entity = entity_create('config_test', array( + 'label' => $this->randomMachineName(), + 'id' => '3', + 'style' => 'blah', + )); + $entity->enforceIsNew(); + $entity->save(); + // Do not add this entity to the list of expected result as it has a + // different value. + $this->assertEqual($expected, $key_value->get('style:test')); + $this->assertEqual([$entity->getConfigDependencyName()], $key_value->get('style:blah')); + + // Ensure that a delete clears a key. + $entity->delete(); + $this->assertEqual(NULL, $key_value->get('style:blah')); + + // Ensure that delete only clears one key. + $entity_id = array_pop($expected); + $test_entities[$entity_id]->delete(); + $this->assertEqual($expected, $key_value->get('style:test')); + $entity_id = array_pop($expected); + $test_entities[$entity_id]->delete(); + $this->assertEqual(NULL, $key_value->get('style:test')); + } + + /** + * Asserts the results as expected regardless of order. + * + * @param array $expected + * Array of expected entity IDs. + */ + protected function assertResults($expected) { + $this->assertIdentical(count($this->queryResults), count($expected)); + foreach ($expected as $value) { + // This also tests whether $this->queryResults[$value] is even set at all. + $this->assertIdentical($this->queryResults[$value], $value); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityChangedTest.php b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityChangedTest.php new file mode 100644 index 0000000..eddb268 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityChangedTest.php @@ -0,0 +1,545 @@ +save(); + ConfigurableLanguage::createFromLangcode('fr')->save(); + + $this->installEntitySchema('entity_test_mul_changed'); + $this->installEntitySchema('entity_test_mulrev_changed'); + + $this->mulChangedStorage = $this->entityManager->getStorage('entity_test_mul_changed'); + $this->mulRevChangedStorage = $this->entityManager->getStorage('entity_test_mulrev_changed'); + } + + /** + * Tests basic EntityChangedInterface functionality. + */ + public function testChanged() { + $user1 = $this->createUser(); + $user2 = $this->createUser(); + + // Create a test entity. + $entity = EntityTestMulChanged::create(array( + 'name' => $this->randomString(), + 'user_id' => $user1->id(), + 'language' => 'en', + )); + $entity->save(); + + $this->assertTrue( + $entity->getChangedTime() >= REQUEST_TIME, + 'Changed time of original language is valid.' + ); + + // We can't assert equality here because the created time is set to the + // request time, while instances of ChangedTestItem use the current + // timestamp every time. Therefor we check if the changed timestamp is + // between the created time and now. + $this->assertTrue( + ($entity->getChangedTime() >= $entity->get('created')->value) && + (($entity->getChangedTime() - $entity->get('created')->value) <= time() - REQUEST_TIME), + 'Changed and created time of original language can be assumed to be identical.' + ); + + $this->assertEqual( + $entity->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), + 'Changed time of original language is the same as changed time across all translations.' + ); + + $changed_en = $entity->getChangedTime(); + + /** @var \Drupal\entity_test\Entity\EntityTestMulRevChanged $german */ + $german = $entity->addTranslation('de'); + + $entity->save(); + + $this->assertEqual( + $entity->getChangedTime(), $changed_en, + 'Changed time of original language did not change.' + ); + + $this->assertTrue( + $german->getChangedTime() > $entity->getChangedTime(), + 'Changed time of the German translation is newer then the original language.' + ); + + $this->assertEqual( + $german->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), + 'Changed time of the German translation is the newest time across all translations.' + ); + + $changed_de = $german->getChangedTime(); + + $entity->save(); + + $this->assertEqual( + $entity->getChangedTime(), $changed_en, + 'Changed time of original language did not change.' + ); + + $this->assertEqual( + $german->getChangedTime(), $changed_de, + 'Changed time of the German translation did not change.' + ); + + $entity->setOwner($user2); + + $entity->save(); + + $this->assertTrue( + $entity->getChangedTime() > $changed_en, + 'Changed time of original language did change.' + ); + + $this->assertEqual( + $german->getChangedTime(), $changed_de, + 'Changed time of the German translation did not change.' + ); + + $this->assertTrue( + $entity->getChangedTime() > $german->getChangedTime(), + 'Changed time of original language is newer then the German translation.' + ); + + $this->assertEqual( + $entity->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), + 'Changed time of the original language is the newest time across all translations.' + ); + + $changed_en = $entity->getChangedTime(); + + // Save entity without any changes. + $entity->save(); + + $this->assertEqual( + $entity->getChangedTime(), $changed_en, + 'Changed time of original language did not change.' + ); + + $this->assertEqual( + $german->getChangedTime(), $changed_de, + 'Changed time of the German translation did not change.' + ); + + // At this point the changed time of the original language (en) is newer + // than the changed time of the German translation. Now test that entity + // queries work as expected. + $query = $this->mulChangedStorage->getQuery(); + $ids = $query->condition('changed', $changed_en)->execute(); + + $this->assertEqual( + reset($ids), $entity->id(), + 'Entity query can access changed time of original language.' + ); + + $query = $this->mulChangedStorage->getQuery(); + $ids = $query->condition('changed', $changed_en, '=', 'en')->execute(); + + $this->assertEqual( + reset($ids), $entity->id(), + 'Entity query can access changed time of original language by setting the original language as condition.' + ); + + $query = $this->mulChangedStorage->getQuery(); + $ids = $query->condition('changed', $changed_de, '=', 'en')->execute(); + + $this->assertFalse( + $ids, + 'There\'s no original entity stored having the changed time of the German translation.' + ); + + $query = $this->mulChangedStorage->getQuery(); + $ids = $query->condition('changed', $changed_en)->condition('default_langcode', '1')->execute(); + + $this->assertEqual( + reset($ids), $entity->id(), + 'Entity query can access changed time of default language.' + ); + + $query = $this->mulChangedStorage->getQuery(); + $ids = $query->condition('changed', $changed_de)->condition('default_langcode', '1')->execute(); + + $this->assertFalse( + $ids, + 'There\'s no entity stored using the default language having the changed time of the German translation.' + ); + + $query = $this->mulChangedStorage->getQuery(); + $ids = $query->condition('changed', $changed_de)->execute(); + + $this->assertEqual( + reset($ids), $entity->id(), + 'Entity query can access changed time of the German translation.' + ); + + $query = $this->mulChangedStorage->getQuery(); + $ids = $query->condition('changed', $changed_de, '=', 'de')->execute(); + + $this->assertEqual( + reset($ids), $entity->id(), + 'Entity query can access changed time of the German translation.' + ); + + $query = $this->mulChangedStorage->getQuery(); + $ids = $query->condition('changed', $changed_en, '=', 'de')->execute(); + + $this->assertFalse( + $ids, + 'There\'s no German translation stored having the changed time of the original language.' + ); + + $query = $this->mulChangedStorage->getQuery(); + $ids = $query->condition('changed', $changed_de, '>')->execute(); + + $this->assertEqual( + reset($ids), $entity->id(), + 'Entity query can access changed time regardless of translation.' + ); + + $query = $this->mulChangedStorage->getQuery(); + $ids = $query->condition('changed', $changed_en, '<')->execute(); + + $this->assertEqual( + reset($ids), $entity->id(), + 'Entity query can access changed time regardless of translation.' + ); + + $query = $this->mulChangedStorage->getQuery(); + $ids = $query->condition('changed', 0, '>')->execute(); + + $this->assertEqual( + reset($ids), $entity->id(), + 'Entity query can access changed time regardless of translation.' + ); + + $query = $this->mulChangedStorage->getQuery(); + $ids = $query->condition('changed', $changed_en, '>')->execute(); + + $this->assertFalse( + $ids, + 'Entity query can access changed time regardless of translation.' + ); + } + + /** + * Tests revisionable EntityChangedInterface functionality. + */ + public function testRevisionChanged() { + $user1 = $this->createUser(); + $user2 = $this->createUser(); + + // Create a test entity. + $entity = EntityTestMulRevChanged::create(array( + 'name' => $this->randomString(), + 'user_id' => $user1->id(), + 'language' => 'en', + )); + $entity->save(); + + $this->assertTrue( + $entity->getChangedTime() >= REQUEST_TIME, + 'Changed time of original language is valid.' + ); + + // We can't assert equality here because the created time is set to the + // request time while instances of ChangedTestItem use the current + // timestamp every time. + $this->assertTrue( + ($entity->getChangedTime() >= $entity->get('created')->value) && + (($entity->getChangedTime() - $entity->get('created')->value) <= time() - REQUEST_TIME), + 'Changed and created time of original language can be assumed to be identical.' + ); + + $this->assertEqual( + $entity->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), + 'Changed time of original language is the same as changed time across all translations.' + ); + + $this->assertTrue( + $this->getRevisionTranslationAffectedFlag($entity), + 'Changed flag of original language is set for a new entity.' + ); + + $changed_en = $entity->getChangedTime(); + + $entity->setNewRevision(); + // Save entity without any changes but create new revision. + $entity->save(); + // A new revision without any changes should not set a new changed time. + $this->assertEqual( + $entity->getChangedTime(), $changed_en, + 'Changed time of original language did not change.' + ); + + $this->assertFalse( + $this->getRevisionTranslationAffectedFlag($entity), + 'Changed flag of original language is not set for new revision without changes.' + ); + + $entity->setNewRevision(); + $entity->setOwner($user2); + $entity->save(); + + $this->assertTrue( + $entity->getChangedTime() > $changed_en, + 'Changed time of original language has been updated by new revision.' + ); + + $this->assertTrue( + $this->getRevisionTranslationAffectedFlag($entity), + 'Changed flag of original language is set for new revision with changes.' + ); + + $changed_en = $entity->getChangedTime(); + + /** @var \Drupal\entity_test\Entity\EntityTestMulRevChanged $german */ + $german = $entity->addTranslation('de'); + + $entity->save(); + + $this->assertEqual( + $entity->getChangedTime(), $changed_en, + 'Changed time of original language did not change.' + ); + + $this->assertTrue( + $german->getChangedTime() > $entity->getChangedTime(), + 'Changed time of the German translation is newer then the original language.' + ); + + $this->assertEqual( + $german->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), + 'Changed time of the German translation is the newest time across all translations.' + ); + + $this->assertTrue( + $this->getRevisionTranslationAffectedFlag($entity), + 'Changed flag of original language is not reset by adding a new translation.' + ); + + $this->assertTrue( + $this->getRevisionTranslationAffectedFlag($german), + 'Changed flag of German translation is set when adding the translation.' + ); + + $changed_de = $german->getChangedTime(); + + $entity->setNewRevision(); + // Save entity without any changes but create new revision. + $entity->save(); + + $this->assertEqual( + $entity->getChangedTime(), $changed_en, + 'Changed time of original language did not change.' + ); + + $this->assertEqual( + $german->getChangedTime(), $changed_de, + 'Changed time of the German translation did not change.' + ); + + $this->assertFalse( + $this->getRevisionTranslationAffectedFlag($entity), + 'Changed flag of original language is not set for new revision without changes.' + ); + + $this->assertFalse( + $this->getRevisionTranslationAffectedFlag($german), + 'Changed flag of the German translation is not set for new revision without changes.' + ); + + $entity->setNewRevision(); + $german->setOwner($user2); + $entity->save(); + + $this->assertEqual( + $entity->getChangedTime(), $changed_en, + 'Changed time of original language did not change.' + ); + + $this->assertTrue( + $german->getChangedTime() > $changed_de, + 'Changed time of the German translation did change.' + ); + + $this->assertEqual( + $german->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), + 'Changed time of the German translation is the newest time across all translations.' + ); + + $this->assertFalse( + $this->getRevisionTranslationAffectedFlag($entity), + 'Changed flag of original language is not set when changing the German Translation.' + ); + + $this->assertTrue( + $this->getRevisionTranslationAffectedFlag($german), + 'Changed flag of German translation is set when changing the German translation.' + ); + + $french = $entity->addTranslation('fr'); + + $entity->setNewRevision(); + $entity->save(); + + $this->assertEqual( + $entity->getChangedTime(), $changed_en, + 'Changed time of original language did not change.' + ); + + $this->assertTrue( + $french->getChangedTime() > $entity->getChangedTime(), + 'Changed time of the French translation is newer then the original language.' + ); + + $this->assertTrue( + $french->getChangedTime() > $entity->getChangedTime(), + 'Changed time of the French translation is newer then the German translation.' + ); + + $this->assertEqual( + $french->getChangedTime(), $entity->getChangedTimeAcrossTranslations(), + 'Changed time of the French translation is the newest time across all translations.' + ); + + $this->assertFalse( + $this->getRevisionTranslationAffectedFlag($entity), + 'Changed flag of original language is reset by adding a new translation and a new revision.' + ); + + $this->assertFalse( + $this->getRevisionTranslationAffectedFlag($german), + 'Changed flag of German translation is reset by adding a new translation and a new revision.' + ); + + $this->assertTrue( + $this->getRevisionTranslationAffectedFlag($french), + 'Changed flag of French translation is set when adding the translation and a new revision.' + ); + + $entity->removeTranslation('fr'); + + $entity->setNewRevision(); + $entity->save(); + + // This block simulates exactly the flow of a node form submission of a new + // translation and a new revision. + $form_entity_builder_entity = EntityTestMulRevChanged::load($entity->id()); + // ContentTranslationController::prepareTranslation(). + $form_entity_builder_entity = $form_entity_builder_entity->addTranslation('fr', $form_entity_builder_entity->toArray()); + // EntityForm::buildEntity() during form submit. + $form_entity_builder_clone = clone $form_entity_builder_entity; + // NodeForm::submitForm(). + $form_entity_builder_clone->setNewRevision(); + // EntityForm::save(). + $form_entity_builder_clone->save(); + + // The assertion fails unless https://www.drupal.org/node/2513094 is + // committed. + $this->assertFalse( + $this->getRevisionTranslationAffectedFlag($entity), + 'Changed flag of original language is reset by adding a new translation and a new revision.' + ); + + $this->assertFalse( + $this->getRevisionTranslationAffectedFlag($german), + 'Changed flag of German translation is reset by adding a new translation and a new revision.' + ); + + $this->assertTrue( + $this->getRevisionTranslationAffectedFlag($french), + 'Changed flag of French translation is set when adding the translation and a new revision.' + ); + + $german->setOwner($user1); + $german->setRevisionTranslationAffected(FALSE); + $entity->save(); + + $this->assertFalse( + $this->getRevisionTranslationAffectedFlag($german), + 'German translation changed but the changed flag is reset manually.' + ); + + $entity->setNewRevision(); + $german->setRevisionTranslationAffected(TRUE); + $entity->save(); + + $this->assertTrue( + $this->getRevisionTranslationAffectedFlag($german), + 'German translation is not changed and a new revision is created but the changed flag is set manually.' + ); + + $german->setOwner($user2); + $entity->setNewRevision(); + $german->setRevisionTranslationAffected(FALSE); + $entity->save(); + + $this->assertFalse( + $this->getRevisionTranslationAffectedFlag($german), + 'German translation changed and a new revision is created but the changed flag is reset manually.' + ); + + } + + /** + * Retrieves the revision translation affected flag value. + * + * @param \Drupal\entity_test\Entity\EntityTestMulRevChanged $entity + * The entity object to be checked. + * + * @return bool + * The flag value. + */ + protected function getRevisionTranslationAffectedFlag(EntityTestMulRevChanged $entity) { + $query = $this->mulRevChangedStorage->getQuery(); + $ids = $query->condition('revision_translation_affected', 1, '=', $entity->language()->getId())->execute(); + $id = reset($ids); + return (bool) ($id == $entity->id()); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityCloneTest.php b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityCloneTest.php new file mode 100644 index 0000000..742708a --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityCloneTest.php @@ -0,0 +1,70 @@ +save(); + + $this->installEntitySchema('entity_test_mul'); + } + + /** + * Tests if entity references on fields are still correct after cloning. + */ + public function testFieldEntityReferenceAfterClone() { + $user = $this->createUser(); + + // Create a test entity. + $entity = EntityTestMul::create([ + 'name' => $this->randomString(), + 'user_id' => $user->id(), + 'language' => 'en', + ]); + + $clone = clone $entity->addTranslation('de'); + + $this->assertEqual($entity->getTranslationLanguages(), $clone->getTranslationLanguages(), 'The entity and its clone have the same translation languages.'); + + $default_langcode = $entity->getUntranslated()->language()->getId(); + foreach (array_keys($clone->getTranslationLanguages()) as $langcode) { + $translation = $clone->getTranslation($langcode); + foreach ($translation->getFields() as $field_name => $field) { + if ($field->getFieldDefinition()->isTranslatable()) { + $args = ['%field_name' => $field_name, '%langcode' => $langcode]; + $this->assertEqual($langcode, $field->getEntity()->language()->getId(), format_string('Translatable field %field_name on translation %langcode has correct entity reference in translation %langcode after cloning.', $args)); + } + else { + $args = ['%field_name' => $field_name, '%langcode' => $langcode, '%default_langcode' => $default_langcode]; + $this->assertEqual($default_langcode, $field->getEntity()->language()->getId(), format_string('Non translatable field %field_name on translation %langcode has correct entity reference in the default translation %default_langcode after cloning.', $args)); + } + } + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityNullStorageTest.php b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityNullStorageTest.php new file mode 100644 index 0000000..6d4b70c --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/ContentEntityNullStorageTest.php @@ -0,0 +1,83 @@ +assertIdentical(0, \Drupal::entityQuery('contact_message')->count()->execute(), 'Counting a null storage returns 0.'); + $this->assertIdentical([], \Drupal::entityQuery('contact_message')->execute(), 'Querying a null storage returns an empty array.'); + $this->assertIdentical([], \Drupal::entityQuery('contact_message')->condition('contact_form', 'test')->execute(), 'Querying a null storage returns an empty array and conditions are ignored.'); + $this->assertIdentical([], \Drupal::entityQueryAggregate('contact_message')->aggregate('name', 'AVG')->execute(), 'Aggregate querying a null storage returns an empty array'); + + } + + /** + * Tests deleting a contact form entity via a configuration import. + * + * @see \Drupal\Core\Entity\Event\BundleConfigImportValidate + */ + public function testDeleteThroughImport() { + $contact_form = ContactForm::create(['id' => 'test']); + $contact_form->save(); + + $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.sync')); + + // Set up the ConfigImporter object for testing. + $storage_comparer = new StorageComparer( + $this->container->get('config.storage.sync'), + $this->container->get('config.storage'), + $this->container->get('config.manager') + ); + $config_importer = new ConfigImporter( + $storage_comparer->createChangelist(), + $this->container->get('event_dispatcher'), + $this->container->get('config.manager'), + $this->container->get('lock'), + $this->container->get('config.typed'), + $this->container->get('module_handler'), + $this->container->get('module_installer'), + $this->container->get('theme_handler'), + $this->container->get('string_translation') + ); + + // Delete the contact message in sync. + $sync = $this->container->get('config.storage.sync'); + $sync->delete($contact_form->getConfigDependencyName()); + + // Import. + $config_importer->reset()->import(); + $this->assertNull(ContactForm::load($contact_form->id()), 'The contact form has been deleted.'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php new file mode 100644 index 0000000..2622910 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php @@ -0,0 +1,332 @@ +installSchema('system', ['key_value_expire']); + \Drupal::service('router.builder')->rebuild(); + + $this->testUser = User::create(array( + 'name' => 'foobar1', + 'mail' => 'foobar1@example.com', + )); + $this->testUser->save(); + \Drupal::service('current_user')->setAccount($this->testUser); + + $this->testAutocreateUser = User::create(array( + 'name' => 'foobar2', + 'mail' => 'foobar2@example.com', + )); + $this->testAutocreateUser->save(); + + for ($i = 1; $i < 3; $i++) { + $entity = EntityTest::create(array( + 'name' => $this->randomMachineName() + )); + $entity->save(); + $this->referencedEntities[] = $entity; + } + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'test_entity_autocomplete'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $form['single'] = array( + '#type' => 'entity_autocomplete', + '#target_type' => 'entity_test', + ); + $form['single_autocreate'] = array( + '#type' => 'entity_autocomplete', + '#target_type' => 'entity_test', + '#autocreate' => array( + 'bundle' => 'entity_test', + ), + ); + $form['single_autocreate_specific_uid'] = array( + '#type' => 'entity_autocomplete', + '#target_type' => 'entity_test', + '#autocreate' => array( + 'bundle' => 'entity_test', + 'uid' => $this->testAutocreateUser->id(), + ), + ); + + $form['tags'] = array( + '#type' => 'entity_autocomplete', + '#target_type' => 'entity_test', + '#tags' => TRUE, + ); + $form['tags_autocreate'] = array( + '#type' => 'entity_autocomplete', + '#target_type' => 'entity_test', + '#tags' => TRUE, + '#autocreate' => array( + 'bundle' => 'entity_test', + ), + ); + $form['tags_autocreate_specific_uid'] = array( + '#type' => 'entity_autocomplete', + '#target_type' => 'entity_test', + '#tags' => TRUE, + '#autocreate' => array( + 'bundle' => 'entity_test', + 'uid' => $this->testAutocreateUser->id(), + ), + ); + + $form['single_no_validate'] = array( + '#type' => 'entity_autocomplete', + '#target_type' => 'entity_test', + '#validate_reference' => FALSE, + ); + $form['single_autocreate_no_validate'] = array( + '#type' => 'entity_autocomplete', + '#target_type' => 'entity_test', + '#validate_reference' => FALSE, + '#autocreate' => array( + 'bundle' => 'entity_test', + ), + ); + + $form['single_access'] = array( + '#type' => 'entity_autocomplete', + '#target_type' => 'entity_test', + '#default_value' => $this->referencedEntities[0], + ); + $form['tags_access'] = array( + '#type' => 'entity_autocomplete', + '#target_type' => 'entity_test', + '#tags' => TRUE, + '#default_value' => array($this->referencedEntities[0], $this->referencedEntities[1]), + ); + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { } + + /** + * Tests valid entries in the EntityAutocomplete Form API element. + */ + public function testValidEntityAutocompleteElement() { + $form_state = (new FormState()) + ->setValues([ + 'single' => $this->getAutocompleteInput($this->referencedEntities[0]), + 'single_autocreate' => 'single - autocreated entity label', + 'single_autocreate_specific_uid' => 'single - autocreated entity label with specific uid', + 'tags' => $this->getAutocompleteInput($this->referencedEntities[0]) . ', ' . $this->getAutocompleteInput($this->referencedEntities[1]), + 'tags_autocreate' => + $this->getAutocompleteInput($this->referencedEntities[0]) + . ', tags - autocreated entity label, ' + . $this->getAutocompleteInput($this->referencedEntities[1]), + 'tags_autocreate_specific_uid' => + $this->getAutocompleteInput($this->referencedEntities[0]) + . ', tags - autocreated entity label with specific uid, ' + . $this->getAutocompleteInput($this->referencedEntities[1]), + ]); + $form_builder = $this->container->get('form_builder'); + $form_builder->submitForm($this, $form_state); + + // Valid form state. + $this->assertEqual(count($form_state->getErrors()), 0); + + // Test the 'single' element. + $this->assertEqual($form_state->getValue('single'), $this->referencedEntities[0]->id()); + + // Test the 'single_autocreate' element. + $value = $form_state->getValue('single_autocreate'); + $this->assertEqual($value['entity']->label(), 'single - autocreated entity label'); + $this->assertEqual($value['entity']->bundle(), 'entity_test'); + $this->assertEqual($value['entity']->getOwnerId(), $this->testUser->id()); + + // Test the 'single_autocreate_specific_uid' element. + $value = $form_state->getValue('single_autocreate_specific_uid'); + $this->assertEqual($value['entity']->label(), 'single - autocreated entity label with specific uid'); + $this->assertEqual($value['entity']->bundle(), 'entity_test'); + $this->assertEqual($value['entity']->getOwnerId(), $this->testAutocreateUser->id()); + + // Test the 'tags' element. + $expected = array( + array('target_id' => $this->referencedEntities[0]->id()), + array('target_id' => $this->referencedEntities[1]->id()), + ); + $this->assertEqual($form_state->getValue('tags'), $expected); + + // Test the 'single_autocreate' element. + $value = $form_state->getValue('tags_autocreate'); + // First value is an existing entity. + $this->assertEqual($value[0]['target_id'], $this->referencedEntities[0]->id()); + // Second value is an autocreated entity. + $this->assertTrue(!isset($value[1]['target_id'])); + $this->assertEqual($value[1]['entity']->label(), 'tags - autocreated entity label'); + $this->assertEqual($value[1]['entity']->getOwnerId(), $this->testUser->id()); + // Third value is an existing entity. + $this->assertEqual($value[2]['target_id'], $this->referencedEntities[1]->id()); + + // Test the 'tags_autocreate_specific_uid' element. + $value = $form_state->getValue('tags_autocreate_specific_uid'); + // First value is an existing entity. + $this->assertEqual($value[0]['target_id'], $this->referencedEntities[0]->id()); + // Second value is an autocreated entity. + $this->assertTrue(!isset($value[1]['target_id'])); + $this->assertEqual($value[1]['entity']->label(), 'tags - autocreated entity label with specific uid'); + $this->assertEqual($value[1]['entity']->getOwnerId(), $this->testAutocreateUser->id()); + // Third value is an existing entity. + $this->assertEqual($value[2]['target_id'], $this->referencedEntities[1]->id()); + } + + /** + * Tests invalid entries in the EntityAutocomplete Form API element. + */ + public function testInvalidEntityAutocompleteElement() { + $form_builder = $this->container->get('form_builder'); + + // Test 'single' with a entity label that doesn't exist + $form_state = (new FormState()) + ->setValues([ + 'single' => 'single - non-existent label', + ]); + $form_builder->submitForm($this, $form_state); + $this->assertEqual(count($form_state->getErrors()), 1); + $this->assertEqual($form_state->getErrors()['single'], t('There are no entities matching "%value".', array('%value' => 'single - non-existent label'))); + + // Test 'single' with a entity ID that doesn't exist. + $form_state = (new FormState()) + ->setValues([ + 'single' => 'single - non-existent label (42)', + ]); + $form_builder->submitForm($this, $form_state); + $this->assertEqual(count($form_state->getErrors()), 1); + $this->assertEqual($form_state->getErrors()['single'], t('The referenced entity (%type: %id) does not exist.', array('%type' => 'entity_test', '%id' => 42))); + + // Do the same tests as above but on an element with '#validate_reference' + // set to FALSE. + $form_state = (new FormState()) + ->setValues([ + 'single_no_validate' => 'single - non-existent label', + 'single_autocreate_no_validate' => 'single - autocreate non-existent label' + ]); + $form_builder->submitForm($this, $form_state); + + // The element without 'autocreate' support still has to emit a warning when + // the input doesn't end with an entity ID enclosed in parentheses. + $this->assertEqual(count($form_state->getErrors()), 1); + $this->assertEqual($form_state->getErrors()['single_no_validate'], t('There are no entities matching "%value".', array('%value' => 'single - non-existent label'))); + + $form_state = (new FormState()) + ->setValues([ + 'single_no_validate' => 'single - non-existent label (42)', + 'single_autocreate_no_validate' => 'single - autocreate non-existent label (43)' + ]); + $form_builder->submitForm($this, $form_state); + + // The input is complete (i.e. contains an entity ID at the end), no errors + // are triggered. + $this->assertEqual(count($form_state->getErrors()), 0); + } + + /** + * Tests that access is properly checked by the EntityAutocomplete element. + */ + public function testEntityAutocompleteAccess() { + $form_builder = $this->container->get('form_builder'); + $form = $form_builder->getForm($this); + + // Check that the current user has proper access to view entity labels. + $expected = $this->referencedEntities[0]->label() . ' (' . $this->referencedEntities[0]->id() . ')'; + $this->assertEqual($form['single_access']['#value'], $expected); + + $expected .= ', ' . $this->referencedEntities[1]->label() . ' (' . $this->referencedEntities[1]->id() . ')'; + $this->assertEqual($form['tags_access']['#value'], $expected); + + // Set up a non-admin user that is *not* allowed to view test entities. + \Drupal::currentUser()->setAccount($this->createUser(array(), array())); + + // Rebuild the form. + $form = $form_builder->getForm($this); + + $expected = t('- Restricted access -') . ' (' . $this->referencedEntities[0]->id() . ')'; + $this->assertEqual($form['single_access']['#value'], $expected); + + $expected .= ', ' . t('- Restricted access -') . ' (' . $this->referencedEntities[1]->id() . ')'; + $this->assertEqual($form['tags_access']['#value'], $expected); + } + + /** + * Returns an entity label in the format needed by the EntityAutocomplete + * element. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * A Drupal entity. + * + * @return string + * A string that can be used as a value for EntityAutocomplete elements. + */ + protected function getAutocompleteInput(EntityInterface $entity) { + return EntityAutocomplete::getEntityLabels(array($entity)); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityAccessControlHandlerTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityAccessControlHandlerTest.php new file mode 100644 index 0000000..8e52453 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityAccessControlHandlerTest.php @@ -0,0 +1,230 @@ + $result) { + $message = format_string("Entity access returns @result with operation '@op'.", array( + '@result' => !isset($result) ? 'null' : ($result ? 'true' : 'false'), + '@op' => $op, + )); + + $this->assertEqual($result, $object->access($op, $account), $message); + } + } + + /** + * Ensures user labels are accessible for everyone. + */ + public function testUserLabelAccess() { + // Set up a non-admin user. + \Drupal::currentUser()->setAccount($this->createUser(['uid' => 2])); + + $anonymous_user = User::getAnonymousUser(); + $user = $this->createUser(); + + // The current user is allowed to view the anonymous user label. + $this->assertEntityAccess(array( + 'create' => FALSE, + 'update' => FALSE, + 'delete' => FALSE, + 'view' => FALSE, + 'view label' => TRUE, + ), $anonymous_user); + + // The current user is allowed to view user labels. + $this->assertEntityAccess(array( + 'create' => FALSE, + 'update' => FALSE, + 'delete' => FALSE, + 'view' => FALSE, + 'view label' => TRUE, + ), $user); + + // Switch to a anonymous user account. + $account_switcher = \Drupal::service('account_switcher'); + $account_switcher->switchTo(new AnonymousUserSession()); + + // The anonymous user is allowed to view the anonymous user label. + $this->assertEntityAccess(array( + 'create' => FALSE, + 'update' => FALSE, + 'delete' => FALSE, + 'view' => FALSE, + 'view label' => TRUE, + ), $anonymous_user); + + // The anonymous user is allowed to view user labels. + $this->assertEntityAccess(array( + 'create' => FALSE, + 'update' => FALSE, + 'delete' => FALSE, + 'view' => FALSE, + 'view label' => TRUE, + ), $user); + + // Restore user account. + $account_switcher->switchBack(); + } + + /** + * Ensures entity access is properly working. + */ + function testEntityAccess() { + // Set up a non-admin user that is allowed to view test entities. + \Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2), array('view test entity'))); + + // Use the 'entity_test_label' entity type in order to test the 'view label' + // access operation. + $entity = EntityTestLabel::create(array( + 'name' => 'test', + )); + + // The current user is allowed to view entities. + $this->assertEntityAccess(array( + 'create' => FALSE, + 'update' => FALSE, + 'delete' => FALSE, + 'view' => TRUE, + 'view label' => TRUE, + ), $entity); + + // The custom user is not allowed to perform any operation on test entities, + // except for viewing their label. + $custom_user = $this->createUser(); + $this->assertEntityAccess(array( + 'create' => FALSE, + 'update' => FALSE, + 'delete' => FALSE, + 'view' => FALSE, + 'view label' => TRUE, + ), $entity, $custom_user); + } + + /** + * Ensures default entity access is checked when necessary. + * + * This ensures that the default checkAccess() implementation of the + * entity access control handler is considered if hook_entity_access() has not + * explicitly forbidden access. Therefore the default checkAccess() + * implementation can forbid access, even after access was already explicitly + * allowed by hook_entity_access(). + * + * @see \Drupal\entity_test\EntityTestAccessControlHandler::checkAccess() + * @see entity_test_entity_access() + */ + function testDefaultEntityAccess() { + // Set up a non-admin user that is allowed to view test entities. + \Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2), array('view test entity'))); + $entity = EntityTest::create(array( + 'name' => 'forbid_access', + )); + + // The user is denied access to the entity. + $this->assertEntityAccess(array( + 'create' => FALSE, + 'update' => FALSE, + 'delete' => FALSE, + 'view' => FALSE, + ), $entity); + } + + /** + * Ensures that the default handler is used as a fallback. + */ + function testEntityAccessDefaultController() { + // The implementation requires that the global user id can be loaded. + \Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2))); + + // Check that the default access control handler is used for entities that don't + // have a specific access control handler defined. + $handler = $this->container->get('entity.manager')->getAccessControlHandler('entity_test_default_access'); + $this->assertTrue($handler instanceof EntityAccessControlHandler, 'The default entity handler is used for the entity_test_default_access entity type.'); + + $entity = EntityTestDefaultAccess::create(); + $this->assertEntityAccess(array( + 'create' => FALSE, + 'update' => FALSE, + 'delete' => FALSE, + 'view' => FALSE, + ), $entity); + } + + /** + * Ensures entity access for entity translations is properly working. + */ + function testEntityTranslationAccess() { + + // Set up a non-admin user that is allowed to view test entity translations. + \Drupal::currentUser()->setAccount($this->createUser(array('uid' => 2), array('view test entity translations'))); + + // Create two test languages. + foreach (array('foo', 'bar') as $langcode) { + ConfigurableLanguage::create(array( + 'id' => $langcode, + 'label' => $this->randomString(), + ))->save(); + } + + $entity = EntityTest::create(array( + 'name' => 'test', + 'langcode' => 'foo', + )); + $entity->save(); + + $translation = $entity->addTranslation('bar'); + $this->assertEntityAccess(array( + 'view' => TRUE, + ), $translation); + } + + /** + * Tests hook invocations. + */ + public function testHooks() { + $state = $this->container->get('state'); + $entity = EntityTest::create(array( + 'name' => 'test', + )); + + // Test hook_entity_create_access() and hook_ENTITY_TYPE_create_access(). + $entity->access('create'); + $this->assertEqual($state->get('entity_test_entity_create_access'), TRUE); + $this->assertIdentical($state->get('entity_test_entity_create_access_context'), [ + 'entity_type_id' => 'entity_test', + 'langcode' => LanguageInterface::LANGCODE_DEFAULT, + ]); + $this->assertEqual($state->get('entity_test_entity_test_create_access'), TRUE); + + // Test hook_entity_access() and hook_ENTITY_TYPE_access(). + $entity->access('view'); + $this->assertEqual($state->get('entity_test_entity_access'), TRUE); + $this->assertEqual($state->get('entity_test_entity_test_access'), TRUE); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityApiTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityApiTest.php new file mode 100644 index 0000000..90520e2 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityApiTest.php @@ -0,0 +1,178 @@ +installEntitySchema($entity_type_id); + } + } + } + + /** + * Tests basic CRUD functionality of the Entity API. + */ + public function testCRUD() { + // All entity variations have to have the same results. + foreach (entity_test_entity_types() as $entity_type) { + $this->assertCRUD($entity_type, $this->createUser()); + } + } + + /** + * Executes a test set for a defined entity type and user. + * + * @param string $entity_type + * The entity type to run the tests with. + * @param \Drupal\user\UserInterface $user1 + * The user to run the tests with. + */ + protected function assertCRUD($entity_type, UserInterface $user1) { + // Create some test entities. + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array('name' => 'test', 'user_id' => $user1->id())); + $entity->save(); + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array('name' => 'test2', 'user_id' => $user1->id())); + $entity->save(); + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array('name' => 'test', 'user_id' => NULL)); + $entity->save(); + + $entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test'))); + $this->assertEqual($entities[0]->name->value, 'test', format_string('%entity_type: Created and loaded entity', array('%entity_type' => $entity_type))); + $this->assertEqual($entities[1]->name->value, 'test', format_string('%entity_type: Created and loaded entity', array('%entity_type' => $entity_type))); + + // Test loading a single entity. + $loaded_entity = entity_load($entity_type, $entity->id()); + $this->assertEqual($loaded_entity->id(), $entity->id(), format_string('%entity_type: Loaded a single entity by id.', array('%entity_type' => $entity_type))); + + // Test deleting an entity. + $entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test2'))); + $entities[0]->delete(); + $entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test2'))); + $this->assertEqual($entities, array(), format_string('%entity_type: Entity deleted.', array('%entity_type' => $entity_type))); + + // Test updating an entity. + $entities = array_values(entity_load_multiple_by_properties($entity_type, array('name' => 'test'))); + $entities[0]->name->value = 'test3'; + $entities[0]->save(); + $entity = entity_load($entity_type, $entities[0]->id()); + $this->assertEqual($entity->name->value, 'test3', format_string('%entity_type: Entity updated.', array('%entity_type' => $entity_type))); + + // Try deleting multiple test entities by deleting all. + $ids = array_keys(entity_load_multiple($entity_type)); + entity_delete_multiple($entity_type, $ids); + + $all = entity_load_multiple($entity_type); + $this->assertTrue(empty($all), format_string('%entity_type: Deleted all entities.', array('%entity_type' => $entity_type))); + + // Verify that all data got deleted. + $definition = \Drupal::entityManager()->getDefinition($entity_type); + $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition->getBaseTable() . '}')->fetchField(), 'Base table was emptied'); + if ($data_table = $definition->getDataTable()) { + $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $data_table . '}')->fetchField(), 'Data table was emptied'); + } + if ($revision_table = $definition->getRevisionTable()) { + $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $revision_table . '}')->fetchField(), 'Data table was emptied'); + } + + // Test deleting a list of entities not indexed by entity id. + $entities = array(); + $entity = entity_create($entity_type, array('name' => 'test', 'user_id' => $user1->id())); + $entity->save(); + $entities['test'] = $entity; + $entity = entity_create($entity_type, array('name' => 'test2', 'user_id' => $user1->id())); + $entity->save(); + $entities['test2'] = $entity; + $controller = \Drupal::entityManager()->getStorage($entity_type); + $controller->delete($entities); + + // Verify that entities got deleted. + $all = entity_load_multiple($entity_type); + $this->assertTrue(empty($all), format_string('%entity_type: Deleted all entities.', array('%entity_type' => $entity_type))); + + // Verify that all data got deleted from the tables. + $definition = \Drupal::entityManager()->getDefinition($entity_type); + $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $definition->getBaseTable() . '}')->fetchField(), 'Base table was emptied'); + if ($data_table = $definition->getDataTable()) { + $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $data_table . '}')->fetchField(), 'Data table was emptied'); + } + if ($revision_table = $definition->getRevisionTable()) { + $this->assertEqual(0, db_query('SELECT COUNT(*) FROM {' . $revision_table . '}')->fetchField(), 'Data table was emptied'); + } + } + + /** + * Tests that exceptions are thrown when saving or deleting an entity. + */ + public function testEntityStorageExceptionHandling() { + $entity = EntityTest::create(array('name' => 'test')); + try { + $GLOBALS['entity_test_throw_exception'] = TRUE; + $entity->save(); + $this->fail('Entity presave EntityStorageException thrown but not caught.'); + } + catch (EntityStorageException $e) { + $this->assertEqual($e->getcode(), 1, 'Entity presave EntityStorageException caught.'); + } + + $entity = EntityTest::create(array('name' => 'test2')); + try { + unset($GLOBALS['entity_test_throw_exception']); + $entity->save(); + $this->pass('Exception presave not thrown and not caught.'); + } + catch (EntityStorageException $e) { + $this->assertNotEqual($e->getCode(), 1, 'Entity presave EntityStorageException caught.'); + } + + $entity = EntityTest::create(array('name' => 'test3')); + $entity->save(); + try { + $GLOBALS['entity_test_throw_exception'] = TRUE; + $entity->delete(); + $this->fail('Entity predelete EntityStorageException not thrown.'); + } + catch (EntityStorageException $e) { + $this->assertEqual($e->getCode(), 2, 'Entity predelete EntityStorageException caught.'); + } + + unset($GLOBALS['entity_test_throw_exception']); + $entity = EntityTest::create(array('name' => 'test4')); + $entity->save(); + try { + $entity->delete(); + $this->pass('Entity predelete EntityStorageException not thrown and not caught.'); + } + catch (EntityStorageException $e) { + $this->assertNotEqual($e->getCode(), 2, 'Entity predelete EntityStorageException thrown.'); + } + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php new file mode 100644 index 0000000..4392aa5 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php @@ -0,0 +1,170 @@ +installSchema('system', ['key_value']); + } + + /** + * Tests autocompletion edge cases with slashes in the names. + */ + function testEntityReferenceAutocompletion() { + // Add an entity with a slash in its name. + $entity_1 = $this->container->get('entity_type.manager') + ->getStorage($this->entityType) + ->create(array('name' => '10/16/2011')); + $entity_1->save(); + + // Add another entity that differs after the slash character. + $entity_2 = $this->container->get('entity_type.manager') + ->getStorage($this->entityType) + ->create(array('name' => '10/17/2011')); + $entity_2->save(); + + // Add another entity that has both a comma, a slash and markup. + $entity_3 = $this->container->get('entity_type.manager') + ->getStorage($this->entityType) + ->create(array('name' => 'label with, and / test')); + $entity_3->save(); + + // Try to autocomplete a entity label that matches both entities. + // We should get both entities in a JSON encoded string. + $input = '10/'; + $data = $this->getAutocompleteResult($input); + $this->assertIdentical($data[0]['label'], Html::escape($entity_1->name->value), 'Autocomplete returned the first matching entity'); + $this->assertIdentical($data[1]['label'], Html::escape($entity_2->name->value), 'Autocomplete returned the second matching entity'); + + // Try to autocomplete a entity label that matches the first entity. + // We should only get the first entity in a JSON encoded string. + $input = '10/16'; + $data = $this->getAutocompleteResult($input); + $target = array( + 'value' => $entity_1->name->value . ' (1)', + 'label' => Html::escape($entity_1->name->value), + ); + $this->assertIdentical(reset($data), $target, 'Autocomplete returns only the expected matching entity.'); + + // Try to autocomplete a entity label that matches the second entity, and + // the first entity is already typed in the autocomplete (tags) widget. + $input = $entity_1->name->value . ' (1), 10/17'; + $data = $this->getAutocompleteResult($input); + $this->assertIdentical($data[0]['label'], Html::escape($entity_2->name->value), 'Autocomplete returned the second matching entity'); + + // Try to autocomplete a entity label with both a comma, a slash and markup. + $input = '"label with, and /"'; + $data = $this->getAutocompleteResult($input); + $n = $entity_3->name->value . ' (3)'; + // Entity labels containing commas or quotes must be wrapped in quotes. + $n = Tags::encode($n); + $target = array( + 'value' => $n, + 'label' => Html::escape($entity_3->name->value), + ); + $this->assertIdentical(reset($data), $target, 'Autocomplete returns an entity label containing a comma and a slash.'); + } + + /** + * Tests that missing or invalid selection setting key are handled correctly. + */ + public function testSelectionSettingsHandling() { + $entity_reference_controller = EntityAutocompleteController::create($this->container); + $request = Request::create('entity_reference_autocomplete/' . $this->entityType . '/default'); + $request->query->set('q', $this->randomString()); + + try { + // Pass an invalid selection settings key (i.e. one that does not exist + // in the key/value store). + $selection_settings_key = $this->randomString(); + $entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key); + + $this->fail('Non-existent selection settings key throws an exception.'); + } + catch (AccessDeniedHttpException $e) { + $this->pass('Non-existent selection settings key throws an exception.'); + } + + try { + // Generate a valid hash key but store a modified settings array. + $selection_settings = []; + $selection_settings_key = Crypt::hmacBase64(serialize($selection_settings) . $this->entityType . 'default', Settings::getHashSalt()); + + $selection_settings[$this->randomMachineName()] = $this->randomString(); + \Drupal::keyValue('entity_autocomplete')->set($selection_settings_key, $selection_settings); + + $entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key); + } + catch (AccessDeniedHttpException $e) { + if ($e->getMessage() == 'Invalid selection settings key.') { + $this->pass('Invalid selection settings key throws an exception.'); + } + else { + $this->fail('Invalid selection settings key throws an exception.'); + } + } + + } + + /** + * Returns the result of an Entity reference autocomplete request. + * + * @param string $input + * The label of the entity to query by. + * + * @return mixed + * The JSON value encoded in its appropriate PHP type. + */ + protected function getAutocompleteResult($input) { + $request = Request::create('entity_reference_autocomplete/' . $this->entityType . '/default'); + $request->query->set('q', $input); + + $selection_settings = []; + $selection_settings_key = Crypt::hmacBase64(serialize($selection_settings) . $this->entityType . 'default', Settings::getHashSalt()); + \Drupal::keyValue('entity_autocomplete')->set($selection_settings_key, $selection_settings); + + $entity_reference_controller = EntityAutocompleteController::create($this->container); + $result = $entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key)->getContent(); + + return Json::decode($result); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityBundleFieldTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityBundleFieldTest.php new file mode 100644 index 0000000..0d69d71 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityBundleFieldTest.php @@ -0,0 +1,118 @@ +installSchema('user', array('users_data')); + $this->moduleHandler = $this->container->get('module_handler'); + $this->database = $this->container->get('database'); + } + + /** + * Tests making use of a custom bundle field. + */ + public function testCustomBundleFieldUsage() { + entity_test_create_bundle('custom'); + + // Check that an entity with bundle entity_test does not have the custom + // field. + $storage = $this->entityManager->getStorage('entity_test'); + $entity = $storage->create([ + 'type' => 'entity_test', + ]); + $this->assertFalse($entity->hasField('custom_bundle_field')); + + // Check that the custom bundle has the defined custom field and check + // saving and deleting of custom field data. + $entity = $storage->create([ + 'type' => 'custom', + ]); + $this->assertTrue($entity->hasField('custom_bundle_field')); + + // Ensure that the field exists in the field map. + $field_map = \Drupal::entityManager()->getFieldMap(); + $this->assertEqual($field_map['entity_test']['custom_bundle_field'], ['type' => 'string', 'bundles' => ['custom' => 'custom']]); + + $entity->custom_bundle_field->value = 'swanky'; + $entity->save(); + $storage->resetCache(); + $entity = $storage->load($entity->id()); + $this->assertEqual($entity->custom_bundle_field->value, 'swanky', 'Entity was saved correctly'); + + $entity->custom_bundle_field->value = 'cozy'; + $entity->save(); + $storage->resetCache(); + $entity = $storage->load($entity->id()); + $this->assertEqual($entity->custom_bundle_field->value, 'cozy', 'Entity was updated correctly.'); + + $entity->delete(); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ + $table_mapping = $storage->getTableMapping(); + $table = $table_mapping->getDedicatedDataTableName($entity->getFieldDefinition('custom_bundle_field')); + $result = $this->database->select($table, 'f') + ->fields('f') + ->condition('f.entity_id', $entity->id()) + ->execute(); + $this->assertFalse($result->fetchAssoc(), 'Field data has been deleted'); + + // Create another entity to test that values are marked as deleted when a + // bundle is deleted. + $entity = $storage->create(['type' => 'custom', 'custom_bundle_field' => 'new']); + $entity->save(); + entity_test_delete_bundle('custom'); + + $table = $table_mapping->getDedicatedDataTableName($entity->getFieldDefinition('custom_bundle_field')); + $result = $this->database->select($table, 'f') + ->condition('f.entity_id', $entity->id()) + ->condition('deleted', 1) + ->countQuery() + ->execute(); + $this->assertEqual(1, $result->fetchField(), 'Field data has been deleted'); + + // Ensure that the field no longer exists in the field map. + $field_map = \Drupal::entityManager()->getFieldMap(); + $this->assertFalse(isset($field_map['entity_test']['custom_bundle_field'])); + + // @todo Test field purge and table deletion once supported. See + // https://www.drupal.org/node/2282119. + // $this->assertFalse($this->database->schema()->tableExists($table), 'Custom field table was deleted'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityCrudHookTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityCrudHookTest.php new file mode 100644 index 0000000..6bd70f1 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityCrudHookTest.php @@ -0,0 +1,564 @@ +installSchema('user', array('users_data')); + $this->installSchema('file', array('file_usage')); + $this->installSchema('node', array('node_access')); + $this->installSchema('comment', array('comment_entity_statistics')); + $this->installConfig(['node', 'comment']); + } + + /** + * Checks the order of CRUD hook execution messages. + * + * entity_crud_hook_test.module implements all core entity CRUD hooks and + * stores a message for each in $GLOBALS['entity_crud_hook_test']. + * + * @param $messages + * An array of plain-text messages in the order they should appear. + */ + protected function assertHookMessageOrder($messages) { + $positions = array(); + foreach ($messages as $message) { + // Verify that each message is found and record its position. + $position = array_search($message, $GLOBALS['entity_crud_hook_test']); + if ($this->assertTrue($position !== FALSE, $message)) { + $positions[] = $position; + } + } + + // Sort the positions and ensure they remain in the same order. + $sorted = $positions; + sort($sorted); + $this->assertTrue($sorted == $positions, 'The hook messages appear in the correct order.'); + } + + /** + * Tests hook invocations for CRUD operations on blocks. + */ + public function testBlockHooks() { + $entity = Block::create(array( + 'id' => 'stark_test_html', + 'plugin' => 'test_html', + 'theme' => 'stark', + )); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_block_create called', + 'entity_crud_hook_test_entity_create called for type block', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $entity->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_block_presave called', + 'entity_crud_hook_test_entity_presave called for type block', + 'entity_crud_hook_test_block_insert called', + 'entity_crud_hook_test_entity_insert called for type block', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $entity = Block::load($entity->id()); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_entity_load called for type block', + 'entity_crud_hook_test_block_load called', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $entity->label = 'New label'; + $entity->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_block_presave called', + 'entity_crud_hook_test_entity_presave called for type block', + 'entity_crud_hook_test_block_update called', + 'entity_crud_hook_test_entity_update called for type block', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $entity->delete(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_block_predelete called', + 'entity_crud_hook_test_entity_predelete called for type block', + 'entity_crud_hook_test_block_delete called', + 'entity_crud_hook_test_entity_delete called for type block', + )); + } + + /** + * Tests hook invocations for CRUD operations on comments. + */ + public function testCommentHooks() { + $account = $this->createUser(); + NodeType::create([ + 'type' => 'article', + 'name' => 'Article', + ])->save(); + $this->addDefaultCommentField('node', 'article', 'comment', CommentItemInterface::OPEN); + + $node = Node::create([ + 'uid' => $account->id(), + 'type' => 'article', + 'title' => 'Test node', + 'status' => 1, + 'promote' => 0, + 'sticky' => 0, + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + 'created' => REQUEST_TIME, + 'changed' => REQUEST_TIME, + ]); + $node->save(); + $nid = $node->id(); + $GLOBALS['entity_crud_hook_test'] = array(); + + $comment = Comment::create(array( + 'cid' => NULL, + 'pid' => 0, + 'entity_id' => $nid, + 'entity_type' => 'node', + 'field_name' => 'comment', + 'uid' => $account->id(), + 'subject' => 'Test comment', + 'created' => REQUEST_TIME, + 'changed' => REQUEST_TIME, + 'status' => 1, + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + )); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_comment_create called', + 'entity_crud_hook_test_entity_create called for type comment', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $comment->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_comment_presave called', + 'entity_crud_hook_test_entity_presave called for type comment', + 'entity_crud_hook_test_comment_insert called', + 'entity_crud_hook_test_entity_insert called for type comment', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $comment = Comment::load($comment->id()); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_entity_load called for type comment', + 'entity_crud_hook_test_comment_load called', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $comment->setSubject('New subject'); + $comment->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_comment_presave called', + 'entity_crud_hook_test_entity_presave called for type comment', + 'entity_crud_hook_test_comment_update called', + 'entity_crud_hook_test_entity_update called for type comment', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $comment->delete(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_comment_predelete called', + 'entity_crud_hook_test_entity_predelete called for type comment', + 'entity_crud_hook_test_comment_delete called', + 'entity_crud_hook_test_entity_delete called for type comment', + )); + } + + /** + * Tests hook invocations for CRUD operations on files. + */ + public function testFileHooks() { + $this->installEntitySchema('file'); + + $url = 'public://entity_crud_hook_test.file'; + file_put_contents($url, 'Test test test'); + $file = File::create([ + 'fid' => NULL, + 'uid' => 1, + 'filename' => 'entity_crud_hook_test.file', + 'uri' => $url, + 'filemime' => 'text/plain', + 'filesize' => filesize($url), + 'status' => 1, + 'created' => REQUEST_TIME, + 'changed' => REQUEST_TIME, + ]); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_file_create called', + 'entity_crud_hook_test_entity_create called for type file', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $file->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_file_presave called', + 'entity_crud_hook_test_entity_presave called for type file', + 'entity_crud_hook_test_file_insert called', + 'entity_crud_hook_test_entity_insert called for type file', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $file = File::load($file->id()); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_entity_load called for type file', + 'entity_crud_hook_test_file_load called', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $file->setFilename('new.entity_crud_hook_test.file'); + $file->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_file_presave called', + 'entity_crud_hook_test_entity_presave called for type file', + 'entity_crud_hook_test_file_update called', + 'entity_crud_hook_test_entity_update called for type file', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $file->delete(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_file_predelete called', + 'entity_crud_hook_test_entity_predelete called for type file', + 'entity_crud_hook_test_file_delete called', + 'entity_crud_hook_test_entity_delete called for type file', + )); + } + + /** + * Tests hook invocations for CRUD operations on nodes. + */ + public function testNodeHooks() { + $account = $this->createUser(); + + $node = Node::create([ + 'uid' => $account->id(), + 'type' => 'article', + 'title' => 'Test node', + 'status' => 1, + 'promote' => 0, + 'sticky' => 0, + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + 'created' => REQUEST_TIME, + 'changed' => REQUEST_TIME, + ]); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_node_create called', + 'entity_crud_hook_test_entity_create called for type node', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $node->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_node_presave called', + 'entity_crud_hook_test_entity_presave called for type node', + 'entity_crud_hook_test_node_insert called', + 'entity_crud_hook_test_entity_insert called for type node', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $node = Node::load($node->id()); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_entity_load called for type node', + 'entity_crud_hook_test_node_load called', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $node->title = 'New title'; + $node->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_node_presave called', + 'entity_crud_hook_test_entity_presave called for type node', + 'entity_crud_hook_test_node_update called', + 'entity_crud_hook_test_entity_update called for type node', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $node->delete(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_node_predelete called', + 'entity_crud_hook_test_entity_predelete called for type node', + 'entity_crud_hook_test_node_delete called', + 'entity_crud_hook_test_entity_delete called for type node', + )); + } + + /** + * Tests hook invocations for CRUD operations on taxonomy terms. + */ + public function testTaxonomyTermHooks() { + $this->installEntitySchema('taxonomy_term'); + + $vocabulary = Vocabulary::create([ + 'name' => 'Test vocabulary', + 'vid' => 'test', + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + 'description' => NULL, + 'module' => 'entity_crud_hook_test', + ]); + $vocabulary->save(); + $GLOBALS['entity_crud_hook_test'] = array(); + + $term = Term::create([ + 'vid' => $vocabulary->id(), + 'name' => 'Test term', + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + 'description' => NULL, + 'format' => 1, + ]); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_taxonomy_term_create called', + 'entity_crud_hook_test_entity_create called for type taxonomy_term', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $term->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_taxonomy_term_presave called', + 'entity_crud_hook_test_entity_presave called for type taxonomy_term', + 'entity_crud_hook_test_taxonomy_term_insert called', + 'entity_crud_hook_test_entity_insert called for type taxonomy_term', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $term = Term::load($term->id()); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_entity_load called for type taxonomy_term', + 'entity_crud_hook_test_taxonomy_term_load called', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $term->setName('New name'); + $term->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_taxonomy_term_presave called', + 'entity_crud_hook_test_entity_presave called for type taxonomy_term', + 'entity_crud_hook_test_taxonomy_term_update called', + 'entity_crud_hook_test_entity_update called for type taxonomy_term', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $term->delete(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_taxonomy_term_predelete called', + 'entity_crud_hook_test_entity_predelete called for type taxonomy_term', + 'entity_crud_hook_test_taxonomy_term_delete called', + 'entity_crud_hook_test_entity_delete called for type taxonomy_term', + )); + } + + /** + * Tests hook invocations for CRUD operations on taxonomy vocabularies. + */ + public function testTaxonomyVocabularyHooks() { + $this->installEntitySchema('taxonomy_term'); + + $vocabulary = Vocabulary::create([ + 'name' => 'Test vocabulary', + 'vid' => 'test', + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + 'description' => NULL, + 'module' => 'entity_crud_hook_test', + ]); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_taxonomy_vocabulary_create called', + 'entity_crud_hook_test_entity_create called for type taxonomy_vocabulary', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $vocabulary->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_taxonomy_vocabulary_presave called', + 'entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary', + 'entity_crud_hook_test_taxonomy_vocabulary_insert called', + 'entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $vocabulary = Vocabulary::load($vocabulary->id()); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_entity_load called for type taxonomy_vocabulary', + 'entity_crud_hook_test_taxonomy_vocabulary_load called', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $vocabulary->set('name', 'New name'); + $vocabulary->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_taxonomy_vocabulary_presave called', + 'entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary', + 'entity_crud_hook_test_taxonomy_vocabulary_update called', + 'entity_crud_hook_test_entity_update called for type taxonomy_vocabulary', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $vocabulary->delete(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_taxonomy_vocabulary_predelete called', + 'entity_crud_hook_test_entity_predelete called for type taxonomy_vocabulary', + 'entity_crud_hook_test_taxonomy_vocabulary_delete called', + 'entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary', + )); + } + + /** + * Tests hook invocations for CRUD operations on users. + */ + public function testUserHooks() { + $account = User::create([ + 'name' => 'Test user', + 'mail' => 'test@example.com', + 'created' => REQUEST_TIME, + 'status' => 1, + 'language' => 'en', + ]); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_user_create called', + 'entity_crud_hook_test_entity_create called for type user', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $account->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_user_presave called', + 'entity_crud_hook_test_entity_presave called for type user', + 'entity_crud_hook_test_user_insert called', + 'entity_crud_hook_test_entity_insert called for type user', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + User::load($account->id()); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_entity_load called for type user', + 'entity_crud_hook_test_user_load called', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + $account->name = 'New name'; + $account->save(); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_user_presave called', + 'entity_crud_hook_test_entity_presave called for type user', + 'entity_crud_hook_test_user_update called', + 'entity_crud_hook_test_entity_update called for type user', + )); + + $GLOBALS['entity_crud_hook_test'] = array(); + user_delete($account->id()); + + $this->assertHookMessageOrder(array( + 'entity_crud_hook_test_user_predelete called', + 'entity_crud_hook_test_entity_predelete called for type user', + 'entity_crud_hook_test_user_delete called', + 'entity_crud_hook_test_entity_delete called for type user', + )); + } + + /** + * Tests rollback from failed entity save. + */ + function testEntityRollback() { + // Create a block. + try { + EntityTest::create(array('name' => 'fail_insert'))->save(); + $this->fail('Expected exception has not been thrown.'); + } + catch (\Exception $e) { + $this->pass('Expected exception has been thrown.'); + } + + if (Database::getConnection()->supportsTransactions()) { + // Check that the block does not exist in the database. + $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute(); + $this->assertTrue(empty($ids), 'Transactions supported, and entity not found in database.'); + } + else { + // Check that the block exists in the database. + $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute(); + $this->assertFalse(empty($ids), 'Transactions not supported, and entity found in database.'); + } + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php new file mode 100644 index 0000000..e3b3b41 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php @@ -0,0 +1,814 @@ +entityDefinitionUpdateManager = $this->container->get('entity.definition_update_manager'); + $this->database = $this->container->get('database'); + + // Install every entity type's schema that wasn't installed in the parent + // method. + foreach (array_diff_key($this->entityManager->getDefinitions(), array_flip(array('user', 'entity_test'))) as $entity_type_id => $entity_type) { + $this->installEntitySchema($entity_type_id); + } + } + + /** + * Tests that new entity type definitions are correctly handled. + */ + public function testNewEntityType() { + $entity_type_id = 'entity_test_new'; + $schema = $this->database->schema(); + + // Check that the "entity_test_new" is not defined. + $entity_types = $this->entityManager->getDefinitions(); + $this->assertFalse(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type does not exist.'); + $this->assertFalse($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type does not exist.'); + + // Check that the "entity_test_new" is now defined and the related schema + // has been created. + $this->enableNewEntityType(); + $entity_types = $this->entityManager->getDefinitions(); + $this->assertTrue(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type exists.'); + $this->assertTrue($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type has been created.'); + } + + /** + * Tests when no definition update is needed. + */ + public function testNoUpdates() { + // Ensure that the definition update manager reports no updates. + $this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that no updates are needed.'); + $this->assertIdentical($this->entityDefinitionUpdateManager->getChangeSummary(), array(), 'EntityDefinitionUpdateManager reports an empty change summary.'); + + // Ensure that applyUpdates() runs without error (it's not expected to do + // anything when there aren't updates). + $this->entityDefinitionUpdateManager->applyUpdates(); + } + + /** + * Tests updating entity schema when there are no existing entities. + */ + public function testEntityTypeUpdateWithoutData() { + // The 'entity_test_update' entity type starts out non-revisionable, so + // ensure the revision table hasn't been created during setUp(). + $this->assertFalse($this->database->schema()->tableExists('entity_test_update_revision'), 'Revision table not created for entity_test_update.'); + + // Update it to be revisionable and ensure the definition update manager + // reports that an update is needed. + $this->updateEntityTypeToRevisionable(); + $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); + $expected = array( + 'entity_test_update' => array( + t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]), + ), + ); + $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected); //, 'EntityDefinitionUpdateManager reports the expected change summary.'); + + // Run the update and ensure the revision table is created. + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertTrue($this->database->schema()->tableExists('entity_test_update_revision'), 'Revision table created for entity_test_update.'); + } + + /** + * Tests updating entity schema when there are existing entities. + */ + public function testEntityTypeUpdateWithData() { + // Save an entity. + $this->entityManager->getStorage('entity_test_update')->create()->save(); + + // Update the entity type to be revisionable and try to apply the update. + // It's expected to throw an exception. + $this->updateEntityTypeToRevisionable(); + try { + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->fail('EntityStorageException thrown when trying to apply an update that requires data migration.'); + } + catch (EntityStorageException $e) { + $this->pass('EntityStorageException thrown when trying to apply an update that requires data migration.'); + } + } + + /** + * Tests creating, updating, and deleting a base field if no entities exist. + */ + public function testBaseFieldCreateUpdateDeleteWithoutData() { + // Add a base field, ensure the update manager reports it, and the update + // creates its schema. + $this->addBaseField(); + $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); + $expected = array( + 'entity_test_update' => array( + t('The %field_name field needs to be installed.', ['%field_name' => t('A new base field')]), + ), + ); + $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), 'Column created in shared table for new_base_field.'); + + // Add an index on the base field, ensure the update manager reports it, + // and the update creates it. + $this->addBaseFieldIndex(); + $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); + $expected = array( + 'entity_test_update' => array( + t('The %field_name field needs to be updated.', ['%field_name' => t('A new base field')]), + ), + ); + $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), 'Index created.'); + + // Remove the above index, ensure the update manager reports it, and the + // update deletes it. + $this->removeBaseFieldIndex(); + $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); + $expected = array( + 'entity_test_update' => array( + t('The %field_name field needs to be updated.', ['%field_name' => t('A new base field')]), + ), + ); + $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), 'Index deleted.'); + + // Update the type of the base field from 'string' to 'text', ensure the + // update manager reports it, and the update adjusts the schema + // accordingly. + $this->modifyBaseField(); + $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); + $expected = array( + 'entity_test_update' => array( + t('The %field_name field needs to be updated.', ['%field_name' => t('A new base field')]), + ), + ); + $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), 'Original column deleted in shared table for new_base_field.'); + $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field__value'), 'Value column created in shared table for new_base_field.'); + $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field__format'), 'Format column created in shared table for new_base_field.'); + + // Remove the base field, ensure the update manager reports it, and the + // update deletes the schema. + $this->removeBaseField(); + $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); + $expected = array( + 'entity_test_update' => array( + t('The %field_name field needs to be uninstalled.', ['%field_name' => t('A new base field')]), + ), + ); + $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field_value'), 'Value column deleted from shared table for new_base_field.'); + $this->assertFalse($this->database->schema()->fieldExists('entity_test_update', 'new_base_field_format'), 'Format column deleted from shared table for new_base_field.'); + } + + /** + * Tests creating, updating, and deleting a bundle field if no entities exist. + */ + public function testBundleFieldCreateUpdateDeleteWithoutData() { + // Add a bundle field, ensure the update manager reports it, and the update + // creates its schema. + $this->addBundleField(); + $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); + $expected = array( + 'entity_test_update' => array( + t('The %field_name field needs to be installed.', ['%field_name' => t('A new bundle field')]), + ), + ); + $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertTrue($this->database->schema()->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table created for new_bundle_field.'); + + // Update the type of the base field from 'string' to 'text', ensure the + // update manager reports it, and the update adjusts the schema + // accordingly. + $this->modifyBundleField(); + $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); + $expected = array( + 'entity_test_update' => [ + t('The %field_name field needs to be updated.', ['%field_name' => t('A new bundle field')]), + ], + ); + $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertTrue($this->database->schema()->fieldExists('entity_test_update__new_bundle_field', 'new_bundle_field_format'), 'Format column created in dedicated table for new_base_field.'); + + // Remove the bundle field, ensure the update manager reports it, and the + // update deletes the schema. + $this->removeBundleField(); + $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); + $expected = array( + 'entity_test_update' => array( + t('The %field_name field needs to be uninstalled.', ['%field_name' => t('A new bundle field')]), + ), + ); + $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertFalse($this->database->schema()->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table deleted for new_bundle_field.'); + } + + /** + * Tests creating and deleting a base field if entities exist. + * + * This tests deletion when there are existing entities, but not existing data + * for the field being deleted. + * + * @see testBaseFieldDeleteWithExistingData() + */ + public function testBaseFieldCreateDeleteWithExistingEntities() { + // Save an entity. + $name = $this->randomString(); + $storage = $this->entityManager->getStorage('entity_test_update'); + $entity = $storage->create(array('name' => $name)); + $entity->save(); + + // Add a base field and run the update. Ensure the base field's column is + // created and the prior saved entity data is still there. + $this->addBaseField(); + $this->entityDefinitionUpdateManager->applyUpdates(); + $schema_handler = $this->database->schema(); + $this->assertTrue($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column created in shared table for new_base_field.'); + $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id()); + $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field creation.'); + + // Remove the base field and run the update. Ensure the base field's column + // is deleted and the prior saved entity data is still there. + $this->removeBaseField(); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertFalse($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column deleted from shared table for new_base_field.'); + $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id()); + $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field deletion.'); + + // Add a base field with a required property and run the update. Ensure + // 'not null' is not applied and thus no exception is thrown. + $this->addBaseField('shape_required'); + $this->entityDefinitionUpdateManager->applyUpdates(); + $assert = $schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && $schema_handler->fieldExists('entity_test_update', 'new_base_field__color'); + $this->assertTrue($assert, 'Columns created in shared table for new_base_field.'); + + // Recreate the field after emptying the base table and check that its + // columns are not 'not null'. + // @todo Revisit this test when allowing for required storage field + // definitions. See https://www.drupal.org/node/2390495. + $entity->delete(); + $this->removeBaseField(); + $this->entityDefinitionUpdateManager->applyUpdates(); + $assert = !$schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && !$schema_handler->fieldExists('entity_test_update', 'new_base_field__color'); + $this->assert($assert, 'Columns removed from the shared table for new_base_field.'); + $this->addBaseField('shape_required'); + $this->entityDefinitionUpdateManager->applyUpdates(); + $assert = $schema_handler->fieldExists('entity_test_update', 'new_base_field__shape') && $schema_handler->fieldExists('entity_test_update', 'new_base_field__color'); + $this->assertTrue($assert, 'Columns created again in shared table for new_base_field.'); + $entity = $storage->create(array('name' => $name)); + $entity->save(); + $this->pass('The new_base_field columns are still nullable'); + } + + /** + * Tests creating and deleting a bundle field if entities exist. + * + * This tests deletion when there are existing entities, but not existing data + * for the field being deleted. + * + * @see testBundleFieldDeleteWithExistingData() + */ + public function testBundleFieldCreateDeleteWithExistingEntities() { + // Save an entity. + $name = $this->randomString(); + $storage = $this->entityManager->getStorage('entity_test_update'); + $entity = $storage->create(array('name' => $name)); + $entity->save(); + + // Add a bundle field and run the update. Ensure the bundle field's table + // is created and the prior saved entity data is still there. + $this->addBundleField(); + $this->entityDefinitionUpdateManager->applyUpdates(); + $schema_handler = $this->database->schema(); + $this->assertTrue($schema_handler->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table created for new_bundle_field.'); + $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id()); + $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field creation.'); + + // Remove the base field and run the update. Ensure the bundle field's + // table is deleted and the prior saved entity data is still there. + $this->removeBundleField(); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertFalse($schema_handler->tableExists('entity_test_update__new_bundle_field'), 'Dedicated table deleted for new_bundle_field.'); + $entity = $this->entityManager->getStorage('entity_test_update')->load($entity->id()); + $this->assertIdentical($entity->name->value, $name, 'Entity data preserved during field deletion.'); + + // Test that required columns are created as 'not null'. + $this->addBundleField('shape_required'); + $this->entityDefinitionUpdateManager->applyUpdates(); + $message = 'The new_bundle_field_shape column is not nullable.'; + $values = array( + 'bundle' => $entity->bundle(), + 'deleted'=> 0, + 'entity_id' => $entity->id(), + 'revision_id' => $entity->id(), + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + 'delta' => 0, + 'new_bundle_field_color' => $this->randomString(), + ); + try { + // Try to insert a record without providing a value for the 'not null' + // column. This should fail. + $this->database->insert('entity_test_update__new_bundle_field') + ->fields($values) + ->execute(); + $this->fail($message); + } + catch (\RuntimeException $e) { + if ($e instanceof DatabaseExceptionWrapper || $e instanceof IntegrityConstraintViolationException) { + // Now provide a value for the 'not null' column. This is expected to + // succeed. + $values['new_bundle_field_shape'] = $this->randomString(); + $this->database->insert('entity_test_update__new_bundle_field') + ->fields($values) + ->execute(); + $this->pass($message); + } else { + // Keep throwing it. + throw $e; + } + } + } + + /** + * Tests deleting a base field when it has existing data. + */ + public function testBaseFieldDeleteWithExistingData() { + // Add the base field and run the update. + $this->addBaseField(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + // Save an entity with the base field populated. + $this->entityManager->getStorage('entity_test_update')->create(array('new_base_field' => 'foo'))->save(); + + // Remove the base field and apply updates. It's expected to throw an + // exception. + // @todo Revisit that expectation once purging is implemented for + // all fields: https://www.drupal.org/node/2282119. + $this->removeBaseField(); + try { + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.'); + } + catch (FieldStorageDefinitionUpdateForbiddenException $e) { + $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.'); + } + } + + /** + * Tests deleting a bundle field when it has existing data. + */ + public function testBundleFieldDeleteWithExistingData() { + // Add the bundle field and run the update. + $this->addBundleField(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + // Save an entity with the bundle field populated. + entity_test_create_bundle('custom'); + $this->entityManager->getStorage('entity_test_update')->create(array('type' => 'test_bundle', 'new_bundle_field' => 'foo'))->save(); + + // Remove the bundle field and apply updates. It's expected to throw an + // exception. + // @todo Revisit that expectation once purging is implemented for + // all fields: https://www.drupal.org/node/2282119. + $this->removeBundleField(); + try { + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.'); + } + catch (FieldStorageDefinitionUpdateForbiddenException $e) { + $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to apply an update that deletes a non-purgeable field with data.'); + } + } + + /** + * Tests updating a base field when it has existing data. + */ + public function testBaseFieldUpdateWithExistingData() { + // Add the base field and run the update. + $this->addBaseField(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + // Save an entity with the base field populated. + $this->entityManager->getStorage('entity_test_update')->create(array('new_base_field' => 'foo'))->save(); + + // Change the field's field type and apply updates. It's expected to + // throw an exception. + $this->modifyBaseField(); + try { + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.'); + } + catch (FieldStorageDefinitionUpdateForbiddenException $e) { + $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.'); + } + } + + /** + * Tests updating a bundle field when it has existing data. + */ + public function testBundleFieldUpdateWithExistingData() { + // Add the bundle field and run the update. + $this->addBundleField(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + // Save an entity with the bundle field populated. + entity_test_create_bundle('custom'); + $this->entityManager->getStorage('entity_test_update')->create(array('type' => 'test_bundle', 'new_bundle_field' => 'foo'))->save(); + + // Change the field's field type and apply updates. It's expected to + // throw an exception. + $this->modifyBundleField(); + try { + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->fail('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.'); + } + catch (FieldStorageDefinitionUpdateForbiddenException $e) { + $this->pass('FieldStorageDefinitionUpdateForbiddenException thrown when trying to update a field schema that has data.'); + } + } + + /** + * Tests creating and deleting a multi-field index when there are no existing entities. + */ + public function testEntityIndexCreateDeleteWithoutData() { + // Add an entity index and ensure the update manager reports that as an + // update to the entity type. + $this->addEntityIndex(); + $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); + $expected = array( + 'entity_test_update' => array( + t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]), + ), + ); + $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); + + // Run the update and ensure the new index is created. + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created.'); + + // Remove the index and ensure the update manager reports that as an + // update to the entity type. + $this->removeEntityIndex(); + $this->assertTrue($this->entityDefinitionUpdateManager->needsUpdates(), 'EntityDefinitionUpdateManager reports that updates are needed.'); + $expected = array( + 'entity_test_update' => array( + t('The %entity_type entity type needs to be updated.', ['%entity_type' => $this->entityManager->getDefinition('entity_test_update')->getLabel()]), + ), + ); + $this->assertEqual($this->entityDefinitionUpdateManager->getChangeSummary(), $expected, 'EntityDefinitionUpdateManager reports the expected change summary.'); + + // Run the update and ensure the index is deleted. + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.'); + + // Test that composite indexes are handled correctly when dropping and + // re-creating one of their columns. + $this->addEntityIndex(); + $this->entityDefinitionUpdateManager->applyUpdates(); + $storage_definition = $this->entityDefinitionUpdateManager->getFieldStorageDefinition('name', 'entity_test_update'); + $this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition); + $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created.'); + $this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition); + $this->assertFalse($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index deleted.'); + $this->entityDefinitionUpdateManager->installFieldStorageDefinition('name', 'entity_test_update', 'entity_test', $storage_definition); + $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index created again.'); + } + + /** + * Tests creating a multi-field index when there are existing entities. + */ + public function testEntityIndexCreateWithData() { + // Save an entity. + $name = $this->randomString(); + $entity = $this->entityManager->getStorage('entity_test_update')->create(array('name' => $name)); + $entity->save(); + + // Add an entity index, run the update. Ensure that the index is created + // despite having data. + $this->addEntityIndex(); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__new_index'), 'Index added.'); + } + + /** + * Tests entity type and field storage definition events. + */ + public function testDefinitionEvents() { + /** @var \Drupal\entity_test\EntityTestDefinitionSubscriber $event_subscriber */ + $event_subscriber = $this->container->get('entity_test.definition.subscriber'); + $event_subscriber->enableEventTracking(); + + // Test field storage definition events. + $storage_definition = current($this->entityManager->getFieldStorageDefinitions('entity_test_rev')); + $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::DELETE), 'Entity type delete was not dispatched yet.'); + $this->entityManager->onFieldStorageDefinitionDelete($storage_definition); + $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::DELETE), 'Entity type delete event successfully dispatched.'); + $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::CREATE), 'Entity type create was not dispatched yet.'); + $this->entityManager->onFieldStorageDefinitionCreate($storage_definition); + $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::CREATE), 'Entity type create event successfully dispatched.'); + $this->assertFalse($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::UPDATE), 'Entity type update was not dispatched yet.'); + $this->entityManager->onFieldStorageDefinitionUpdate($storage_definition, $storage_definition); + $this->assertTrue($event_subscriber->hasEventFired(FieldStorageDefinitionEvents::UPDATE), 'Entity type update event successfully dispatched.'); + + // Test entity type events. + $entity_type = $this->entityManager->getDefinition('entity_test_rev'); + $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::CREATE), 'Entity type create was not dispatched yet.'); + $this->entityManager->onEntityTypeCreate($entity_type); + $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::CREATE), 'Entity type create event successfully dispatched.'); + $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::UPDATE), 'Entity type update was not dispatched yet.'); + $this->entityManager->onEntityTypeUpdate($entity_type, $entity_type); + $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::UPDATE), 'Entity type update event successfully dispatched.'); + $this->assertFalse($event_subscriber->hasEventFired(EntityTypeEvents::DELETE), 'Entity type delete was not dispatched yet.'); + $this->entityManager->onEntityTypeDelete($entity_type); + $this->assertTrue($event_subscriber->hasEventFired(EntityTypeEvents::DELETE), 'Entity type delete event successfully dispatched.'); + } + + /** + * Tests updating entity schema and creating a base field. + * + * This tests updating entity schema and creating a base field at the same + * time when there are no existing entities. + */ + public function testEntityTypeSchemaUpdateAndBaseFieldCreateWithoutData() { + $this->updateEntityTypeToRevisionable(); + $this->addBaseField(); + $message = 'Successfully updated entity schema and created base field at the same time.'; + // Entity type updates create base fields as well, thus make sure doing both + // at the same time does not lead to errors due to the base field being + // created twice. + try { + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->pass($message); + } + catch (\Exception $e) { + $this->fail($message); + throw $e; + } + } + + /** + * Tests updating entity schema and creating a revisionable base field. + * + * This tests updating entity schema and creating a revisionable base field + * at the same time when there are no existing entities. + */ + public function testEntityTypeSchemaUpdateAndRevisionableBaseFieldCreateWithoutData() { + $this->updateEntityTypeToRevisionable(); + $this->addRevisionableBaseField(); + $message = 'Successfully updated entity schema and created revisionable base field at the same time.'; + // Entity type updates create base fields as well, thus make sure doing both + // at the same time does not lead to errors due to the base field being + // created twice. + try { + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->pass($message); + } + catch (\Exception $e) { + $this->fail($message); + throw $e; + } + } + + /** + * Tests applying single updates. + */ + public function testSingleActionCalls() { + $db_schema = $this->database->schema(); + + // Ensure that a non-existing entity type cannot be installed. + $message = 'A non-existing entity type cannot be installed'; + try { + $this->entityDefinitionUpdateManager->installEntityType(new ContentEntityType(['id' => 'foo'])); + $this->fail($message); + } + catch (PluginNotFoundException $e) { + $this->pass($message); + } + + // Ensure that a field cannot be installed on non-existing entity type. + $message = 'A field cannot be installed on a non-existing entity type'; + try { + $storage_definition = BaseFieldDefinition::create('string') + ->setLabel(t('A new revisionable base field')) + ->setRevisionable(TRUE); + $this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'foo', 'entity_test', $storage_definition); + $this->fail($message); + } + catch (PluginNotFoundException $e) { + $this->pass($message); + } + + // Ensure that a non-existing field cannot be installed. + $storage_definition = BaseFieldDefinition::create('string') + ->setLabel(t('A new revisionable base field')) + ->setRevisionable(TRUE); + $this->entityDefinitionUpdateManager->installFieldStorageDefinition('bar', 'entity_test_update', 'entity_test', $storage_definition); + $this->assertFalse($db_schema->fieldExists('entity_test_update', 'bar'), "A non-existing field cannot be installed."); + + // Ensure that installing an existing entity type is a no-op. + $entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update'); + $this->entityDefinitionUpdateManager->installEntityType($entity_type); + $this->assertTrue($db_schema->tableExists('entity_test_update'), 'Installing an existing entity type is a no-op'); + + // Create a new base field. + $this->addRevisionableBaseField(); + $storage_definition = BaseFieldDefinition::create('string') + ->setLabel(t('A new revisionable base field')) + ->setRevisionable(TRUE); + $this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' does not exist before applying the update."); + $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition); + $this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table."); + + // Ensure that installing an existing field is a no-op. + $this->entityDefinitionUpdateManager->installFieldStorageDefinition('new_base_field', 'entity_test_update', 'entity_test', $storage_definition); + $this->assertTrue($db_schema->fieldExists('entity_test_update', 'new_base_field'), 'Installing an existing field is a no-op'); + + // Update an existing field schema. + $this->modifyBaseField(); + $storage_definition = BaseFieldDefinition::create('text') + ->setName('new_base_field') + ->setTargetEntityTypeId('entity_test_update') + ->setLabel(t('A new revisionable base field')) + ->setRevisionable(TRUE); + $this->entityDefinitionUpdateManager->updateFieldStorageDefinition($storage_definition); + $this->assertFalse($db_schema->fieldExists('entity_test_update', 'new_base_field'), "Previous schema for 'new_base_field' no longer exists."); + $this->assertTrue( + $db_schema->fieldExists('entity_test_update', 'new_base_field__value') && $db_schema->fieldExists('entity_test_update', 'new_base_field__format'), + "New schema for 'new_base_field' has been created." + ); + + // Drop an existing field schema. + $this->entityDefinitionUpdateManager->uninstallFieldStorageDefinition($storage_definition); + $this->assertFalse( + $db_schema->fieldExists('entity_test_update', 'new_base_field__value') || $db_schema->fieldExists('entity_test_update', 'new_base_field__format'), + "The schema for 'new_base_field' has been dropped." + ); + + // Make the entity type revisionable. + $this->updateEntityTypeToRevisionable(); + $this->assertFalse($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' does not exist before applying the update."); + $entity_type = $this->entityDefinitionUpdateManager->getEntityType('entity_test_update'); + $keys = $entity_type->getKeys(); + $keys['revision'] = 'revision_id'; + $entity_type->set('entity_keys', $keys); + $this->entityDefinitionUpdateManager->updateEntityType($entity_type); + $this->assertTrue($db_schema->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' table has been created."); + } + + /** + * Ensures that a new field and index on a shared table are created. + * + * @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::createSharedTableSchema + */ + public function testCreateFieldAndIndexOnSharedTable() { + $this->addBaseField(); + $this->addBaseFieldIndex(); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table."); + $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update_field__new_base_field'), "New index 'entity_test_update_field__new_base_field' has been created on the 'entity_test_update' table."); + // Check index size in for MySQL. + if (Database::getConnection()->driver() == 'mysql') { + $result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update_field__new_base_field\' and column_name = \'new_base_field\'')->fetchObject(); + $this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.'); + } + } + + /** + * Ensures that a new entity level index is created when data exists. + * + * @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::onEntityTypeUpdate + */ + public function testCreateIndexUsingEntityStorageSchemaWithData() { + // Save an entity. + $name = $this->randomString(); + $storage = $this->entityManager->getStorage('entity_test_update'); + $entity = $storage->create(array('name' => $name)); + $entity->save(); + + // Create an index. + $indexes = array( + 'entity_test_update__type_index' => array('type'), + ); + $this->state->set('entity_test_update.additional_entity_indexes', $indexes); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__type_index'), "New index 'entity_test_update__type_index' has been created on the 'entity_test_update' table."); + // Check index size in for MySQL. + if (Database::getConnection()->driver() == 'mysql') { + $result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update__type_index\' and column_name = \'type\'')->fetchObject(); + $this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.'); + } + } + + /** + * Tests updating a base field when it has existing data. + */ + public function testBaseFieldEntityKeyUpdateWithExistingData() { + // Add the base field and run the update. + $this->addBaseField(); + $this->entityDefinitionUpdateManager->applyUpdates(); + + // Save an entity with the base field populated. + $this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => $this->randomString()])->save(); + + // Save an entity with the base field not populated. + /** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */ + $entity = $this->entityManager->getStorage('entity_test_update')->create(); + $entity->save(); + + // Promote the base field to an entity key. This will trigger the addition + // of a NOT NULL constraint. + $this->makeBaseFieldEntityKey(); + + // Try to apply the update and verify they fail since we have a NULL value. + $message = 'An error occurs when trying to enabling NOT NULL constraints with NULL data.'; + try { + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->fail($message); + } + catch (EntityStorageException $e) { + $this->pass($message); + } + + // Check that the update is correctly applied when no NULL data is left. + $entity->set('new_base_field', $this->randomString()); + $entity->save(); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->pass('The update is correctly performed when no NULL data exists.'); + + // Check that the update actually applied a NOT NULL constraint. + $entity->set('new_base_field', NULL); + $message = 'The NOT NULL constraint was correctly applied.'; + try { + $entity->save(); + $this->fail($message); + } + catch (EntityStorageException $e) { + $this->pass($message); + } + } + + /** + * Check that field schema is correctly handled with long-named fields. + */ + function testLongNameFieldIndexes() { + $this->addLongNameBaseField(); + $entity_type_id = 'entity_test_update'; + $entity_type = $this->entityManager->getDefinition($entity_type_id); + $definitions = EntityTestUpdate::baseFieldDefinitions($entity_type); + $name = 'new_long_named_entity_reference_base_field'; + $this->entityDefinitionUpdateManager->installFieldStorageDefinition($name, $entity_type_id, 'entity_test', $definitions[$name]); + $this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'Entity and field schema data are correctly detected.'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldDefaultValueTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldDefaultValueTest.php new file mode 100644 index 0000000..bf56c91 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldDefaultValueTest.php @@ -0,0 +1,81 @@ +uuid = $this->container->get('uuid'); + } + + /** + * Tests default values on entities and fields. + */ + public function testDefaultValues() { + // All entity variations have to have the same results. + foreach (entity_test_entity_types() as $entity_type) { + $this->assertDefaultValues($entity_type); + } + } + + /** + * Executes a test set for a defined entity type. + * + * @param string $entity_type_id + * The entity type to run the tests with. + */ + protected function assertDefaultValues($entity_type_id) { + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type_id) + ->create(); + $definition = $this->entityManager->getDefinition($entity_type_id); + $langcode_key = $definition->getKey('langcode'); + $this->assertEqual($entity->{$langcode_key}->value, 'en', SafeMarkup::format('%entity_type: Default language', array('%entity_type' => $entity_type_id))); + $this->assertTrue(Uuid::isValid($entity->uuid->value), SafeMarkup::format('%entity_type: Default UUID', array('%entity_type' => $entity_type_id))); + $this->assertEqual($entity->name->getValue(), array(), 'Field has one empty value by default.'); + } + + /** + * Tests custom default value callbacks. + */ + public function testDefaultValueCallback() { + $entity = $this->entityManager->getStorage('entity_test_default_value')->create(); + // The description field has a default value callback for testing, see + // entity_test_field_default_value(). + $string = 'description_' . $entity->language()->getId(); + $expected = array( + array( + 'shape' => "shape:0:$string", + 'color' => "color:0:$string", + ), + array( + 'shape' => "shape:1:$string", + 'color' => "color:1:$string", + ), + ); + $this->assertEqual($entity->description->getValue(), $expected); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php new file mode 100644 index 0000000..fbb4db6 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityFieldTest.php @@ -0,0 +1,752 @@ +installEntitySchema($entity_type_id); + } + } + + // Create the test field. + module_load_install('entity_test'); + entity_test_install(); + + // Install required default configuration for filter module. + $this->installConfig(array('system', 'filter')); + } + + /** + * Creates a test entity. + * + * @return \Drupal\Core\Entity\EntityInterface + */ + protected function createTestEntity($entity_type) { + $this->entityName = $this->randomMachineName(); + $this->entityUser = $this->createUser(); + $this->entityFieldText = $this->randomMachineName(); + + // Pass in the value of the name field when creating. With the user + // field we test setting a field after creation. + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(); + $entity->user_id->target_id = $this->entityUser->id(); + $entity->name->value = $this->entityName; + + // Set a value for the test field. + $entity->field_test_text->value = $this->entityFieldText; + + return $entity; + } + + /** + * Tests reading and writing properties and field items. + */ + public function testReadWrite() { + // All entity variations have to have the same results. + foreach (entity_test_entity_types() as $entity_type) { + $this->doTestReadWrite($entity_type); + } + } + + /** + * Executes the read write test set for a defined entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function doTestReadWrite($entity_type) { + $entity = $this->createTestEntity($entity_type); + + $langcode = 'en'; + + // Access the name field. + $this->assertTrue($entity->name instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', array('%entity_type' => $entity_type))); + $this->assertTrue($entity->name[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', array('%entity_type' => $entity_type))); + + $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($this->entityName, $entity->name[0]->value, format_string('%entity_type: Name value can be read through list access.', array('%entity_type' => $entity_type))); + $this->assertEqual($entity->name->getValue(), array(0 => array('value' => $this->entityName)), format_string('%entity_type: Plain field value returned.', array('%entity_type' => $entity_type))); + + // Change the name. + $new_name = $this->randomMachineName(); + $entity->name->value = $new_name; + $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read.', array('%entity_type' => $entity_type))); + $this->assertEqual($entity->name->getValue(), array(0 => array('value' => $new_name)), format_string('%entity_type: Plain field value reflects the update.', array('%entity_type' => $entity_type))); + + $new_name = $this->randomMachineName(); + $entity->name[0]->value = $new_name; + $this->assertEqual($new_name, $entity->name->value, format_string('%entity_type: Name can be updated and read through list access.', array('%entity_type' => $entity_type))); + + // Access the user field. + $this->assertTrue($entity->user_id instanceof FieldItemListInterface, format_string('%entity_type: Field implements interface', array('%entity_type' => $entity_type))); + $this->assertTrue($entity->user_id[0] instanceof FieldItemInterface, format_string('%entity_type: Field item implements interface', array('%entity_type' => $entity_type))); + + $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type))); + + // Change the assigned user by entity. + $new_user1 = $this->createUser(); + $entity->user_id->entity = $new_user1; + $this->assertEqual($new_user1->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($new_user1->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', array('%entity_type' => $entity_type))); + + // Change the assigned user by id. + $new_user2 = $this->createUser(); + $entity->user_id->target_id = $new_user2->id(); + $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated username value can be read.', array('%entity_type' => $entity_type))); + + // Try unsetting a field property. + $entity->name->value = NULL; + $entity->user_id->target_id = NULL; + $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', array('%entity_type' => $entity_type))); + $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', array('%entity_type' => $entity_type))); + $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', array('%entity_type' => $entity_type))); + + // Test setting the values via the typed data API works as well. + // Change the assigned user by entity. + $entity->user_id->first()->get('entity')->setValue($new_user2); + $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type))); + + // Change the assigned user by id. + $entity->user_id->first()->get('target_id')->setValue($new_user2->id()); + $this->assertEqual($new_user2->id(), $entity->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($new_user2->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type))); + + // Try unsetting a field. + $entity->name->first()->get('value')->setValue(NULL); + $entity->user_id->first()->get('target_id')->setValue(NULL); + $this->assertNull($entity->name->value, format_string('%entity_type: Name field is not set.', array('%entity_type' => $entity_type))); + $this->assertNull($entity->user_id->target_id, format_string('%entity_type: User ID field is not set.', array('%entity_type' => $entity_type))); + $this->assertNull($entity->user_id->entity, format_string('%entity_type: User entity field is not set.', array('%entity_type' => $entity_type))); + + // Create a fresh entity so target_id does not get its property object + // instantiated, then verify setting a new value via typed data API works. + $entity2 = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + 'user_id' => array('target_id' => $new_user1->id()), + )); + // Access the property object, and set a value. + $entity2->user_id->first()->get('target_id')->setValue($new_user2->id()); + $this->assertEqual($new_user2->id(), $entity2->user_id->target_id, format_string('%entity_type: Updated user id can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($new_user2->name->value, $entity2->user_id->entity->name->value, format_string('%entity_type: Updated user name value can be read.', array('%entity_type' => $entity_type))); + + // Test using isset(), empty() and unset(). + $entity->name->value = 'test unset'; + unset($entity->name->value); + $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type))); + $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type))); + $this->assertTrue(empty($entity->name->value), format_string('%entity_type: Name is empty.', array('%entity_type' => $entity_type))); + $this->assertTrue(empty($entity->name[0]->value), format_string('%entity_type: Name is empty.', array('%entity_type' => $entity_type))); + + $entity->name->value = 'a value'; + $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type))); + $this->assertTrue(isset($entity->name[0]->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type))); + $this->assertFalse(empty($entity->name->value), format_string('%entity_type: Name is not empty.', array('%entity_type' => $entity_type))); + $this->assertFalse(empty($entity->name[0]->value), format_string('%entity_type: Name is not empty.', array('%entity_type' => $entity_type))); + $this->assertTrue(isset($entity->name[0]), format_string('%entity_type: Name string item is set.', array('%entity_type' => $entity_type))); + $this->assertFalse(isset($entity->name[1]), format_string('%entity_type: Second name string item is not set as it does not exist', array('%entity_type' => $entity_type))); + $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', array('%entity_type' => $entity_type))); + $this->assertFalse(isset($entity->nameInvalid), format_string('%entity_type: Not existing field is not set.', array('%entity_type' => $entity_type))); + + unset($entity->name[0]); + $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', array('%entity_type' => $entity_type))); + $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type))); + $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name is not set.', array('%entity_type' => $entity_type))); + + // Test emptying a field by assigning an empty value. NULL and array() + // behave the same. + foreach ([NULL, array(), 'unset'] as $empty) { + // Make sure a value is present + $entity->name->value = 'a value'; + $this->assertTrue(isset($entity->name->value), format_string('%entity_type: Name is set.', array('%entity_type' => $entity_type))); + // Now, empty the field. + if ($empty === 'unset') { + unset($entity->name); + } + else { + $entity->name = $empty; + } + $this->assertTrue(isset($entity->name), format_string('%entity_type: Name field is set.', array('%entity_type' => $entity_type))); + $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is set.', array('%entity_type' => $entity_type))); + $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', array('%entity_type' => $entity_type))); + $this->assertIdentical($entity->name->getValue(), array(), format_string('%entity_type: Name field value is an empty array.', array('%entity_type' => $entity_type))); + $this->assertFalse(isset($entity->name[0]), format_string('%entity_type: Name field item is not set.', array('%entity_type' => $entity_type))); + $this->assertFalse(isset($entity->name[0]->value), format_string('%entity_type: First name item value is not set.', array('%entity_type' => $entity_type))); + $this->assertFalse(isset($entity->name->value), format_string('%entity_type: Name value is not set.', array('%entity_type' => $entity_type))); + } + + // Access the language field. + $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); + $this->assertEqual($langcode, $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual(\Drupal::languageManager()->getLanguage($langcode), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type))); + + // Change the language by code. + $entity->{$langcode_key}->value = \Drupal::languageManager()->getDefaultLanguage()->getId(); + $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type))); + + // Revert language by code then try setting it by language object. + $entity->{$langcode_key}->value = $langcode; + $entity->{$langcode_key}->language = \Drupal::languageManager()->getDefaultLanguage(); + $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual(\Drupal::languageManager()->getDefaultLanguage(), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type))); + + // Access the text field and test updating. + $this->assertEqual($entity->field_test_text->value, $this->entityFieldText, format_string('%entity_type: Text field can be read.', array('%entity_type' => $entity_type))); + $new_text = $this->randomMachineName(); + $entity->field_test_text->value = $new_text; + $this->assertEqual($entity->field_test_text->value, $new_text, format_string('%entity_type: Updated text field can be read.', array('%entity_type' => $entity_type))); + + // Test creating the entity by passing in plain values. + $this->entityName = $this->randomMachineName(); + $name_item[0]['value'] = $this->entityName; + $this->entityUser = $this->createUser(); + $user_item[0]['target_id'] = $this->entityUser->id(); + $this->entityFieldText = $this->randomMachineName(); + $text_item[0]['value'] = $this->entityFieldText; + + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + 'name' => $name_item, + 'user_id' => $user_item, + 'field_test_text' => $text_item, + )); + $this->assertEqual($this->entityName, $entity->name->value, format_string('%entity_type: Name value can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', array('%entity_type' => $entity_type))); + + // Tests copying field values by assigning the TypedData objects. + $entity2 = $this->createTestEntity($entity_type); + $entity2->name = $entity->name; + $entity2->user_id = $entity->user_id; + $entity2->field_test_text = $entity->field_test_text; + $this->assertFalse($entity->name === $entity2->name, format_string('%entity_type: Copying properties results in a different field object.', array('%entity_type' => $entity_type))); + $this->assertEqual($entity->name->value, $entity2->name->value, format_string('%entity_type: Name field copied.', array('%entity_type' => $entity_type))); + $this->assertEqual($entity->user_id->target_id, $entity2->user_id->target_id, format_string('%entity_type: User id field copied.', array('%entity_type' => $entity_type))); + $this->assertEqual($entity->field_test_text->value, $entity2->field_test_text->value, format_string('%entity_type: Text field copied.', array('%entity_type' => $entity_type))); + + // Tests that assigning TypedData objects to non-field properties keeps the + // assigned value as is. + $entity2 = $this->createTestEntity($entity_type); + $entity2->_not_a_field = $entity->name; + $this->assertTrue($entity2->_not_a_field === $entity->name, format_string('%entity_type: Typed data objects can be copied to non-field properties as is.', array('%entity_type' => $entity_type))); + + // Tests adding a value to a field item list. + $entity->name[] = 'Another name'; + $this->assertEqual($entity->name[1]->value, 'Another name', format_string('%entity_type: List item added via [] and the first property.', array('%entity_type' => $entity_type))); + $entity->name[] = array('value' => 'Third name'); + $this->assertEqual($entity->name[2]->value, 'Third name', format_string('%entity_type: List item added via [] and an array of properties.', array('%entity_type' => $entity_type))); + $entity->name[3] = array('value' => 'Fourth name'); + $this->assertEqual($entity->name[3]->value, 'Fourth name', format_string('%entity_type: List item added via offset and an array of properties.', array('%entity_type' => $entity_type))); + unset($entity->name[3]); + + // Test removing and empty-ing list items. + $this->assertEqual(count($entity->name), 3, format_string('%entity_type: List has 3 items.', array('%entity_type' => $entity_type))); + unset($entity->name[1]); + $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Second list item has been removed.', array('%entity_type' => $entity_type))); + $this->assertEqual($entity->name[1]->value, 'Third name', format_string('%entity_type: The subsequent items have been shifted up.', array('%entity_type' => $entity_type))); + $this->assertEqual($entity->name[1]->getName(), 1, format_string('%entity_type: The items names have been updated to their new delta.', array('%entity_type' => $entity_type))); + $entity->name[1] = NULL; + $this->assertEqual(count($entity->name), 2, format_string('%entity_type: Assigning NULL does not reduce array count.', array('%entity_type' => $entity_type))); + $this->assertTrue($entity->name[1]->isEmpty(), format_string('%entity_type: Assigning NULL empties the item.', array('%entity_type' => $entity_type))); + + // Test using isEmpty(). + unset($entity->name[1]); + $this->assertFalse($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is not empty.', array('%entity_type' => $entity_type))); + $entity->name->value = NULL; + $this->assertTrue($entity->name[0]->isEmpty(), format_string('%entity_type: Name item is empty.', array('%entity_type' => $entity_type))); + $this->assertTrue($entity->name->isEmpty(), format_string('%entity_type: Name field is empty.', array('%entity_type' => $entity_type))); + $this->assertEqual(count($entity->name), 1, format_string('%entity_type: Empty item is considered when counting.', array('%entity_type' => $entity_type))); + $this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), format_string('%entity_type: Count matches iterator count.', array('%entity_type' => $entity_type))); + $this->assertTrue($entity->name->getValue() === array(0 => array('value' => NULL)), format_string('%entity_type: Name field value contains a NULL value.', array('%entity_type' => $entity_type))); + + // Test using filterEmptyItems(). + $entity->name = array(NULL, 'foo'); + $this->assertEqual(count($entity->name), 2, format_string('%entity_type: List has 2 items.', array('%entity_type' => $entity_type))); + $entity->name->filterEmptyItems(); + $this->assertEqual(count($entity->name), 1, format_string('%entity_type: The empty item was removed.', array('%entity_type' => $entity_type))); + $this->assertEqual($entity->name[0]->value, 'foo', format_string('%entity_type: The items were renumbered.', array('%entity_type' => $entity_type))); + $this->assertEqual($entity->name[0]->getName(), 0, format_string('%entity_type: The deltas were updated in the items.', array('%entity_type' => $entity_type))); + + // Test get and set field values. + $entity->name = 'foo'; + $this->assertEqual($entity->name[0]->toArray(), array('value' => 'foo'), format_string('%entity_type: Field value has been retrieved via toArray()', array('%entity_type' => $entity_type))); + + $values = $entity->toArray(); + $this->assertEqual($values['name'], array(0 => array('value' => 'foo')), format_string('%entity_type: Field value has been retrieved via toArray() from an entity.', array('%entity_type' => $entity_type))); + + // Make sure the user id can be set to zero. + $user_item[0]['target_id'] = 0; + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + 'name' => $name_item, + 'user_id' => $user_item, + 'field_test_text' => $text_item, + )); + $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', array('%entity_type' => $entity_type))); + $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', array('%entity_type' => $entity_type))); + + // Test setting the ID with the value only. + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + 'name' => $name_item, + 'user_id' => 0, + 'field_test_text' => $text_item, + )); + $this->assertNotNull($entity->user_id->target_id, format_string('%entity_type: User id is not NULL', array('%entity_type' => $entity_type))); + $this->assertIdentical($entity->user_id->target_id, 0, format_string('%entity_type: User id has been set to 0', array('%entity_type' => $entity_type))); + } + + /** + * Tries to save and load an entity again. + */ + public function testSave() { + // All entity variations have to have the same results. + foreach (entity_test_entity_types() as $entity_type) { + $this->doTestSave($entity_type); + } + } + + /** + * Executes the save tests for the given entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function doTestSave($entity_type) { + $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); + $entity = $this->createTestEntity($entity_type); + $entity->save(); + $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity has received an id.', array('%entity_type' => $entity_type))); + + $entity = entity_load($entity_type, $entity->id()); + $this->assertTrue((bool) $entity->id(), format_string('%entity_type: Entity loaded.', array('%entity_type' => $entity_type))); + + // Access the name field. + $this->assertEqual(1, $entity->id->value, format_string('%entity_type: ID value can be read.', array('%entity_type' => $entity_type))); + $this->assertTrue(is_string($entity->uuid->value), format_string('%entity_type: UUID value can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual('en', $entity->{$langcode_key}->value, format_string('%entity_type: Language code can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual(\Drupal::languageManager()->getLanguage('en'), $entity->{$langcode_key}->language, format_string('%entity_type: Language object can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($this->entityUser->id(), $entity->user_id->target_id, format_string('%entity_type: User id can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($this->entityUser->getUsername(), $entity->user_id->entity->name->value, format_string('%entity_type: User name can be read.', array('%entity_type' => $entity_type))); + $this->assertEqual($this->entityFieldText, $entity->field_test_text->value, format_string('%entity_type: Text field can be read.', array('%entity_type' => $entity_type))); + } + + /** + * Tests introspection and getting metadata upfront. + */ + public function testIntrospection() { + // All entity variations have to have the same results. + foreach (entity_test_entity_types() as $entity_type) { + $this->doTestIntrospection($entity_type); + } + } + + /** + * Executes the introspection tests for the given entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function doTestIntrospection($entity_type) { + // Test getting metadata upfront. The entity types used for this test have + // a default bundle that is the same as the entity type. + $definitions = \Drupal::entityManager()->getFieldDefinitions($entity_type, $entity_type); + $this->assertEqual($definitions['name']->getType(), 'string', $entity_type .': Name field found.'); + $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type .': User field found.'); + $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type .': Test-text-field field found.'); + + // Test deriving further metadata. + $this->assertTrue($definitions['name'] instanceof FieldDefinitionInterface); + $field_item_definition = $definitions['name']->getItemDefinition(); + $this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface); + $this->assertEqual($field_item_definition->getDataType(), 'field_item:string'); + $value_definition = $field_item_definition->getPropertyDefinition('value'); + $this->assertTrue($value_definition instanceof DataDefinitionInterface); + $this->assertEqual($value_definition->getDataType(), 'string'); + + // Test deriving metadata from references. + $entity_definition = \Drupal\Core\Entity\TypedData\EntityDataDefinition::create($entity_type); + $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); + $reference_definition = $entity_definition->getPropertyDefinition($langcode_key) + ->getPropertyDefinition('language') + ->getTargetDefinition(); + $this->assertEqual($reference_definition->getDataType(), 'language'); + + $reference_definition = $entity_definition->getPropertyDefinition('user_id') + ->getPropertyDefinition('entity') + ->getTargetDefinition(); + + $this->assertTrue($reference_definition instanceof \Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface, 'Definition of the referenced user retrieved.'); + $this->assertEqual($reference_definition->getEntityTypeId(), 'user', 'Referenced entity is of type "user".'); + + // Test propagating down. + $name_definition = $reference_definition->getPropertyDefinition('name'); + $this->assertTrue($name_definition instanceof FieldDefinitionInterface); + $this->assertEqual($name_definition->getPropertyDefinition('value')->getDataType(), 'string'); + + // Test introspecting an entity object. + // @todo: Add bundles and test bundles as well. + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(); + + $definitions = $entity->getFieldDefinitions(); + $this->assertEqual($definitions['name']->getType(), 'string', $entity_type .': Name field found.'); + $this->assertEqual($definitions['user_id']->getType(), 'entity_reference', $entity_type .': User field found.'); + $this->assertEqual($definitions['field_test_text']->getType(), 'text', $entity_type .': Test-text-field field found.'); + + $name_properties = $entity->name->getFieldDefinition()->getPropertyDefinitions(); + $this->assertEqual($name_properties['value']->getDataType(), 'string', $entity_type .': String value property of the name found.'); + + $userref_properties = $entity->user_id->getFieldDefinition()->getPropertyDefinitions(); + $this->assertEqual($userref_properties['target_id']->getDataType(), 'integer', $entity_type .': Entity id property of the user found.'); + $this->assertEqual($userref_properties['entity']->getDataType(), 'entity_reference', $entity_type .': Entity reference property of the user found.'); + + $textfield_properties = $entity->field_test_text->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinitions(); + $this->assertEqual($textfield_properties['value']->getDataType(), 'string', $entity_type .': String value property of the test-text field found.'); + $this->assertEqual($textfield_properties['format']->getDataType(), 'filter_format', $entity_type .': String format field of the test-text field found.'); + $this->assertEqual($textfield_properties['processed']->getDataType(), 'string', $entity_type .': String processed property of the test-text field found.'); + + // Make sure provided contextual information is right. + $entity_adapter = $entity->getTypedData(); + $this->assertIdentical($entity_adapter->getRoot(), $entity_adapter, 'Entity is root object.'); + $this->assertEqual($entity_adapter->getPropertyPath(), ''); + $this->assertEqual($entity_adapter->getName(), ''); + $this->assertEqual($entity_adapter->getParent(), NULL); + + $field = $entity->user_id; + $this->assertIdentical($field->getRoot()->getValue(), $entity, 'Entity is root object.'); + $this->assertIdentical($field->getEntity(), $entity, 'getEntity() returns the entity.'); + $this->assertEqual($field->getPropertyPath(), 'user_id'); + $this->assertEqual($field->getName(), 'user_id'); + $this->assertIdentical($field->getParent()->getValue(), $entity, 'Parent object matches.'); + + $field_item = $field[0]; + $this->assertIdentical($field_item->getRoot()->getValue(), $entity, 'Entity is root object.'); + $this->assertIdentical($field_item->getEntity(), $entity, 'getEntity() returns the entity.'); + $this->assertEqual($field_item->getPropertyPath(), 'user_id.0'); + $this->assertEqual($field_item->getName(), '0'); + $this->assertIdentical($field_item->getParent(), $field, 'Parent object matches.'); + + $item_value = $field_item->get('entity'); + $this->assertIdentical($item_value->getRoot()->getValue(), $entity, 'Entity is root object.'); + $this->assertEqual($item_value->getPropertyPath(), 'user_id.0.entity'); + $this->assertEqual($item_value->getName(), 'entity'); + $this->assertIdentical($item_value->getParent(), $field_item, 'Parent object matches.'); + } + + /** + * Tests iterating over properties. + */ + public function testIterator() { + // All entity variations have to have the same results. + foreach (entity_test_entity_types() as $entity_type) { + $this->doTestIterator($entity_type); + } + } + + /** + * Executes the iterator tests for the given entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function doTestIterator($entity_type) { + $entity = $this->createTestEntity($entity_type); + + foreach ($entity as $name => $field) { + $this->assertTrue($field instanceof FieldItemListInterface, $entity_type . ": Field $name implements interface."); + + foreach ($field as $delta => $item) { + $this->assertTrue($field[0] instanceof FieldItemInterface, $entity_type . ": Item $delta of field $name implements interface."); + + foreach ($item as $value_name => $value_property) { + $this->assertTrue($value_property instanceof TypedDataInterface, $entity_type . ": Value $value_name of item $delta of field $name implements interface."); + + $value = $value_property->getValue(); + $this->assertTrue(!isset($value) || is_scalar($value) || $value instanceof EntityInterface, $entity_type . ": Value $value_name of item $delta of field $name is a primitive or an entity."); + } + } + } + + $fields = $entity->getFields(); + $this->assertEqual(array_keys($fields), array_keys($entity->getTypedData()->getDataDefinition()->getPropertyDefinitions()), format_string('%entity_type: All fields returned.', array('%entity_type' => $entity_type))); + $this->assertEqual($fields, iterator_to_array($entity->getIterator()), format_string('%entity_type: Entity iterator iterates over all fields.', array('%entity_type' => $entity_type))); + } + + /** + * Tests working with the entity based upon the TypedData API. + */ + public function testDataStructureInterfaces() { + // All entity variations have to have the same results. + foreach (entity_test_entity_types() as $entity_type) { + $this->doTestDataStructureInterfaces($entity_type); + } + } + + /** + * Executes the data structure interfaces tests for the given entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function doTestDataStructureInterfaces($entity_type) { + $entity = $this->createTestEntity($entity_type); + + // Test using the whole tree of typed data by navigating through the tree of + // contained properties and getting all contained strings, limited by a + // certain depth. + $strings = array(); + $this->getContainedStrings($entity->getTypedData(), 0, $strings); + + // @todo: Once the user entity has defined properties this should contain + // the user name and other user entity strings as well. + $target_strings = array( + $entity->uuid->value, + 'en', + $this->entityName, + // Bundle name. + $entity->bundle(), + $this->entityFieldText, + // Field format. + NULL, + ); + asort($strings); + asort($target_strings); + $this->assertEqual(array_values($strings), array_values($target_strings), format_string('%entity_type: All contained strings found.', array('%entity_type' => $entity_type))); + } + + /** + * Recursive helper for getting all contained strings, + * i.e. properties of type string. + */ + public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &$strings) { + + if ($wrapper instanceof StringInterface) { + $strings[] = $wrapper->getValue(); + } + + // Recurse until a certain depth is reached if possible. + if ($depth < 7) { + if ($wrapper instanceof \Drupal\Core\TypedData\ListInterface) { + foreach ($wrapper as $item) { + $this->getContainedStrings($item, $depth + 1, $strings); + } + } + elseif ($wrapper instanceof \Drupal\Core\TypedData\ComplexDataInterface) { + foreach ($wrapper as $property) { + $this->getContainedStrings($property, $depth + 1, $strings); + } + } + } + } + + /** + * Makes sure data types are correctly derived for all entity types. + */ + public function testDataTypes() { + $types = \Drupal::typedDataManager()->getDefinitions(); + foreach (entity_test_entity_types() as $entity_type) { + $this->assertTrue($types['entity:' . $entity_type]['class'], 'Entity data type registered.'); + } + // Check bundle types are provided as well. + entity_test_create_bundle('bundle'); + $types = \Drupal::typedDataManager()->getDefinitions(); + $this->assertTrue($types['entity:entity_test:bundle']['class'], 'Entity bundle data type registered.'); + } + + /** + * Tests a base field override on a non-existing base field. + * + * @see entity_test_entity_base_field_info_alter() + */ + public function testBaseFieldNonExistingBaseField() { + $this->entityManager->getStorage('node_type')->create(array( + 'type' => 'page', + 'name' => 'page', + ))->save(); + $this->entityManager->clearCachedFieldDefinitions(); + $fields = $this->entityManager->getFieldDefinitions('node', 'page'); + $override = $fields['status']->getConfig('page'); + $override->setLabel($this->randomString())->save(); + \Drupal::state()->set('entity_test.node_remove_status_field', TRUE); + $this->entityManager->clearCachedFieldDefinitions(); + $fields = $this->entityManager->getFieldDefinitions('node', 'page'); + // A base field override on a non-existing base field should not cause a + // field definition to come into existence. + $this->assertFalse(isset($fields['status']), 'Node\'s status base field does not exist.'); + } + + /** + * Tests creating a field override config for a bundle field. + * + * @see entity_test_entity_base_field_info_alter() + */ + public function testFieldOverrideBundleField() { + // First make sure the bundle field override in code, which is provided by + // the test entity works. + entity_test_create_bundle('some_test_bundle', 'Some test bundle', 'entity_test_field_override'); + $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'entity_test_field_override'); + $this->assertEqual($field_definitions['name']->getDescription(), 'The default description.'); + $this->assertNull($field_definitions['name']->getTargetBundle()); + + $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle'); + $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.'); + $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle'); + + // Now create a config override of the bundle field. + $field_config = $field_definitions['name']->getConfig('some_test_bundle'); + $field_config->setTranslatable(FALSE); + $field_config->save(); + + // Make sure both overrides are present. + $this->entityManager->clearCachedFieldDefinitions(); + $field_definitions = $this->entityManager->getFieldDefinitions('entity_test_field_override', 'some_test_bundle'); + $this->assertEqual($field_definitions['name']->getDescription(), 'Custom description.'); + $this->assertEqual($field_definitions['name']->getTargetBundle(), 'some_test_bundle'); + $this->assertFalse($field_definitions['name']->isTranslatable()); + } + + /** + * Tests validation constraints provided by the Entity API. + */ + public function testEntityConstraintValidation() { + $entity = $this->createTestEntity('entity_test'); + $entity->save(); + // Create a reference field item and let it reference the entity. + $definition = BaseFieldDefinition::create('entity_reference') + ->setLabel('Test entity') + ->setSetting('target_type', 'entity_test'); + $reference_field = \Drupal::typedDataManager()->create($definition); + $reference = $reference_field->appendItem(array('entity' => $entity))->get('entity'); + + // Test validation the typed data object. + $violations = $reference->validate(); + $this->assertEqual($violations->count(), 0); + + // Test validating an entity of the wrong type. + $user = $this->createUser(); + $user->save(); + $node = $node = Node::create([ + 'type' => 'page', + 'uid' => $user->id(), + 'title' => $this->randomString(), + ]); + $reference->setValue($node); + $violations = $reference->validate(); + $this->assertEqual($violations->count(), 1); + + // Test bundle validation. + NodeType::create(array('type' => 'article')) + ->save(); + $definition = BaseFieldDefinition::create('entity_reference') + ->setLabel('Test entity') + ->setSetting('target_type', 'node') + ->setSetting('handler_settings', ['target_bundles' => ['article' => 'article']]); + $reference_field = \Drupal::TypedDataManager()->create($definition); + $reference_field->appendItem(array('entity' => $node)); + $violations = $reference_field->validate(); + $this->assertEqual($violations->count(), 1); + + $node = Node::create([ + 'type' => 'article', + 'uid' => $user->id(), + 'title' => $this->randomString(), + ]); + $node->save(); + $reference_field->entity = $node; + $violations = $reference_field->validate(); + $this->assertEqual($violations->count(), 0); + } + + /** + * Tests getting processed property values via a computed property. + */ + public function testComputedProperties() { + // All entity variations have to have the same results. + foreach (entity_test_entity_types() as $entity_type) { + $this->doTestComputedProperties($entity_type); + } + } + + /** + * Executes the computed properties tests for the given entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function doTestComputedProperties($entity_type) { + $entity = $this->createTestEntity($entity_type); + $entity->field_test_text->value = "The text text to filter."; + $entity->field_test_text->format = filter_default_format(); + + $target = "

                                  The <strong>text</strong> text to filter.

                                  \n"; + $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', array('%entity_type' => $entity_type))); + + // Save and load entity and make sure it still works. + $entity->save(); + $entity = entity_load($entity_type, $entity->id()); + $this->assertEqual($entity->field_test_text->processed, $target, format_string('%entity_type: Text is processed with the default filter.', array('%entity_type' => $entity_type))); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityLanguageTestBase.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityLanguageTestBase.php new file mode 100644 index 0000000..8897eeb --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityLanguageTestBase.php @@ -0,0 +1,141 @@ +languageManager = $this->container->get('language_manager'); + + foreach (entity_test_entity_types() as $entity_type_id) { + // The entity_test schema is installed by the parent. + if ($entity_type_id != 'entity_test') { + $this->installEntitySchema($entity_type_id); + } + } + + $this->installConfig(array('language')); + + // Create the test field. + module_load_install('entity_test'); + entity_test_install(); + + // Enable translations for the test entity type. + $this->state->set('entity_test.translation', TRUE); + + // Create a translatable test field. + $this->fieldName = Unicode::strtolower($this->randomMachineName() . '_field_name'); + + // Create an untranslatable test field. + $this->untranslatableFieldName = Unicode::strtolower($this->randomMachineName() . '_field_name'); + + // Create field fields in all entity variations. + foreach (entity_test_entity_types() as $entity_type) { + FieldStorageConfig::create(array( + 'field_name' => $this->fieldName, + 'entity_type' => $entity_type, + 'type' => 'text', + 'cardinality' => 4, + ))->save(); + FieldConfig::create([ + 'field_name' => $this->fieldName, + 'entity_type' => $entity_type, + 'bundle' => $entity_type, + 'translatable' => TRUE, + ])->save(); + + FieldStorageConfig::create(array( + 'field_name' => $this->untranslatableFieldName, + 'entity_type' => $entity_type, + 'type' => 'text', + 'cardinality' => 4, + ))->save(); + FieldConfig::create([ + 'field_name' => $this->untranslatableFieldName, + 'entity_type' => $entity_type, + 'bundle' => $entity_type, + 'translatable' => FALSE, + ])->save(); + } + + // Create the default languages. + $this->installConfig(array('language')); + + // Create test languages. + $this->langcodes = array(); + for ($i = 0; $i < 3; ++$i) { + $language = ConfigurableLanguage::create(array( + 'id' => 'l' . $i, + 'label' => $this->randomString(), + 'weight' => $i, + )); + $this->langcodes[$i] = $language->getId(); + $language->save(); + } + } + + /** + * Toggles field storage translatability. + * + * @param string $entity_type + * The type of the entity fields are attached to. + */ + protected function toggleFieldTranslatability($entity_type, $bundle) { + $fields = array($this->fieldName, $this->untranslatableFieldName); + foreach ($fields as $field_name) { + $field = FieldConfig::loadByName($entity_type, $bundle, $field_name); + $translatable = !$field->isTranslatable(); + $field->set('translatable', $translatable); + $field->save(); + $field = FieldConfig::loadByName($entity_type, $bundle, $field_name); + $this->assertEqual($field->isTranslatable(), $translatable, 'Field translatability changed.'); + } + \Drupal::cache('entity')->deleteAll(); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryAggregateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryAggregateTest.php new file mode 100644 index 0000000..d7ab5e9 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryAggregateTest.php @@ -0,0 +1,589 @@ +entityStorage = $this->entityManager->getStorage('entity_test'); + $this->factory = $this->container->get('entity.query'); + + // Add some fieldapi fields to be used in the test. + for ($i = 1; $i <= 2; $i++) { + $field_name = 'field_test_' . $i; + FieldStorageConfig::create(array( + 'field_name' => $field_name, + 'entity_type' => 'entity_test', + 'type' => 'integer', + 'cardinality' => 2, + ))->save(); + FieldConfig::create([ + 'field_name' => $field_name, + 'entity_type' => 'entity_test', + 'bundle' => 'entity_test', + ])->save(); + } + + $entity = $this->entityStorage->create(array( + 'id' => 1, + 'user_id' => 1, + 'field_test_1' => 1, + 'field_test_2' => 2, + )); + $entity->enforceIsNew(); + $entity->save(); + + $entity = $this->entityStorage->create(array( + 'id' => 2, + 'user_id' => 2, + 'field_test_1' => 1, + 'field_test_2' => 7, + )); + $entity->enforceIsNew(); + $entity->save(); + $entity = $this->entityStorage->create(array( + 'id' => 3, + 'user_id' => 2, + 'field_test_1' => 2, + 'field_test_2' => 1, + )); + $entity->enforceIsNew(); + $entity->save(); + $entity = $this->entityStorage->create(array( + 'id' => 4, + 'user_id' => 2, + 'field_test_1' => 2, + 'field_test_2' => 8, + )); + $entity->enforceIsNew(); + $entity->save(); + $entity = $this->entityStorage->create(array( + 'id' => 5, + 'user_id' => 3, + 'field_test_1' => 2, + 'field_test_2' => 2, + )); + $entity->enforceIsNew(); + $entity->save(); + $entity = $this->entityStorage->create(array( + 'id' => 6, + 'user_id' => 3, + 'field_test_1' => 3, + 'field_test_2' => 8, + )); + $entity->enforceIsNew(); + $entity->save(); + + } + + /** + * Test aggregation support. + */ + public function testAggregation() { + // Apply a simple groupby. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->groupBy('user_id') + ->execute(); + + $this->assertResults(array( + array('user_id' => 1), + array('user_id' => 2), + array('user_id' => 3), + )); + + $function_expected = array(); + $function_expected['count'] = array(array('id_count' => 6)); + $function_expected['min'] = array(array('id_min' => 1)); + $function_expected['max'] = array(array('id_max' => 6)); + $function_expected['sum'] = array(array('id_sum' => 21)); + $function_expected['avg'] = array(array('id_avg' => (21.0/6.0))); + + // Apply a simple aggregation for different aggregation functions. + foreach ($function_expected as $aggregation_function => $expected) { + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('id', $aggregation_function) + ->execute(); + $this->assertEqual($this->queryResult, $expected); + } + + // Apply aggregation and groupby on the same query. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('id', 'COUNT') + ->groupBy('user_id') + ->execute(); + $this->assertResults(array( + array('user_id' => 1, 'id_count' => 1), + array('user_id' => 2, 'id_count' => 3), + array('user_id' => 3, 'id_count' => 2), + )); + + // Apply aggregation and a condition which matches. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('id', 'COUNT') + ->groupBy('id') + ->conditionAggregate('id', 'COUNT', 8) + ->execute(); + $this->assertResults(array()); + + // Don't call aggregate to test the implicit aggregate call. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->groupBy('id') + ->conditionAggregate('id', 'COUNT', 8) + ->execute(); + $this->assertResults(array()); + + // Apply aggregation and a condition which matches. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('id', 'count') + ->groupBy('id') + ->conditionAggregate('id', 'COUNT', 6) + ->execute(); + $this->assertResults(array(array('id_count' => 6))); + + // Apply aggregation, a groupby and a condition which matches partially via + // the operator '='. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('id', 'count') + ->conditionAggregate('id', 'count', 2) + ->groupBy('user_id') + ->execute(); + $this->assertResults(array(array('id_count' => 2, 'user_id' => 3))); + + // Apply aggregation, a groupby and a condition which matches partially via + // the operator '>'. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('id', 'count') + ->conditionAggregate('id', 'COUNT', 1, '>') + ->groupBy('user_id') + ->execute(); + $this->assertResults(array( + array('id_count' => 2, 'user_id' => 3), + array('id_count' => 3, 'user_id' => 2), + )); + + // Apply aggregation and a sort. This might not be useful, but have a proper + // test coverage. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('id', 'COUNT') + ->sortAggregate('id', 'COUNT') + ->execute(); + $this->assertSortedResults(array(array('id_count' => 6))); + + // Don't call aggregate to test the implicit aggregate call. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->sortAggregate('id', 'COUNT') + ->execute(); + $this->assertSortedResults(array(array('id_count' => 6))); + + // Apply aggregation, groupby and a sort descending. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('id', 'COUNT') + ->groupBy('user_id') + ->sortAggregate('id', 'COUNT', 'DESC') + ->execute(); + $this->assertSortedResults(array( + array('user_id' => 2, 'id_count' => 3), + array('user_id' => 3, 'id_count' => 2), + array('user_id' => 1, 'id_count' => 1), + )); + + // Apply aggregation, groupby and a sort ascending. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('id', 'COUNT') + ->groupBy('user_id') + ->sortAggregate('id', 'COUNT', 'ASC') + ->execute(); + $this->assertSortedResults(array( + array('user_id' => 1, 'id_count' => 1), + array('user_id' => 3, 'id_count' => 2), + array('user_id' => 2, 'id_count' => 3), + )); + + // Apply aggregation, groupby, an aggregation condition and a sort with the + // operator '='. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('id', 'COUNT') + ->groupBy('user_id') + ->sortAggregate('id', 'COUNT') + ->conditionAggregate('id', 'COUNT', 2) + ->execute(); + $this->assertSortedResults(array(array('id_count' => 2, 'user_id' => 3))); + + // Apply aggregation, groupby, an aggregation condition and a sort with the + // operator '<' and order ASC. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('id', 'COUNT') + ->groupBy('user_id') + ->sortAggregate('id', 'COUNT', 'ASC') + ->conditionAggregate('id', 'COUNT', 3, '<') + ->execute(); + $this->assertSortedResults(array( + array('id_count' => 1, 'user_id' => 1), + array('id_count' => 2, 'user_id' => 3), + )); + + // Apply aggregation, groupby, an aggregation condition and a sort with the + // operator '<' and order DESC. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('id', 'COUNT') + ->groupBy('user_id') + ->sortAggregate('id', 'COUNT', 'DESC') + ->conditionAggregate('id', 'COUNT', 3, '<') + ->execute(); + $this->assertSortedResults(array( + array('id_count' => 2, 'user_id' => 3), + array('id_count' => 1, 'user_id' => 1), + )); + + // Test aggregation/groupby support for fieldapi fields. + + // Just group by a fieldapi field. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->groupBy('field_test_1') + ->execute(); + $this->assertResults(array( + array('field_test_1' => 1), + array('field_test_1' => 2), + array('field_test_1' => 3), + )); + + // Group by a fieldapi field and aggregate a normal property. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('user_id', 'COUNT') + ->groupBy('field_test_1') + ->execute(); + + $this->assertResults(array( + array('field_test_1' => 1, 'user_id_count' => 2), + array('field_test_1' => 2, 'user_id_count' => 3), + array('field_test_1' => 3, 'user_id_count' => 1), + )); + + // Group by a normal property and aggregate a fieldapi field. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('field_test_1', 'COUNT') + ->groupBy('user_id') + ->execute(); + + $this->assertResults(array( + array('user_id' => 1, 'field_test_1_count' => 1), + array('user_id' => 2, 'field_test_1_count' => 3), + array('user_id' => 3, 'field_test_1_count' => 2), + )); + + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('field_test_1', 'SUM') + ->groupBy('user_id') + ->execute(); + $this->assertResults(array( + array('user_id' => 1, 'field_test_1_sum' => 1), + array('user_id' => 2, 'field_test_1_sum' => 5), + array('user_id' => 3, 'field_test_1_sum' => 5), + )); + + // Aggregate by two different fieldapi fields. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('field_test_1', 'SUM') + ->aggregate('field_test_2', 'SUM') + ->groupBy('user_id') + ->execute(); + $this->assertResults(array( + array('user_id' => 1, 'field_test_1_sum' => 1, 'field_test_2_sum' => 2), + array('user_id' => 2, 'field_test_1_sum' => 5, 'field_test_2_sum' => 16), + array('user_id' => 3, 'field_test_1_sum' => 5, 'field_test_2_sum' => 10), + )); + + // This time aggregate the same field twice. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('field_test_1', 'SUM') + ->aggregate('field_test_1', 'COUNT') + ->groupBy('user_id') + ->execute(); + $this->assertResults(array( + array('user_id' => 1, 'field_test_1_sum' => 1, 'field_test_1_count' => 1), + array('user_id' => 2, 'field_test_1_sum' => 5, 'field_test_1_count' => 3), + array('user_id' => 3, 'field_test_1_sum' => 5, 'field_test_1_count' => 2), + )); + + // Group by and aggregate by a fieldapi field. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->groupBy('field_test_1') + ->aggregate('field_test_2', 'COUNT') + ->execute(); + $this->assertResults(array( + array('field_test_1' => 1, 'field_test_2_count' => 2), + array('field_test_1' => 2, 'field_test_2_count' => 3), + array('field_test_1' => 3, 'field_test_2_count' => 1), + )); + + // Group by and aggregate by a fieldapi field and use multiple aggregate + // functions. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->groupBy('field_test_1') + ->aggregate('field_test_2', 'COUNT') + ->aggregate('field_test_2', 'SUM') + ->execute(); + $this->assertResults(array( + array('field_test_1' => 1, 'field_test_2_count' => 2, 'field_test_2_sum' => 9), + array('field_test_1' => 2, 'field_test_2_count' => 3, 'field_test_2_sum' => 11), + array('field_test_1' => 3, 'field_test_2_count' => 1, 'field_test_2_sum' => 8), + )); + + // Apply an aggregate condition for a fieldapi field and group by a simple + // property. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->conditionAggregate('field_test_1', 'COUNT', 3) + ->groupBy('user_id') + ->execute(); + $this->assertResults(array( + array('user_id' => 2, 'field_test_1_count' => 3), + array('user_id' => 3, 'field_test_1_count' => 2), + )); + + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('field_test_1', 'SUM') + ->conditionAggregate('field_test_1', 'COUNT', 2, '>') + ->groupBy('user_id') + ->execute(); + $this->assertResults(array( + array('user_id' => 2, 'field_test_1_sum' => 5, 'field_test_1_count' => 3), + array('user_id' => 3, 'field_test_1_sum' => 5, 'field_test_1_count' => 2), + )); + + // Apply an aggregate condition for a simple property and a group by a + // fieldapi field. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->conditionAggregate('user_id', 'COUNT', 2) + ->groupBy('field_test_1') + ->execute(); + $this->assertResults(array( + array('field_test_1' => 1, 'user_id_count' => 2), + )); + + $this->queryResult = $this->factory->getAggregate('entity_test') + ->conditionAggregate('user_id', 'COUNT', 2, '>') + ->groupBy('field_test_1') + ->execute(); + $this->assertResults(array( + array('field_test_1' => 1, 'user_id_count' => 2), + array('field_test_1' => 2, 'user_id_count' => 3), + )); + + // Apply an aggregate condition and a group by fieldapi fields. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->groupBy('field_test_1') + ->conditionAggregate('field_test_2', 'COUNT', 2) + ->execute(); + $this->assertResults(array( + array('field_test_1' => 1, 'field_test_2_count' => 2), + )); + $this->queryResult = $this->factory->getAggregate('entity_test') + ->groupBy('field_test_1') + ->conditionAggregate('field_test_2', 'COUNT', 2, '>') + ->execute(); + $this->assertResults(array( + array('field_test_1' => 1, 'field_test_2_count' => 2), + array('field_test_1' => 2, 'field_test_2_count' => 3), + )); + + // Apply an aggregate condition and a group by fieldapi fields with multiple + // conditions via AND. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->groupBy('field_test_1') + ->conditionAggregate('field_test_2', 'COUNT', 2) + ->conditionAggregate('field_test_2', 'SUM', 8) + ->execute(); + $this->assertResults(array()); + + // Apply an aggregate condition and a group by fieldapi fields with multiple + // conditions via OR. + $this->queryResult = $this->factory->getAggregate('entity_test', 'OR') + ->groupBy('field_test_1') + ->conditionAggregate('field_test_2', 'COUNT', 2) + ->conditionAggregate('field_test_2', 'SUM', 8) + ->execute(); + $this->assertResults(array( + array('field_test_1' => 1, 'field_test_2_count' => 2, 'field_test_2_sum' => 9), + array('field_test_1' => 3, 'field_test_2_count' => 1, 'field_test_2_sum' => 8), + )); + + // Group by a normal property and aggregate a fieldapi field and sort by the + // groupby field. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('field_test_1', 'COUNT') + ->groupBy('user_id') + ->sort('user_id', 'DESC') + ->execute(); + $this->assertSortedResults(array( + array('user_id' => 3, 'field_test_1_count' => 2), + array('user_id' => 2, 'field_test_1_count' => 3), + array('user_id' => 1, 'field_test_1_count' => 1), + )); + + $this->queryResult = $this->factory->getAggregate('entity_test') + ->aggregate('field_test_1', 'COUNT') + ->groupBy('user_id') + ->sort('user_id', 'ASC') + ->execute(); + $this->assertSortedResults(array( + array('user_id' => 1, 'field_test_1_count' => 1), + array('user_id' => 2, 'field_test_1_count' => 3), + array('user_id' => 3, 'field_test_1_count' => 2), + )); + + $this->queryResult = $this->factory->getAggregate('entity_test') + ->conditionAggregate('field_test_1', 'COUNT', 2, '>') + ->groupBy('user_id') + ->sort('user_id', 'ASC') + ->execute(); + $this->assertSortedResults(array( + array('user_id' => 2, 'field_test_1_count' => 3), + array('user_id' => 3, 'field_test_1_count' => 2), + )); + + // Group by a normal property, aggregate a fieldapi field, and sort by the + // aggregated field. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->sortAggregate('field_test_1', 'COUNT', 'DESC') + ->groupBy('user_id') + ->execute(); + $this->assertSortedResults(array( + array('user_id' => 2, 'field_test_1_count' => 3), + array('user_id' => 3, 'field_test_1_count' => 2), + array('user_id' => 1, 'field_test_1_count' => 1), + )); + + $this->queryResult = $this->factory->getAggregate('entity_test') + ->sortAggregate('field_test_1', 'COUNT', 'ASC') + ->groupBy('user_id') + ->execute(); + $this->assertSortedResults(array( + array('user_id' => 1, 'field_test_1_count' => 1), + array('user_id' => 3, 'field_test_1_count' => 2), + array('user_id' => 2, 'field_test_1_count' => 3), + )); + + // Group by and aggregate by fieldapi field, and sort by the groupby field. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->groupBy('field_test_1') + ->aggregate('field_test_2', 'COUNT') + ->sort('field_test_1', 'ASC') + ->execute(); + $this->assertSortedResults(array( + array('field_test_1' => 1, 'field_test_2_count' => 2), + array('field_test_1' => 2, 'field_test_2_count' => 3), + array('field_test_1' => 3, 'field_test_2_count' => 1), + )); + + $this->queryResult = $this->factory->getAggregate('entity_test') + ->groupBy('field_test_1') + ->aggregate('field_test_2', 'COUNT') + ->sort('field_test_1', 'DESC') + ->execute(); + $this->assertSortedResults(array( + array('field_test_1' => 3, 'field_test_2_count' => 1), + array('field_test_1' => 2, 'field_test_2_count' => 3), + array('field_test_1' => 1, 'field_test_2_count' => 2), + )); + + // Groupby and aggregate by fieldapi field, and sort by the aggregated + // field. + $this->queryResult = $this->factory->getAggregate('entity_test') + ->groupBy('field_test_1') + ->sortAggregate('field_test_2', 'COUNT', 'DESC') + ->execute(); + $this->assertSortedResults(array( + array('field_test_1' => 2, 'field_test_2_count' => 3), + array('field_test_1' => 1, 'field_test_2_count' => 2), + array('field_test_1' => 3, 'field_test_2_count' => 1), + )); + + $this->queryResult = $this->factory->getAggregate('entity_test') + ->groupBy('field_test_1') + ->sortAggregate('field_test_2', 'COUNT', 'ASC') + ->execute(); + $this->assertSortedResults(array( + array('field_test_1' => 3, 'field_test_2_count' => 1), + array('field_test_1' => 1, 'field_test_2_count' => 2), + array('field_test_1' => 2, 'field_test_2_count' => 3), + )); + + } + + /** + * Asserts the results as expected regardless of order between and in rows. + * + * @param array $expected + * An array of the expected results. + */ + protected function assertResults($expected, $sorted = FALSE) { + $found = TRUE; + $expected_keys = array_keys($expected); + foreach ($this->queryResult as $key => $row) { + $keys = $sorted ? array($key) : $expected_keys; + foreach ($keys as $key) { + $expected_row = $expected[$key]; + if (!array_diff_assoc($row, $expected_row) && !array_diff_assoc($expected_row, $row)) { + continue 2; + } + } + $found = FALSE; + break; + } + return $this->assertTrue($found, strtr('!expected expected, !found found', array('!expected' => print_r($expected, TRUE), '!found' => print_r($this->queryResult, TRUE)))); + } + + /** + * Asserts the results as expected regardless of order in rows. + * + * @param array $expected + * An array of the expected results. + */ + protected function assertSortedResults($expected) { + return $this->assertResults($expected, TRUE); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryRelationshipTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryRelationshipTest.php new file mode 100644 index 0000000..4639e78 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryRelationshipTest.php @@ -0,0 +1,177 @@ +installEntitySchema('taxonomy_term'); + + // We want an entity reference field. It needs a vocabulary, terms, a field + // storage and a field. First, create the vocabulary. + $vocabulary = Vocabulary::create([ + 'vid' => Unicode::strtolower($this->randomMachineName()), + ]); + $vocabulary->save(); + + // Second, create the field. + entity_test_create_bundle('test_bundle'); + $this->fieldName = strtolower($this->randomMachineName()); + $handler_settings = array( + 'target_bundles' => array( + $vocabulary->id() => $vocabulary->id(), + ), + 'auto_create' => TRUE, + ); + $this->createEntityReferenceField('entity_test', 'test_bundle', $this->fieldName, NULL, 'taxonomy_term', 'default', $handler_settings); + + // Create two terms and also two accounts. + for ($i = 0; $i <= 1; $i++) { + $term = Term::create([ + 'name' => $this->randomMachineName(), + 'vid' => $vocabulary->id(), + ]); + $term->save(); + $this->terms[] = $term; + $this->accounts[] = $this->createUser(); + } + // Create three entity_test entities, the 0th entity will point to the + // 0th account and 0th term, the 1st and 2nd entity will point to the + // 1st account and 1st term. + for ($i = 0; $i <= 2; $i++) { + $entity = EntityTest::create(array('type' => 'test_bundle')); + $entity->name->value = $this->randomMachineName(); + $index = $i ? 1 : 0; + $entity->user_id->target_id = $this->accounts[$index]->id(); + $entity->{$this->fieldName}->target_id = $this->terms[$index]->id(); + $entity->save(); + $this->entities[] = $entity; + } + $this->factory = \Drupal::service('entity.query'); + } + + /** + * Tests querying. + */ + public function testQuery() { + // This returns the 0th entity as that's only one pointing to the 0th + // account. + $this->queryResults = $this->factory->get('entity_test') + ->condition("user_id.entity.name", $this->accounts[0]->getUsername()) + ->execute(); + $this->assertResults(array(0)); + // This returns the 1st and 2nd entity as those point to the 1st account. + $this->queryResults = $this->factory->get('entity_test') + ->condition("user_id.entity.name", $this->accounts[0]->getUsername(), '<>') + ->execute(); + $this->assertResults(array(1, 2)); + // This returns all three entities because all of them point to an + // account. + $this->queryResults = $this->factory->get('entity_test') + ->exists("user_id.entity.name") + ->execute(); + $this->assertResults(array(0, 1, 2)); + // This returns no entities because all of them point to an account. + $this->queryResults = $this->factory->get('entity_test') + ->notExists("user_id.entity.name") + ->execute(); + $this->assertEqual(count($this->queryResults), 0); + // This returns the 0th entity as that's only one pointing to the 0th + // term (test without specifying the field column). + $this->queryResults = $this->factory->get('entity_test') + ->condition("$this->fieldName.entity.name", $this->terms[0]->name->value) + ->execute(); + $this->assertResults(array(0)); + // This returns the 0th entity as that's only one pointing to the 0th + // term (test with specifying the column name). + $this->queryResults = $this->factory->get('entity_test') + ->condition("$this->fieldName.target_id.entity.name", $this->terms[0]->name->value) + ->execute(); + $this->assertResults(array(0)); + // This returns the 1st and 2nd entity as those point to the 1st term. + $this->queryResults = $this->factory->get('entity_test') + ->condition("$this->fieldName.entity.name", $this->terms[0]->name->value, '<>') + ->execute(); + $this->assertResults(array(1, 2)); + } + + /** + * Assert the results. + * + * @param array $expected + * A list of indexes in the $this->entities array. + */ + protected function assertResults($expected) { + $this->assertEqual(count($this->queryResults), count($expected)); + foreach ($expected as $key) { + $id = $this->entities[$key]->id(); + $this->assertEqual($this->queryResults[$id], $id); + } + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryTest.php new file mode 100644 index 0000000..fbda4e1 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryTest.php @@ -0,0 +1,870 @@ +installEntitySchema('entity_test_mulrev'); + + $this->installConfig(array('language')); + + $figures = Unicode::strtolower($this->randomMachineName()); + $greetings = Unicode::strtolower($this->randomMachineName()); + foreach (array($figures => 'shape', $greetings => 'text') as $field_name => $field_type) { + $field_storage = FieldStorageConfig::create(array( + 'field_name' => $field_name, + 'entity_type' => 'entity_test_mulrev', + 'type' => $field_type, + 'cardinality' => 2, + )); + $field_storage->save(); + $field_storages[] = $field_storage; + } + $bundles = array(); + for ($i = 0; $i < 2; $i++) { + // For the sake of tablesort, make sure the second bundle is higher than + // the first one. Beware: MySQL is not case sensitive. + do { + $bundle = $this->randomMachineName(); + } while ($bundles && strtolower($bundles[0]) >= strtolower($bundle)); + entity_test_create_bundle($bundle); + foreach ($field_storages as $field_storage) { + FieldConfig::create([ + 'field_storage' => $field_storage, + 'bundle' => $bundle, + ])->save(); + } + $bundles[] = $bundle; + } + // Each unit is a list of field name, langcode and a column-value array. + $units[] = array($figures, 'en', array( + 'color' => 'red', + 'shape' => 'triangle', + )); + $units[] = array($figures, 'en', array( + 'color' => 'blue', + 'shape' => 'circle', + )); + // To make it easier to test sorting, the greetings get formats according + // to their langcode. + $units[] = array($greetings, 'tr', array( + 'value' => 'merhaba', + 'format' => 'format-tr' + )); + $units[] = array($greetings, 'pl', array( + 'value' => 'siema', + 'format' => 'format-pl' + )); + // Make these languages available to the greetings field. + ConfigurableLanguage::createFromLangcode('tr')->save(); + ConfigurableLanguage::createFromLangcode('pl')->save(); + // Calculate the cartesian product of the unit array by looking at the + // bits of $i and add the unit at the bits that are 1. For example, + // decimal 13 is binary 1101 so unit 3,2 and 0 will be added to the + // entity. + for ($i = 1; $i <= 15; $i++) { + $entity = EntityTestMulRev::create(array( + 'type' => $bundles[$i & 1], + 'name' => $this->randomMachineName(), + 'langcode' => 'en', + )); + // Make sure the name is set for every language that we might create. + foreach (array('tr', 'pl') as $langcode) { + $entity->addTranslation($langcode)->name = $this->randomMachineName(); + } + foreach (array_reverse(str_split(decbin($i))) as $key => $bit) { + if ($bit) { + list($field_name, $langcode, $values) = $units[$key]; + $entity->getTranslation($langcode)->{$field_name}[] = $values; + } + } + $entity->save(); + } + $this->bundles = $bundles; + $this->figures = $figures; + $this->greetings = $greetings; + $this->factory = \Drupal::service('entity.query'); + } + + /** + * Test basic functionality. + */ + function testEntityQuery() { + $greetings = $this->greetings; + $figures = $this->figures; + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->exists($greetings, 'tr') + ->condition("$figures.color", 'red') + ->sort('id') + ->execute(); + // As unit 0 was the red triangle and unit 2 was the turkish greeting, + // bit 0 and bit 2 needs to be set. + $this->assertResult(5, 7, 13, 15); + + $query = $this->factory->get('entity_test_mulrev', 'OR') + ->exists($greetings, 'tr') + ->condition("$figures.color", 'red') + ->sort('id'); + $count_query = clone $query; + $this->assertEqual(12, $count_query->count()->execute()); + $this->queryResults = $query->execute(); + // Now bit 0 (1, 3, 5, 7, 9, 11, 13, 15) or bit 2 (4, 5, 6, 7, 12, 13, 14, + // 15) needs to be set. + $this->assertResult(1, 3, 4, 5, 6, 7, 9, 11, 12, 13, 14, 15); + + // Test cloning of query conditions. + $query = $this->factory->get('entity_test_mulrev') + ->condition("$figures.color", 'red') + ->sort('id'); + $cloned_query = clone $query; + $cloned_query + ->condition("$figures.shape", 'circle'); + // Bit 0 (1, 3, 5, 7, 9, 11, 13, 15) needs to be set. + $this->queryResults = $query->execute(); + $this->assertResult(1, 3, 5, 7, 9, 11, 13, 15); + // No red color has a circle shape. + $this->queryResults = $cloned_query->execute(); + $this->assertResult(); + + $query = $this->factory->get('entity_test_mulrev'); + $group = $query->orConditionGroup() + ->exists($greetings, 'tr') + ->condition("$figures.color", 'red'); + $this->queryResults = $query + ->condition($group) + ->condition("$greetings.value", 'sie', 'STARTS_WITH') + ->sort('revision_id') + ->execute(); + // Bit 3 and (bit 0 or 2) -- the above 8 part of the above. + $this->assertResult(9, 11, 12, 13, 14, 15); + + // No figure has both the colors blue and red at the same time. + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->condition("$figures.color", 'blue') + ->condition("$figures.color", 'red') + ->sort('id') + ->execute(); + $this->assertResult(); + + // But an entity might have a red and a blue figure both. + $query = $this->factory->get('entity_test_mulrev'); + $group_blue = $query->andConditionGroup()->condition("$figures.color", 'blue'); + $group_red = $query->andConditionGroup()->condition("$figures.color", 'red'); + $this->queryResults = $query + ->condition($group_blue) + ->condition($group_red) + ->sort('revision_id') + ->execute(); + // Unit 0 and unit 1, so bits 0 1. + $this->assertResult(3, 7, 11, 15); + + // Do the same test but with IN operator. + $query = $this->factory->get('entity_test_mulrev'); + $group_blue = $query->andConditionGroup()->condition("$figures.color", array('blue'), 'IN'); + $group_red = $query->andConditionGroup()->condition("$figures.color", array('red'), 'IN'); + $this->queryResults = $query + ->condition($group_blue) + ->condition($group_red) + ->sort('id') + ->execute(); + // Unit 0 and unit 1, so bits 0 1. + $this->assertResult(3, 7, 11, 15); + + // An entity might have either red or blue figure. + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->condition("$figures.color", array('blue', 'red'), 'IN') + ->sort('id') + ->execute(); + // Bit 0 or 1 is on. + $this->assertResult(1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15); + + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->exists("$figures.color") + ->notExists("$greetings.value") + ->sort('id') + ->execute(); + // Bit 0 or 1 is on but 2 and 3 are not. + $this->assertResult(1, 2, 3); + // Now update the 'merhaba' string to xsiemax which is not a meaningful + // word but allows us to test revisions and string operations. + $ids = $this->factory->get('entity_test_mulrev') + ->condition("$greetings.value", 'merhaba') + ->sort('id') + ->execute(); + $entities = entity_load_multiple('entity_test_mulrev', $ids); + $first_entity = reset($entities); + $old_name = $first_entity->name->value; + foreach ($entities as $entity) { + $entity->setNewRevision(); + $entity->getTranslation('tr')->$greetings->value = 'xsiemax'; + $entity->name->value .= 'x'; + $entity->save(); + } + // We changed the entity names, so the current revision should not match. + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->condition('name.value', $old_name) + ->execute(); + $this->assertResult(); + // Only if all revisions are queried, we find the old revision. + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->condition('name.value', $old_name) + ->allRevisions() + ->sort('revision_id') + ->execute(); + $this->assertRevisionResult(array($first_entity->id()), array($first_entity->id())); + // When querying current revisions, this string is no longer found. + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->condition("$greetings.value", 'merhaba') + ->execute(); + $this->assertResult(); + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->condition("$greetings.value", 'merhaba') + ->allRevisions() + ->sort('revision_id') + ->execute(); + // The query only matches the original revisions. + $this->assertRevisionResult(array(4, 5, 6, 7, 12, 13, 14, 15), array(4, 5, 6, 7, 12, 13, 14, 15)); + $results = $this->factory->get('entity_test_mulrev') + ->condition("$greetings.value", 'siema', 'CONTAINS') + ->sort('id') + ->execute(); + // This matches both the original and new current revisions, multiple + // revisions are returned for some entities. + $assert = array(16 => '4', 17 => '5', 18 => '6', 19 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 20 => '12', 21 => '13', 22 => '14', 23 => '15'); + $this->assertIdentical($results, $assert); + $results = $this->factory->get('entity_test_mulrev') + ->condition("$greetings.value", 'siema', 'STARTS_WITH') + ->sort('revision_id') + ->execute(); + // Now we only get the ones that originally were siema, entity id 8 and + // above. + $this->assertIdentical($results, array_slice($assert, 4, 8, TRUE)); + $results = $this->factory->get('entity_test_mulrev') + ->condition("$greetings.value", 'a', 'ENDS_WITH') + ->sort('revision_id') + ->execute(); + // It is very important that we do not get the ones which only have + // xsiemax despite originally they were merhaba, ie. ended with a. + $this->assertIdentical($results, array_slice($assert, 4, 8, TRUE)); + $results = $this->factory->get('entity_test_mulrev') + ->condition("$greetings.value", 'a', 'ENDS_WITH') + ->allRevisions() + ->sort('id') + ->sort('revision_id') + ->execute(); + // Now we get everything. + $assert = array(4 => '4', 5 => '5', 6 => '6', 7 => '7', 8 => '8', 9 => '9', 10 => '10', 11 => '11', 12 => '12', 20 => '12', 13 => '13', 21 => '13', 14 => '14', 22 => '14', 15 => '15', 23 => '15'); + $this->assertIdentical($results, $assert); + } + + /** + * Test sort(). + * + * Warning: this is complicated. + */ + function testSort() { + $greetings = $this->greetings; + $figures = $this->figures; + // Order up and down on a number. + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->sort('id') + ->execute(); + $this->assertResult(range(1, 15)); + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->sort('id', 'DESC') + ->execute(); + $this->assertResult(range(15, 1)); + $query = $this->factory->get('entity_test_mulrev') + ->sort("$figures.color") + ->sort("$greetings.format") + ->sort('id'); + // As we do not have any conditions, here are the possible colors and + // language codes, already in order, with the first occurrence of the + // entity id marked with *: + // 8 NULL pl * + // 12 NULL pl * + + // 4 NULL tr * + // 12 NULL tr + + // 2 blue NULL * + // 3 blue NULL * + + // 10 blue pl * + // 11 blue pl * + // 14 blue pl * + // 15 blue pl * + + // 6 blue tr * + // 7 blue tr * + // 14 blue tr + // 15 blue tr + + // 1 red NULL + // 3 red NULL + + // 9 red pl * + // 11 red pl + // 13 red pl * + // 15 red pl + + // 5 red tr * + // 7 red tr + // 13 red tr + // 15 red tr + $count_query = clone $query; + $this->assertEqual(15, $count_query->count()->execute()); + $this->queryResults = $query->execute(); + $this->assertResult(8, 12, 4, 2, 3, 10, 11, 14, 15, 6, 7, 1, 9, 13, 5); + + // Test the pager by setting element #1 to page 2 with a page size of 4. + // Results will be #8-12 from above. + $request = Request::createFromGlobals(); + $request->query->replace(array( + 'page' => '0,2', + )); + \Drupal::getContainer()->get('request_stack')->push($request); + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->sort("$figures.color") + ->sort("$greetings.format") + ->sort('id') + ->pager(4, 1) + ->execute(); + $this->assertResult(15, 6, 7, 1); + + // Now test the reversed order. + $query = $this->factory->get('entity_test_mulrev') + ->sort("$figures.color", 'DESC') + ->sort("$greetings.format", 'DESC') + ->sort('id', 'DESC'); + $count_query = clone $query; + $this->assertEqual(15, $count_query->count()->execute()); + $this->queryResults = $query->execute(); + $this->assertResult(15, 13, 7, 5, 11, 9, 3, 1, 14, 6, 10, 2, 12, 4, 8); + } + + /** + * Test tablesort(). + */ + public function testTableSort() { + // While ordering on bundles do not give us a definite order, we can still + // assert that all entities from one bundle are after the other as the + // order dictates. + $request = Request::createFromGlobals(); + $request->query->replace(array( + 'sort' => 'asc', + 'order' => 'Type', + )); + \Drupal::getContainer()->get('request_stack')->push($request); + + $header = array( + 'id' => array('data' => 'Id', 'specifier' => 'id'), + 'type' => array('data' => 'Type', 'specifier' => 'type'), + ); + + $this->queryResults = array_values($this->factory->get('entity_test_mulrev') + ->tableSort($header) + ->execute()); + $this->assertBundleOrder('asc'); + + $request->query->add(array( + 'sort' => 'desc', + )); + \Drupal::getContainer()->get('request_stack')->push($request); + + $header = array( + 'id' => array('data' => 'Id', 'specifier' => 'id'), + 'type' => array('data' => 'Type', 'specifier' => 'type'), + ); + $this->queryResults = array_values($this->factory->get('entity_test_mulrev') + ->tableSort($header) + ->execute()); + $this->assertBundleOrder('desc'); + + // Ordering on ID is definite, however. + $request->query->add(array( + 'order' => 'Id', + )); + \Drupal::getContainer()->get('request_stack')->push($request); + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->tableSort($header) + ->execute(); + $this->assertResult(range(15, 1)); + } + + /** + * Test that count queries are separated across entity types. + */ + public function testCount() { + // Create a field with the same name in a different entity type. + $field_name = $this->figures; + $field_storage = FieldStorageConfig::create(array( + 'field_name' => $field_name, + 'entity_type' => 'entity_test', + 'type' => 'shape', + 'cardinality' => 2, + 'translatable' => TRUE, + )); + $field_storage->save(); + $bundle = $this->randomMachineName(); + FieldConfig::create([ + 'field_storage' => $field_storage, + 'bundle' => $bundle, + ])->save(); + + $entity = EntityTest::create(array( + 'id' => 1, + 'type' => $bundle, + )); + $entity->enforceIsNew(); + $entity->save(); + + // As the single entity of this type we just saved does not have a value + // in the color field, the result should be 0. + $count = $this->factory->get('entity_test') + ->exists("$field_name.color") + ->count() + ->execute(); + $this->assertFalse($count); + } + + /** + * Tests that nested condition groups work as expected. + */ + public function testNestedConditionGroups() { + // Query for all entities of the first bundle that have either a red + // triangle as a figure or the Turkish greeting as a greeting. + $query = $this->factory->get('entity_test_mulrev'); + + $first_and = $query->andConditionGroup() + ->condition($this->figures . '.color', 'red') + ->condition($this->figures . '.shape', 'triangle'); + $second_and = $query->andConditionGroup() + ->condition($this->greetings . '.value', 'merhaba') + ->condition($this->greetings . '.format', 'format-tr'); + + $or = $query->orConditionGroup() + ->condition($first_and) + ->condition($second_and); + + $this->queryResults = $query + ->condition($or) + ->condition('type', reset($this->bundles)) + ->sort('id') + ->execute(); + + $this->assertResult(6, 14); + } + + protected function assertResult() { + $assert = array(); + $expected = func_get_args(); + if ($expected && is_array($expected[0])) { + $expected = $expected[0]; + } + foreach ($expected as $binary) { + $assert[$binary] = strval($binary); + } + $this->assertIdentical($this->queryResults, $assert); + } + + protected function assertRevisionResult($keys, $expected) { + $assert = array(); + foreach ($expected as $key => $binary) { + $assert[$keys[$key]] = strval($binary); + } + $this->assertIdentical($this->queryResults, $assert); + return $assert; + } + + protected function assertBundleOrder($order) { + // This loop is for bundle1 entities. + for ($i = 1; $i <= 15; $i +=2) { + $ok = TRUE; + $index1 = array_search($i, $this->queryResults); + $this->assertNotIdentical($index1, FALSE, "$i found at $index1."); + // This loop is for bundle2 entities. + for ($j = 2; $j <= 15; $j += 2) { + if ($ok) { + if ($order == 'asc') { + $ok = $index1 > array_search($j, $this->queryResults); + } + else { + $ok = $index1 < array_search($j, $this->queryResults); + } + } + } + $this->assertTrue($ok, "$i is after all entities in bundle2"); + } + } + + /** + * Test adding a tag and metadata to the Entity query object. + * + * The tags and metadata should propagate to the SQL query object. + */ + public function testMetaData() { + $query = \Drupal::entityQuery('entity_test_mulrev'); + $query + ->addTag('efq_metadata_test') + ->addMetaData('foo', 'bar') + ->execute(); + + global $efq_test_metadata; + $this->assertEqual($efq_test_metadata, 'bar', 'Tag and metadata propagated to the SQL query object.'); + } + + /** + * Test case sensitive and in-sensitive query conditions. + */ + public function testCaseSensitivity() { + $bundle = $this->randomMachineName(); + + $field_storage = FieldStorageConfig::create(array( + 'field_name' => 'field_ci', + 'entity_type' => 'entity_test_mulrev', + 'type' => 'string', + 'cardinality' => 1, + 'translatable' => FALSE, + 'settings' => array( + 'case_sensitive' => FALSE, + ) + )); + $field_storage->save(); + + FieldConfig::create(array( + 'field_storage' => $field_storage, + 'bundle' => $bundle, + ))->save(); + + $field_storage = FieldStorageConfig::create(array( + 'field_name' => 'field_cs', + 'entity_type' => 'entity_test_mulrev', + 'type' => 'string', + 'cardinality' => 1, + 'translatable' => FALSE, + 'settings' => array( + 'case_sensitive' => TRUE, + ), + )); + $field_storage->save(); + + FieldConfig::create(array( + 'field_storage' => $field_storage, + 'bundle' => $bundle, + ))->save(); + + $fixtures = array(); + + for ($i = 0; $i < 2; $i++) { + // If the last 4 of the string are all numbers, then there is no + // difference between upper and lowercase and the case sensitive CONTAINS + // test will fail. Ensure that can not happen by appending a non-numeric + // character. See https://www.drupal.org/node/2397297. + $string = $this->randomMachineName(7) . 'a'; + $fixtures[] = array( + 'original' => $string, + 'uppercase' => Unicode::strtoupper($string), + 'lowercase' => Unicode::strtolower($string), + ); + } + + EntityTestMulRev::create(array( + 'type' => $bundle, + 'name' => $this->randomMachineName(), + 'langcode' => 'en', + 'field_ci' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], + 'field_cs' => $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'] + ))->save(); + + // Check the case insensitive field, = operator. + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_ci', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase'] + )->execute(); + $this->assertIdentical(count($result), 1, 'Case insensitive, lowercase'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase'] + )->execute(); + $this->assertIdentical(count($result), 1, 'Case insensitive, uppercase'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_ci', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'] + )->execute(); + $this->assertIdentical(count($result), 1, 'Case insensitive, mixed.'); + + // Check the case sensitive field, = operator. + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_cs', $fixtures[0]['lowercase'] . $fixtures[1]['lowercase'] + )->execute(); + $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['uppercase'] + )->execute(); + $this->assertIdentical(count($result), 0, 'Case sensitive, uppercase.'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_cs', $fixtures[0]['uppercase'] . $fixtures[1]['lowercase'] + )->execute(); + $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.'); + + // Check the case insensitive field, IN operator. + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_ci', array($fixtures[0]['lowercase'] . $fixtures[1]['lowercase']), 'IN' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case insensitive, lowercase'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_ci', array($fixtures[0]['uppercase'] . $fixtures[1]['uppercase']), 'IN' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case insensitive, uppercase'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_ci', array($fixtures[0]['uppercase'] . $fixtures[1]['lowercase']), 'IN' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case insensitive, mixed'); + + // Check the case sensitive field, IN operator. + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_cs', array($fixtures[0]['lowercase'] . $fixtures[1]['lowercase']), 'IN' + )->execute(); + $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_cs', array($fixtures[0]['uppercase'] . $fixtures[1]['uppercase']), 'IN' + )->execute(); + $this->assertIdentical(count($result), 0, 'Case sensitive, uppercase'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_cs', array($fixtures[0]['uppercase'] . $fixtures[1]['lowercase']), 'IN' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case sensitive, mixed'); + + // Check the case insensitive field, STARTS_WITH operator. + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_ci', $fixtures[0]['lowercase'], 'STARTS_WITH' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_ci', $fixtures[0]['uppercase'], 'STARTS_WITH' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.'); + + // Check the case sensitive field, STARTS_WITH operator. + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_cs', $fixtures[0]['lowercase'], 'STARTS_WITH' + )->execute(); + $this->assertIdentical(count($result), 0, 'Case sensitive, lowercase.'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_cs', $fixtures[0]['uppercase'], 'STARTS_WITH' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.'); + + + // Check the case insensitive field, ENDS_WITH operator. + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_ci', $fixtures[1]['lowercase'], 'ENDS_WITH' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_ci', $fixtures[1]['uppercase'], 'ENDS_WITH' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.'); + + // Check the case sensitive field, ENDS_WITH operator. + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_cs', $fixtures[1]['lowercase'], 'ENDS_WITH' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_cs', $fixtures[1]['uppercase'], 'ENDS_WITH' + )->execute(); + $this->assertIdentical(count($result), 0, 'Case sensitive, exact match.'); + + + // Check the case insensitive field, CONTAINS operator, use the inner 8 + // characters of the uppercase and lowercase strings. + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_ci', Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_ci', Unicode::strtolower(Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case sensitive, exact match.'); + + // Check the case sensitive field, CONTAINS operator. + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_cs', Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8), 'CONTAINS' + )->execute(); + $this->assertIdentical(count($result), 1, 'Case sensitive, lowercase.'); + + $result = \Drupal::entityQuery('entity_test_mulrev')->condition( + 'field_cs', Unicode::strtolower(Unicode::substr($fixtures[0]['uppercase'] . $fixtures[1]['lowercase'], 4, 8)), 'CONTAINS' + )->execute(); + $this->assertIdentical(count($result), 0, 'Case sensitive, exact match.'); + + } + + /** + * Test base fields with multiple columns. + */ + public function testBaseFieldMultipleColumns() { + $this->enableModules(['taxonomy']); + $this->installEntitySchema('taxonomy_term'); + + Vocabulary::create(['vid' => 'tags']); + + $term1 = Term::create([ + 'name' => $this->randomMachineName(), + 'vid' => 'tags', + 'description' => array( + 'value' => $this->randomString(), + 'format' => 'format1', + )]); + $term1->save(); + + $term2 = Term::create([ + 'name' => $this->randomMachineName(), + 'vid' => 'tags', + 'description' => array( + 'value' => $this->randomString(), + 'format' => 'format2', + )]); + $term2->save(); + + $ids = \Drupal::entityQuery('taxonomy_term') + ->condition('description.format', 'format1') + ->execute(); + + $this->assertEqual(count($ids), 1); + $this->assertEqual($term1->id(), reset($ids)); + } + + /** + * Test forward-revisions. + */ + public function testForwardRevisions() { + // Ensure entity 14 is returned. + $result = \Drupal::entityQuery('entity_test_mulrev') + ->condition('id', [14], 'IN') + ->execute(); + $this->assertEqual(count($result), 1); + + // Set a revision on entity 14 that isn't the current default. + $entity = EntityTestMulRev::load(14); + $current_values = $entity->{$this->figures}->getValue(); + + $entity->setNewRevision(TRUE); + $entity->isDefaultRevision(FALSE); + $entity->{$this->figures}->setValue([ + 'color' => 'red', + 'shape' => 'square' + ]); + $entity->save(); + + // Entity query should still return entity 14. + $result = \Drupal::entityQuery('entity_test_mulrev') + ->condition('id', [14], 'IN') + ->execute(); + $this->assertEqual(count($result), 1); + + // Verify that field conditions on the default and forward revision are + // work as expected. + $result = \Drupal::entityQuery('entity_test_mulrev') + ->condition('id', [14], 'IN') + ->condition("$this->figures.color", $current_values[0]['color']) + ->execute(); + $this->assertEqual($result, [14 => '14']); + $result = $this->factory->get('entity_test_mulrev') + ->condition('id', [14], 'IN') + ->condition("$this->figures.color", 'red') + ->allRevisions() + ->execute(); + $this->assertEqual($result, [16 => '14']); + } + + /** + * Test against SQL inject of condition field. This covers a + * database driver's EntityQuery\Condition class. + */ + public function testInjectionInCondition() { + try { + $this->queryResults = $this->factory->get('entity_test_mulrev') + ->condition('1 ; -- ', array(0, 1), 'IN') + ->sort('id') + ->execute(); + $this->fail('SQL Injection attempt in Entity Query condition in operator should result in an exception.'); + } + catch (\Exception $e) { + $this->pass('SQL Injection attempt in Entity Query condition in operator should result in an exception.'); + } + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityReferenceFieldTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityReferenceFieldTest.php new file mode 100644 index 0000000..e529140 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityReferenceFieldTest.php @@ -0,0 +1,459 @@ +installEntitySchema('entity_test_rev'); + + // Create a field. + $this->createEntityReferenceField( + $this->entityType, + $this->bundle, + $this->fieldName, + 'Field test', + $this->referencedEntityType, + 'default', + array('target_bundles' => array($this->bundle)), + FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED + ); + + } + + /** + * Tests reference field validation. + */ + public function testEntityReferenceFieldValidation() { + // Test a valid reference. + $referenced_entity = $this->container->get('entity_type.manager') + ->getStorage($this->referencedEntityType) + ->create(array('type' => $this->bundle)); + $referenced_entity->save(); + + $entity = $this->container->get('entity_type.manager') + ->getStorage($this->entityType) + ->create(array('type' => $this->bundle)); + $entity->{$this->fieldName}->target_id = $referenced_entity->id(); + $violations = $entity->{$this->fieldName}->validate(); + $this->assertEqual($violations->count(), 0, 'Validation passes.'); + + // Test an invalid reference. + $entity->{$this->fieldName}->target_id = 9999; + $violations = $entity->{$this->fieldName}->validate(); + $this->assertEqual($violations->count(), 1, 'Validation throws a violation.'); + $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', array('%type' => $this->referencedEntityType, '%id' => 9999))); + + // Test a non-referenceable bundle. + entity_test_create_bundle('non_referenceable', NULL, $this->referencedEntityType); + $referenced_entity = entity_create($this->referencedEntityType, array('type' => 'non_referenceable')); + $referenced_entity->save(); + $entity->{$this->fieldName}->target_id = $referenced_entity->id(); + $violations = $entity->{$this->fieldName}->validate(); + $this->assertEqual($violations->count(), 1, 'Validation throws a violation.'); + $this->assertEqual($violations[0]->getMessage(), t('This entity (%type: %id) cannot be referenced.', array('%type' => $this->referencedEntityType, '%id' => $referenced_entity->id()))); + } + + /** + * Tests the multiple target entities loader. + */ + public function testReferencedEntitiesMultipleLoad() { + // Create the parent entity. + $entity = $this->container->get('entity_type.manager') + ->getStorage($this->entityType) + ->create(array('type' => $this->bundle)); + + // Create three target entities and attach them to parent field. + $target_entities = array(); + $reference_field = array(); + for ($i = 0; $i < 3; $i++) { + $target_entity = $this->container->get('entity_type.manager') + ->getStorage($this->referencedEntityType) + ->create(array('type' => $this->bundle)); + $target_entity->save(); + $target_entities[] = $target_entity; + $reference_field[]['target_id'] = $target_entity->id(); + } + + // Also attach a non-existent entity and a NULL target id. + $reference_field[3]['target_id'] = 99999; + $target_entities[3] = NULL; + $reference_field[4]['target_id'] = NULL; + $target_entities[4] = NULL; + + // Attach the first created target entity as the sixth item ($delta == 5) of + // the parent entity field. We want to test the case when the same target + // entity is referenced twice (or more times) in the same entity reference + // field. + $reference_field[5] = $reference_field[0]; + $target_entities[5] = $target_entities[0]; + + // Create a new target entity that is not saved, thus testing the + // "autocreate" feature. + $target_entity_unsaved = $this->container->get('entity_type.manager') + ->getStorage($this->referencedEntityType) + ->create(array('type' => $this->bundle, 'name' => $this->randomString())); + $reference_field[6]['entity'] = $target_entity_unsaved; + $target_entities[6] = $target_entity_unsaved; + + // Set the field value. + $entity->{$this->fieldName}->setValue($reference_field); + + // Load the target entities using EntityReferenceField::referencedEntities(). + $entities = $entity->{$this->fieldName}->referencedEntities(); + + // Test returned entities: + // - Deltas must be preserved. + // - Non-existent entities must not be retrieved in target entities result. + foreach ($target_entities as $delta => $target_entity) { + if (!empty($target_entity)) { + if (!$target_entity->isNew()) { + // There must be an entity in the loaded set having the same id for + // the same delta. + $this->assertEqual($target_entity->id(), $entities[$delta]->id()); + } + else { + // For entities that were not yet saved, there must an entity in the + // loaded set having the same label for the same delta. + $this->assertEqual($target_entity->label(), $entities[$delta]->label()); + } + } + else { + // A non-existent or NULL entity target id must not return any item in + // the target entities set. + $this->assertFalse(isset($entities[$delta])); + } + } + } + + /** + * Tests referencing entities with string IDs. + */ + public function testReferencedEntitiesStringId() { + $field_name = 'entity_reference_string_id'; + $this->installEntitySchema('entity_test_string_id'); + $this->createEntityReferenceField( + $this->entityType, + $this->bundle, + $field_name, + 'Field test', + 'entity_test_string_id', + 'default', + array('target_bundles' => array($this->bundle)), + FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED + ); + // Create the parent entity. + $entity = $this->container->get('entity_type.manager') + ->getStorage($this->entityType) + ->create(array('type' => $this->bundle)); + + // Create the default target entity. + $target_entity = EntityTestStringId::create([ + 'id' => $this->randomString(), + 'type' => $this->bundle + ]); + $target_entity->save(); + + // Set the field value. + $entity->{$field_name}->setValue(array(array('target_id' => $target_entity->id()))); + + // Load the target entities using EntityReferenceField::referencedEntities(). + $entities = $entity->{$field_name}->referencedEntities(); + $this->assertEqual($entities[0]->id(), $target_entity->id()); + + // Test that a string ID works as a default value and the field's config + // schema is correct. + $field = FieldConfig::loadByName($this->entityType, $this->bundle, $field_name); + $field->setDefaultValue($target_entity->id()); + $field->save(); + $this->assertConfigSchema(\Drupal::service('config.typed'), 'field.field.' . $field->id(), $field->toArray()); + + // Test that the default value works. + $entity = $this->container->get('entity_type.manager') + ->getStorage($this->entityType) + ->create(array('type' => $this->bundle)); + $entities = $entity->{$field_name}->referencedEntities(); + $this->assertEqual($entities[0]->id(), $target_entity->id()); + } + + /** + * Tests all the possible ways to autocreate an entity via the API. + */ + function testAutocreateApi() { + $entity = $this->entityManager + ->getStorage($this->entityType) + ->create(array('name' => $this->randomString())); + + // Test content entity autocreation. + $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { + $entity->set('user_id', $user); + }); + $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { + $entity->set('user_id', $user, FALSE); + }); + $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { + $entity->user_id->setValue($user); + }); + $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { + $entity->user_id[0]->get('entity')->setValue($user); + }); + $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { + $entity->user_id->setValue(array('entity' => $user, 'target_id' => NULL)); + }); + try { + $message = 'Setting both the entity and an invalid target_id property fails.'; + $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { + $user->save(); + $entity->user_id->setValue(array('entity' => $user, 'target_id' => $this->generateRandomEntityId())); + }); + $this->fail($message); + } + catch (\InvalidArgumentException $e) { + $this->pass($message); + } + $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { + $entity->user_id = $user; + }); + $this->assertUserAutocreate($entity, function(EntityInterface $entity, UserInterface $user) { + $entity->user_id->entity = $user; + }); + + // Test config entity autocreation. + $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { + $entity->set('user_role', $role); + }); + $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { + $entity->set('user_role', $role, FALSE); + }); + $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { + $entity->user_role->setValue($role); + }); + $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { + $entity->user_role[0]->get('entity')->setValue($role); + }); + $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { + $entity->user_role->setValue(array('entity' => $role, 'target_id' => NULL)); + }); + try { + $message = 'Setting both the entity and an invalid target_id property fails.'; + $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { + $role->save(); + $entity->user_role->setValue(array('entity' => $role, 'target_id' => $this->generateRandomEntityId(TRUE))); + }); + $this->fail($message); + } + catch (\InvalidArgumentException $e) { + $this->pass($message); + } + $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { + $entity->user_role = $role; + }); + $this->assertUserRoleAutocreate($entity, function(EntityInterface $entity, RoleInterface $role) { + $entity->user_role->entity = $role; + }); + + // Test target entity saving after setting it as new. + $storage = $this->entityManager->getStorage('user'); + $user_id = $this->generateRandomEntityId(); + $user = $storage->create(array('uid' => $user_id, 'name' => $this->randomString())); + $entity->user_id = $user; + $user->save(); + $entity->save(); + $this->assertEqual($entity->user_id->target_id, $user->id()); + } + + /** + * Asserts that the setter callback performs autocreation for users. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The referencing entity. + * @param $setter_callback + * A callback setting the target entity on the referencing entity. + * + * @return bool + * TRUE if the user was autocreated, FALSE otherwise. + */ + protected function assertUserAutocreate(EntityInterface $entity, $setter_callback) { + $storage = $this->entityManager->getStorage('user'); + $user_id = $this->generateRandomEntityId(); + $user = $storage->create(array('uid' => $user_id, 'name' => $this->randomString())); + $setter_callback($entity, $user); + $entity->save(); + $storage->resetCache(); + $user = User::load($user_id); + return $this->assertEqual($entity->user_id->target_id, $user->id()); + } + + /** + * Asserts that the setter callback performs autocreation for user roles. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The referencing entity. + * @param $setter_callback + * A callback setting the target entity on the referencing entity. + * + * @return bool + * TRUE if the user was autocreated, FALSE otherwise. + */ + protected function assertUserRoleAutocreate(EntityInterface $entity, $setter_callback) { + $storage = $this->entityManager->getStorage('user_role'); + $role_id = $this->generateRandomEntityId(TRUE); + $role = $storage->create(array('id' => $role_id, 'label' => $this->randomString())); + $setter_callback($entity, $role); + $entity->save(); + $storage->resetCache(); + $role = Role::load($role_id); + return $this->assertEqual($entity->user_role->target_id, $role->id()); + } + + /** + * Tests that the target entity is not unnecessarily loaded. + */ + public function testTargetEntityNoLoad() { + // Setup a test entity type with an entity reference field to itself. We use + // a special storage class throwing exceptions when a load operation is + // triggered to be able to detect them. + $entity_type = clone $this->entityManager->getDefinition('entity_test_update'); + $entity_type->setHandlerClass('storage', '\Drupal\entity_test\EntityTestNoLoadStorage'); + $this->state->set('entity_test_update.entity_type', $entity_type); + $definitions = array( + 'target_reference' => BaseFieldDefinition::create('entity_reference') + ->setSetting('target_type', $entity_type->id()) + ->setSetting('handler', 'default') + ); + $this->state->set('entity_test_update.additional_base_field_definitions', $definitions); + $this->entityManager->clearCachedDefinitions(); + $this->installEntitySchema($entity_type->id()); + + // Create the target entity. + $storage = $this->entityManager->getStorage($entity_type->id()); + $target_id = $this->generateRandomEntityId(); + $target = $storage->create(array('id' => $target_id, 'name' => $this->randomString())); + $target->save(); + $this->assertEqual($target_id, $target->id(), 'The target entity has a random identifier.'); + + // Check that populating the reference with an existing target id does not + // trigger a load operation. + $message = 'The target entity was not loaded.'; + try { + $entity = $this->entityManager + ->getStorage($entity_type->id()) + ->create(array('name' => $this->randomString())); + $entity->target_reference = $target_id; + $this->pass($message); + } + catch (EntityStorageException $e) { + $this->fail($message); + } + + // Check that the storage actually triggers the expected exception when + // trying to load the target entity. + $message = 'An exception is thrown when trying to load the target entity'; + try { + $storage->load($target_id); + $this->fail($message); + } + catch (EntityStorageException $e) { + $this->pass($message); + } + } + + /** + * Tests the dependencies entity reference fields are created with. + */ + public function testEntityReferenceFieldDependencies() { + $field_name = 'user_reference_field'; + $entity_type = 'entity_test'; + + $field_storage = FieldStorageConfig::create([ + 'field_name' => $field_name, + 'type' => 'entity_reference', + 'entity_type' => $entity_type, + 'settings' => [ + 'target_type' => 'user', + ], + ]); + $field_storage->save(); + $this->assertEqual(['module' => ['entity_test', 'user']], $field_storage->getDependencies()); + + $field = FieldConfig::create([ + 'field_name' => $field_name, + 'entity_type' => $entity_type, + 'bundle' => 'entity_test', + 'label' => $field_name, + 'settings' => [ + 'handler' => 'default', + ], + ]); + $field->save(); + $this->assertEqual(['config' => ['field.storage.entity_test.user_reference_field'], 'module' => ['entity_test']], $field->getDependencies()); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityReferenceSelection/EntityReferenceSelectionSortTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityReferenceSelection/EntityReferenceSelectionSortTest.php new file mode 100644 index 0000000..f8e0ea9 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityReferenceSelection/EntityReferenceSelectionSortTest.php @@ -0,0 +1,138 @@ + 'article', + )); + $article->save(); + + // Test as a non-admin. + $normal_user = $this->createUser(array(), array('access content')); + \Drupal::currentUser()->setAccount($normal_user); + } + + /** + * Assert sorting by field and property. + */ + public function testSort() { + // Add text field to entity, to sort by. + FieldStorageConfig::create(array( + 'field_name' => 'field_text', + 'entity_type' => 'node', + 'type' => 'text', + 'entity_types' => array('node'), + ))->save(); + + FieldConfig::create([ + 'label' => 'Text Field', + 'field_name' => 'field_text', + 'entity_type' => 'node', + 'bundle' => 'article', + 'settings' => array(), + 'required' => FALSE, + ])->save(); + + // Build a set of test data. + $node_values = array( + 'published1' => array( + 'type' => 'article', + 'status' => 1, + 'title' => 'Node published1 (<&>)', + 'uid' => 1, + 'field_text' => array( + array( + 'value' => 1, + ), + ), + ), + 'published2' => array( + 'type' => 'article', + 'status' => 1, + 'title' => 'Node published2 (<&>)', + 'uid' => 1, + 'field_text' => array( + array( + 'value' => 2, + ), + ), + ), + ); + + $nodes = array(); + $node_labels = array(); + foreach ($node_values as $key => $values) { + $node = Node::create($values); + $node->save(); + $nodes[$key] = $node; + $node_labels[$key] = Html::escape($node->label()); + } + + $selection_options = array( + 'target_type' => 'node', + 'handler' => 'default', + 'handler_settings' => array( + 'target_bundles' => NULL, + // Add sorting. + 'sort' => array( + 'field' => 'field_text.value', + 'direction' => 'DESC', + ), + ), + ); + $handler = $this->container->get('plugin.manager.entity_reference_selection')->getInstance($selection_options); + + // Not only assert the result, but make sure the keys are sorted as + // expected. + $result = $handler->getReferenceableEntities(); + $expected_result = array( + $nodes['published2']->id() => $node_labels['published2'], + $nodes['published1']->id() => $node_labels['published1'], + ); + $this->assertIdentical($result['article'], $expected_result, 'Query sorted by field returned expected values.'); + + // Assert sort by base field. + $selection_options['handler_settings']['sort'] = array( + 'field' => 'nid', + 'direction' => 'ASC', + ); + $handler = $this->container->get('plugin.manager.entity_reference_selection')->getInstance($selection_options); + $result = $handler->getReferenceableEntities(); + $expected_result = array( + $nodes['published1']->id() => $node_labels['published1'], + $nodes['published2']->id() => $node_labels['published2'], + ); + $this->assertIdentical($result['article'], $expected_result, 'Query sorted by property returned expected values.'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityRevisionTranslationTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityRevisionTranslationTest.php new file mode 100644 index 0000000..e7a4055 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityRevisionTranslationTest.php @@ -0,0 +1,95 @@ +save(); + + $this->installEntitySchema('entity_test_mulrev'); + } + + /** + * Tests if the translation object has the right revision id after new revision. + */ + public function testNewRevisionAfterTranslation() { + $user = $this->createUser(); + + // Create a test entity. + $entity = EntityTestMulRev::create([ + 'name' => $this->randomString(), + 'user_id' => $user->id(), + 'language' => 'en', + ]); + $entity->save(); + $old_rev_id = $entity->getRevisionId(); + + $translation = $entity->addTranslation('de'); + $translation->setNewRevision(); + $translation->save(); + + $this->assertTrue($translation->getRevisionId() > $old_rev_id, 'The saved translation in new revision has a newer revision id.'); + $this->assertTrue($this->reloadEntity($entity)->getRevisionId() > $old_rev_id, 'The entity from the storage has a newer revision id.'); + } + + /** + * Tests if the translation object has the right revision id after new revision. + */ + public function testRevertRevisionAfterTranslation() { + $user = $this->createUser(); + $storage = $this->entityManager->getStorage('entity_test_mulrev'); + + // Create a test entity. + $entity = EntityTestMulRev::create([ + 'name' => $this->randomString(), + 'user_id' => $user->id(), + 'language' => 'en', + ]); + $entity->save(); + $old_rev_id = $entity->getRevisionId(); + + $translation = $entity->addTranslation('de'); + $translation->setNewRevision(); + $translation->save(); + + $entity = $this->reloadEntity($entity); + + $this->assertTrue($entity->hasTranslation('de')); + + $entity = $storage->loadRevision($old_rev_id); + + $entity->setNewRevision(); + $entity->isDefaultRevision(TRUE); + $entity->save(); + + $entity = $this->reloadEntity($entity); + + $this->assertFalse($entity->hasTranslation('de')); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntitySchemaTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntitySchemaTest.php new file mode 100644 index 0000000..5567780 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntitySchemaTest.php @@ -0,0 +1,196 @@ +installSchema('user', array('users_data')); + $this->database = $this->container->get('database'); + } + + /** + * Tests the custom bundle field creation and deletion. + */ + public function testCustomFieldCreateDelete() { + // Install the module which adds the field. + $this->installModule('entity_schema_test'); + $this->entityManager->clearCachedDefinitions(); + $storage_definitions = $this->entityManager->getFieldStorageDefinitions('entity_test'); + $this->assertNotNull($storage_definitions['custom_base_field'], 'Base field definition found.'); + $this->assertNotNull($storage_definitions['custom_bundle_field'], 'Bundle field definition found.'); + + // Make sure the field schema can be created. + $this->entityManager->onFieldStorageDefinitionCreate($storage_definitions['custom_base_field']); + $this->entityManager->onFieldStorageDefinitionCreate($storage_definitions['custom_bundle_field']); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ + $table_mapping = $this->entityManager->getStorage('entity_test')->getTableMapping(); + $base_table = current($table_mapping->getTableNames()); + $base_column = current($table_mapping->getColumnNames('custom_base_field')); + $this->assertTrue($this->database->schema()->fieldExists($base_table, $base_column), 'Table column created'); + $table = $table_mapping->getDedicatedDataTableName($storage_definitions['custom_bundle_field']); + $this->assertTrue($this->database->schema()->tableExists($table), 'Table created'); + + // Make sure the field schema can be deleted. + $this->entityManager->onFieldStorageDefinitionDelete($storage_definitions['custom_base_field']); + $this->entityManager->onFieldStorageDefinitionDelete($storage_definitions['custom_bundle_field']); + $this->assertFalse($this->database->schema()->fieldExists($base_table, $base_column), 'Table column dropped'); + $this->assertFalse($this->database->schema()->tableExists($table), 'Table dropped'); + } + + /** + * Updates the entity type definition. + * + * @param bool $alter + * Whether the original definition should be altered or not. + */ + protected function updateEntityType($alter) { + $entity_test_id = 'entity_test'; + $original = $this->entityManager->getDefinition($entity_test_id); + $this->entityManager->clearCachedDefinitions(); + $this->state->set('entity_schema_update', $alter); + $entity_type = $this->entityManager->getDefinition($entity_test_id); + $this->entityManager->onEntityTypeUpdate($entity_type, $original); + } + + /** + * Tests that entity schema responds to changes in the entity type definition. + */ + public function testEntitySchemaUpdate() { + $this->installModule('entity_schema_test'); + $storage_definitions = $this->entityManager->getFieldStorageDefinitions('entity_test'); + $this->entityManager->onFieldStorageDefinitionCreate($storage_definitions['custom_base_field']); + $this->entityManager->onFieldStorageDefinitionCreate($storage_definitions['custom_bundle_field']); + $schema_handler = $this->database->schema(); + $tables = array('entity_test', 'entity_test_revision', 'entity_test_field_data', 'entity_test_field_revision'); + $dedicated_tables = array('entity_test__custom_bundle_field', 'entity_test_revision__custom_bundle_field'); + + // Initially only the base table and the dedicated field data table should + // exist. + foreach ($tables as $index => $table) { + $this->assertEqual($schema_handler->tableExists($table), !$index, SafeMarkup::format('Entity schema correct for the @table table.', array('@table' => $table))); + } + $this->assertTrue($schema_handler->tableExists($dedicated_tables[0]), SafeMarkup::format('Field schema correct for the @table table.', array('@table' => $table))); + + // Update the entity type definition and check that the entity schema now + // supports translations and revisions. + $this->updateEntityType(TRUE); + foreach ($tables as $table) { + $this->assertTrue($schema_handler->tableExists($table), SafeMarkup::format('Entity schema correct for the @table table.', array('@table' => $table))); + } + foreach ($dedicated_tables as $table) { + $this->assertTrue($schema_handler->tableExists($table), SafeMarkup::format('Field schema correct for the @table table.', array('@table' => $table))); + } + + // Revert changes and check that the entity schema now does not support + // neither translations nor revisions. + $this->updateEntityType(FALSE); + foreach ($tables as $index => $table) { + $this->assertEqual($schema_handler->tableExists($table), !$index, SafeMarkup::format('Entity schema correct for the @table table.', array('@table' => $table))); + } + $this->assertTrue($schema_handler->tableExists($dedicated_tables[0]), SafeMarkup::format('Field schema correct for the @table table.', array('@table' => $table))); + } + + /** + * {@inheritdoc} + */ + protected function refreshServices() { + parent::refreshServices(); + $this->database = $this->container->get('database'); + } + + /** + * Tests that modifying the UUID field for a translatable entity works. + */ + public function testModifyingTranslatableColumnSchema() { + $this->installModule('entity_schema_test'); + $this->updateEntityType(TRUE); + $fields = ['revision_log', 'uuid']; + foreach ($fields as $field_name) { + $original_definition = $this->entityManager->getBaseFieldDefinitions('entity_test')[$field_name]; + $new_definition = clone $original_definition; + $new_definition->setLabel($original_definition->getLabel() . ', the other one'); + $this->assertTrue($this->entityManager->getStorage('entity_test') + ->requiresFieldDataMigration($new_definition, $original_definition)); + } + } + + /** + * Tests fields from an uninstalled module are removed from the schema. + */ + public function testCleanUpStorageDefinition() { + // Find all the entity types provided by the entity_test module and install + // the schema for them. + $entity_type_ids = []; + $entities = \Drupal::entityManager()->getDefinitions(); + foreach ($entities as $entity_type_id => $definition) { + if ($definition->getProvider() == 'entity_test') { + $this->installEntitySchema($entity_type_id); + $entity_type_ids[] = $entity_type_id; + }; + } + + // Get a list of all the entities in the schema. + $key_value_store = \Drupal::keyValue('entity.storage_schema.sql'); + $schema = $key_value_store->getAll(); + + // Count the storage definitions provided by the entity_test module, so that + // after uninstall we can be sure there were some to be deleted. + $entity_type_id_count = 0; + + foreach (array_keys($schema) as $storage_definition_name) { + list($entity_type_id, ,) = explode('.', $storage_definition_name); + if (in_array($entity_type_id, $entity_type_ids)) { + $entity_type_id_count++; + } + } + + // Ensure that there are storage definitions from the entity_test module. + $this->assertNotEqual($entity_type_id_count, 0, 'There are storage definitions provided by the entity_test module in the schema.'); + + // Uninstall the entity_test module. + $this->container->get('module_installer')->uninstall(array('entity_test')); + + // Get a list of all the entities in the schema. + $key_value_store = \Drupal::keyValue('entity.storage_schema.sql'); + $schema = $key_value_store->getAll(); + + // Count the storage definitions that come from entity types provided by + // the entity_test module. + $entity_type_id_count = 0; + + foreach (array_keys($schema) as $storage_definition_name) { + list($entity_type_id, ,) = explode('.', $storage_definition_name); + if (in_array($entity_type_id, $entity_type_ids)) { + $entity_type_id_count++; + } + } + + // Ensure that all storage definitions have been removed from the schema. + $this->assertEqual($entity_type_id_count, 0, 'After uninstalling entity_test module the schema should not contains fields from entities provided by the module.'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityTranslationTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityTranslationTest.php new file mode 100644 index 0000000..5c1ac27 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityTranslationTest.php @@ -0,0 +1,937 @@ +doTestEntityLanguageMethods($entity_type); + } + } + + /** + * Executes the entity language method tests for the given entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function doTestEntityLanguageMethods($entity_type) { + $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + 'name' => 'test', + 'user_id' => $this->container->get('current_user')->id(), + )); + $this->assertEqual($entity->language()->getId(), $this->languageManager->getDefaultLanguage()->getId(), format_string('%entity_type: Entity created with API has default language.', array('%entity_type' => $entity_type))); + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + 'name' => 'test', + 'user_id' => \Drupal::currentUser()->id(), + $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED, + )); + + $this->assertEqual($entity->language()->getId(), LanguageInterface::LANGCODE_NOT_SPECIFIED, format_string('%entity_type: Entity language not specified.', array('%entity_type' => $entity_type))); + $this->assertFalse($entity->getTranslationLanguages(FALSE), format_string('%entity_type: No translations are available', array('%entity_type' => $entity_type))); + + // Set the value in default language. + $entity->set($this->fieldName, array(0 => array('value' => 'default value'))); + // Get the value. + $field = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT)->get($this->fieldName); + $this->assertEqual($field->value, 'default value', format_string('%entity_type: Untranslated value retrieved.', array('%entity_type' => $entity_type))); + $this->assertEqual($field->getLangcode(), LanguageInterface::LANGCODE_NOT_SPECIFIED, format_string('%entity_type: Field object has the expected langcode.', array('%entity_type' => $entity_type))); + + // Try to get add a translation to language neutral entity. + $message = 'Adding a translation to a language-neutral entity results in an error.'; + try { + $entity->addTranslation($this->langcodes[1]); + $this->fail($message); + } + catch (\InvalidArgumentException $e) { + $this->pass($message); + } + + // Now, make the entity language-specific by assigning a language and test + // translating it. + $default_langcode = $this->langcodes[0]; + $entity->{$langcode_key}->value = $default_langcode; + $entity->{$this->fieldName} = array(); + $this->assertEqual($entity->language(), \Drupal::languageManager()->getLanguage($this->langcodes[0]), format_string('%entity_type: Entity language retrieved.', array('%entity_type' => $entity_type))); + $this->assertFalse($entity->getTranslationLanguages(FALSE), format_string('%entity_type: No translations are available', array('%entity_type' => $entity_type))); + + // Set the value in default language. + $entity->set($this->fieldName, array(0 => array('value' => 'default value'))); + // Get the value. + $field = $entity->get($this->fieldName); + $this->assertEqual($field->value, 'default value', format_string('%entity_type: Untranslated value retrieved.', array('%entity_type' => $entity_type))); + $this->assertEqual($field->getLangcode(), $default_langcode, format_string('%entity_type: Field object has the expected langcode.', array('%entity_type' => $entity_type))); + + // Set a translation. + $entity->addTranslation($this->langcodes[1])->set($this->fieldName, array(0 => array('value' => 'translation 1'))); + $field = $entity->getTranslation($this->langcodes[1])->{$this->fieldName}; + $this->assertEqual($field->value, 'translation 1', format_string('%entity_type: Translated value set.', array('%entity_type' => $entity_type))); + $this->assertEqual($field->getLangcode(), $this->langcodes[1], format_string('%entity_type: Field object has the expected langcode.', array('%entity_type' => $entity_type))); + + // Make sure the untranslated value stays. + $field = $entity->get($this->fieldName); + $this->assertEqual($field->value, 'default value', 'Untranslated value stays.'); + $this->assertEqual($field->getLangcode(), $default_langcode, 'Untranslated value has the expected langcode.'); + + $translations[$this->langcodes[1]] = \Drupal::languageManager()->getLanguage($this->langcodes[1]); + $this->assertEqual($entity->getTranslationLanguages(FALSE), $translations, 'Translations retrieved.'); + + // Try to get a value using a language code for a non-existing translation. + $message = 'Getting a non existing translation results in an error.'; + try { + $entity->getTranslation($this->langcodes[2])->get($this->fieldName)->value; + $this->fail($message); + } + catch (\InvalidArgumentException $e) { + $this->pass($message); + } + + // Try to get a not available translation. + $this->assertNull($entity->addTranslation($this->langcodes[2])->get($this->fieldName)->value, format_string('%entity_type: A translation that is not available is NULL.', array('%entity_type' => $entity_type))); + + // Try to get a value using an invalid language code. + $message = 'Getting an invalid translation results in an error.'; + try { + $entity->getTranslation('invalid')->get($this->fieldName)->value; + $this->fail($message); + } + catch (\InvalidArgumentException $e) { + $this->pass($message); + } + + // Try to set a value using an invalid language code. + try { + $entity->getTranslation('invalid')->set($this->fieldName, NULL); + $this->fail(format_string('%entity_type: Setting a translation for an invalid language throws an exception.', array('%entity_type' => $entity_type))); + } + catch (\InvalidArgumentException $e) { + $this->pass(format_string('%entity_type: Setting a translation for an invalid language throws an exception.', array('%entity_type' => $entity_type))); + } + + // Set the value in default language. + $field_name = 'field_test_text'; + $entity->getTranslation($this->langcodes[1])->set($field_name, array(0 => array('value' => 'default value2'))); + // Get the value. + $field = $entity->get($field_name); + $this->assertEqual($field->value, 'default value2', format_string('%entity_type: Untranslated value set into a translation in non-strict mode.', array('%entity_type' => $entity_type))); + $this->assertEqual($field->getLangcode(), $default_langcode, format_string('%entity_type: Field object has the expected langcode.', array('%entity_type' => $entity_type))); + } + + /** + * Tests multilingual properties. + */ + public function testMultilingualProperties() { + // Test all entity variations with data table support. + foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) { + $this->doTestMultilingualProperties($entity_type); + } + } + + /** + * Executes the multilingual property tests for the given entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function doTestMultilingualProperties($entity_type) { + $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); + $default_langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('default_langcode'); + $name = $this->randomMachineName(); + $uid = mt_rand(0, 127); + $langcode = $this->langcodes[0]; + + // Create a language neutral entity and check that properties are stored + // as language neutral. + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array('name' => $name, 'user_id' => $uid, $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED)); + $entity->save(); + $entity = entity_load($entity_type, $entity->id()); + $default_langcode = $entity->language()->getId(); + $this->assertEqual($default_langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED, format_string('%entity_type: Entity created as language neutral.', array('%entity_type' => $entity_type))); + $field = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT)->get('name'); + $this->assertEqual($name, $field->value, format_string('%entity_type: The entity name has been correctly stored as language neutral.', array('%entity_type' => $entity_type))); + $this->assertEqual($default_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expect langcode.', array('%entity_type' => $entity_type))); + $this->assertEqual($uid, $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT)->get('user_id')->target_id, format_string('%entity_type: The entity author has been correctly stored as language neutral.', array('%entity_type' => $entity_type))); + + $translation = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT); + $field = $translation->get('name'); + $this->assertEqual($name, $field->value, format_string('%entity_type: The entity name defaults to neutral language.', array('%entity_type' => $entity_type))); + $this->assertEqual($default_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expect langcode.', array('%entity_type' => $entity_type))); + $this->assertEqual($uid, $translation->get('user_id')->target_id, format_string('%entity_type: The entity author defaults to neutral language.', array('%entity_type' => $entity_type))); + $field = $entity->get('name'); + $this->assertEqual($name, $field->value, format_string('%entity_type: The entity name can be retrieved without specifying a language.', array('%entity_type' => $entity_type))); + $this->assertEqual($default_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expect langcode.', array('%entity_type' => $entity_type))); + $this->assertEqual($uid, $entity->get('user_id')->target_id, format_string('%entity_type: The entity author can be retrieved without specifying a language.', array('%entity_type' => $entity_type))); + + // Create a language-aware entity and check that properties are stored + // as language-aware. + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array('name' => $name, 'user_id' => $uid, $langcode_key => $langcode)); + $entity->save(); + $entity = entity_load($entity_type, $entity->id()); + $default_langcode = $entity->language()->getId(); + $this->assertEqual($default_langcode, $langcode, format_string('%entity_type: Entity created as language specific.', array('%entity_type' => $entity_type))); + $field = $entity->getTranslation($langcode)->get('name'); + $this->assertEqual($name, $field->value, format_string('%entity_type: The entity name has been correctly stored as a language-aware property.', array('%entity_type' => $entity_type))); + $this->assertEqual($default_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expect langcode.', array('%entity_type' => $entity_type))); + $this->assertEqual($uid, $entity->getTranslation($langcode)->get('user_id')->target_id, format_string('%entity_type: The entity author has been correctly stored as a language-aware property.', array('%entity_type' => $entity_type))); + + // Create property translations. + $properties = array(); + $default_langcode = $langcode; + foreach ($this->langcodes as $langcode) { + if ($langcode != $default_langcode) { + $properties[$langcode] = array( + 'name' => array(0 => $this->randomMachineName()), + 'user_id' => array(0 => mt_rand(128, 256)), + ); + } + else { + $properties[$langcode] = array( + 'name' => array(0 => $name), + 'user_id' => array(0 => $uid), + ); + } + $translation = $entity->hasTranslation($langcode) ? $entity->getTranslation($langcode) : $entity->addTranslation($langcode); + foreach ($properties[$langcode] as $field_name => $values) { + $translation->set($field_name, $values); + } + } + $entity->save(); + + // Check that property translation were correctly stored. + $entity = entity_load($entity_type, $entity->id()); + foreach ($this->langcodes as $langcode) { + $args = array( + '%entity_type' => $entity_type, + '%langcode' => $langcode, + ); + $field = $entity->getTranslation($langcode)->get('name'); + $this->assertEqual($properties[$langcode]['name'][0], $field->value, format_string('%entity_type: The entity name has been correctly stored for language %langcode.', $args)); + $field_langcode = ($langcode == $entity->language()->getId()) ? $default_langcode : $langcode; + $this->assertEqual($field_langcode, $field->getLangcode(), format_string('%entity_type: The field object has the expected langcode %langcode.', $args)); + $this->assertEqual($properties[$langcode]['user_id'][0], $entity->getTranslation($langcode)->get('user_id')->target_id, format_string('%entity_type: The entity author has been correctly stored for language %langcode.', $args)); + } + + // Test query conditions (cache is reset at each call). + $translated_id = $entity->id(); + // Create an additional entity with only the uid set. The uid for the + // original language is the same of one used for a translation. + $langcode = $this->langcodes[1]; + $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + 'user_id' => $properties[$langcode]['user_id'], + 'name' => 'some name', + $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED, + )) + ->save(); + + $entities = entity_load_multiple($entity_type); + $this->assertEqual(count($entities), 3, format_string('%entity_type: Three entities were created.', array('%entity_type' => $entity_type))); + $entities = entity_load_multiple($entity_type, array($translated_id)); + $this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by id.', array('%entity_type' => $entity_type))); + $entities = entity_load_multiple_by_properties($entity_type, array('name' => $name)); + $this->assertEqual(count($entities), 2, format_string('%entity_type: Two entities correctly loaded by name.', array('%entity_type' => $entity_type))); + // @todo The default language condition should go away in favor of an + // explicit parameter. + $entities = entity_load_multiple_by_properties($entity_type, array('name' => $properties[$langcode]['name'][0], $default_langcode_key => 0)); + $this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by name translation.', array('%entity_type' => $entity_type))); + $entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $default_langcode, 'name' => $name)); + $this->assertEqual(count($entities), 1, format_string('%entity_type: One entity correctly loaded by name and language.', array('%entity_type' => $entity_type))); + + $entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0])); + $this->assertEqual(count($entities), 0, format_string('%entity_type: No entity loaded by name translation specifying the translation language.', array('%entity_type' => $entity_type))); + $entities = entity_load_multiple_by_properties($entity_type, array($langcode_key => $langcode, 'name' => $properties[$langcode]['name'][0], $default_langcode_key => 0)); + $this->assertEqual(count($entities), 1, format_string('%entity_type: One entity loaded by name translation and language specifying to look for translations.', array('%entity_type' => $entity_type))); + $entities = entity_load_multiple_by_properties($entity_type, array('user_id' => $properties[$langcode]['user_id'][0], $default_langcode_key => NULL)); + $this->assertEqual(count($entities), 2, format_string('%entity_type: Two entities loaded by uid without caring about property translatability.', array('%entity_type' => $entity_type))); + + // Test property conditions and orders with multiple languages in the same + // query. + $query = \Drupal::entityQuery($entity_type); + $group = $query->andConditionGroup() + ->condition('user_id', $properties[$default_langcode]['user_id'][0], '=', $default_langcode) + ->condition('name', $properties[$default_langcode]['name'][0], '=', $default_langcode); + $result = $query + ->condition($group) + ->condition('name', $properties[$langcode]['name'][0], '=', $langcode) + ->execute(); + $this->assertEqual(count($result), 1, format_string('%entity_type: One entity loaded by name and uid using different language meta conditions.', array('%entity_type' => $entity_type))); + + // Test mixed property and field conditions. + $entity = entity_load($entity_type, reset($result), TRUE); + $field_value = $this->randomString(); + $entity->getTranslation($langcode)->set($this->fieldName, array(array('value' => $field_value))); + $entity->save(); + $query = \Drupal::entityQuery($entity_type); + $default_langcode_group = $query->andConditionGroup() + ->condition('user_id', $properties[$default_langcode]['user_id'][0], '=', $default_langcode) + ->condition('name', $properties[$default_langcode]['name'][0], '=', $default_langcode); + $langcode_group = $query->andConditionGroup() + ->condition('name', $properties[$langcode]['name'][0], '=', $langcode) + ->condition("$this->fieldName.value", $field_value, '=', $langcode); + $result = $query + ->condition($langcode_key, $default_langcode) + ->condition($default_langcode_group) + ->condition($langcode_group) + ->execute(); + $this->assertEqual(count($result), 1, format_string('%entity_type: One entity loaded by name, uid and field value using different language meta conditions.', array('%entity_type' => $entity_type))); + } + + /** + * Tests the Entity Translation API behavior. + */ + function testEntityTranslationAPI() { + // Test all entity variations with data table support. + foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) { + $this->doTestEntityTranslationAPI($entity_type); + } + } + + /** + * Executes the Entity Translation API tests for the given entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function doTestEntityTranslationAPI($entity_type) { + $default_langcode = $this->langcodes[0]; + $langcode = $this->langcodes[1]; + $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); + $default_langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('default_langcode'); + + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ + $entity = $this->entityManager + ->getStorage($entity_type) + ->create(array('name' => $this->randomMachineName(), $langcode_key => LanguageInterface::LANGCODE_NOT_SPECIFIED)); + + $entity->save(); + $hooks = $this->getHooksInfo(); + $this->assertFalse($hooks, 'No entity translation hooks are fired when creating an entity.'); + + // Verify that we obtain the entity object itself when we attempt to + // retrieve a translation referring to it. + $translation = $entity->getTranslation(LanguageInterface::LANGCODE_NOT_SPECIFIED); + $this->assertFalse($translation->isNewTranslation(), 'Existing translations are not marked as new.'); + $this->assertIdentical($entity, $translation, 'The translation object corresponding to a non-default language is the entity object itself when the entity is language-neutral.'); + $entity->{$langcode_key}->value = $default_langcode; + $translation = $entity->getTranslation($default_langcode); + $this->assertIdentical($entity, $translation, 'The translation object corresponding to the default language (explicit) is the entity object itself.'); + $translation = $entity->getTranslation(LanguageInterface::LANGCODE_DEFAULT); + $this->assertIdentical($entity, $translation, 'The translation object corresponding to the default language (implicit) is the entity object itself.'); + $this->assertTrue($entity->{$default_langcode_key}->value, 'The translation object is the default one.'); + + // Verify that trying to retrieve a translation for a locked language when + // the entity is language-aware causes an exception to be thrown. + $message = 'A language-neutral translation cannot be retrieved.'; + try { + $entity->getTranslation(LanguageInterface::LANGCODE_NOT_SPECIFIED); + $this->fail($message); + } + catch (\LogicException $e) { + $this->pass($message); + } + + // Create a translation and verify that the translation object and the + // original object behave independently. + $name = $default_langcode . '_' . $this->randomMachineName(); + $entity->name->value = $name; + $name_translated = $langcode . '_' . $this->randomMachineName(); + $translation = $entity->addTranslation($langcode); + $this->assertTrue($translation->isNewTranslation(), 'Newly added translations are marked as new.'); + $this->assertNotIdentical($entity, $translation, 'The entity and the translation object differ from one another.'); + $this->assertTrue($entity->hasTranslation($langcode), 'The new translation exists.'); + $this->assertEqual($translation->language()->getId(), $langcode, 'The translation language matches the specified one.'); + $this->assertEqual($translation->{$langcode_key}->value, $langcode, 'The translation field language value matches the specified one.'); + $this->assertFalse($translation->{$default_langcode_key}->value, 'The translation object is not the default one.'); + $this->assertEqual($translation->getUntranslated()->language()->getId(), $default_langcode, 'The original language can still be retrieved.'); + $translation->name->value = $name_translated; + $this->assertEqual($entity->name->value, $name, 'The original name is retained after setting a translated value.'); + $entity->name->value = $name; + $this->assertEqual($translation->name->value, $name_translated, 'The translated name is retained after setting the original value.'); + + // Save the translation and check that the expected hooks are fired. + $translation->save(); + $hooks = $this->getHooksInfo(); + + $this->assertEqual($hooks['entity_translation_create'], $langcode, 'The generic entity translation creation hook has fired.'); + $this->assertEqual($hooks[$entity_type . '_translation_create'], $langcode, 'The entity-type-specific entity translation creation hook has fired.'); + + $this->assertEqual($hooks['entity_translation_insert'], $langcode, 'The generic entity translation insertion hook has fired.'); + $this->assertEqual($hooks[$entity_type . '_translation_insert'], $langcode, 'The entity-type-specific entity translation insertion hook has fired.'); + + // Verify that changing translation language causes an exception to be + // thrown. + $message = 'The translation language cannot be changed.'; + try { + $translation->{$langcode_key}->value = $this->langcodes[2]; + $this->fail($message); + } + catch (\LogicException $e) { + $this->pass($message); + } + + // Verify that reassigning the same translation language is allowed. + $message = 'The translation language can be reassigned the same value.'; + try { + $translation->{$langcode_key}->value = $langcode; + $this->pass($message); + } + catch (\LogicException $e) { + $this->fail($message); + } + + // Verify that changing the default translation flag causes an exception to + // be thrown. + foreach ($entity->getTranslationLanguages() as $t_langcode => $language) { + $translation = $entity->getTranslation($t_langcode); + $default = $translation->isDefaultTranslation(); + + $message = 'The default translation flag can be reassigned the same value.'; + try { + $translation->{$default_langcode_key}->value = $default; + $this->pass($message); + } + catch (\LogicException $e) { + $this->fail($message); + } + + $message = 'The default translation flag cannot be changed.'; + try { + $translation->{$default_langcode_key}->value = !$default; + $this->fail($message); + } + catch (\LogicException $e) { + $this->pass($message); + } + + $this->assertEqual($translation->{$default_langcode_key}->value, $default); + } + + // Check that after loading an entity the language is the default one. + $entity = $this->reloadEntity($entity); + $this->assertEqual($entity->language()->getId(), $default_langcode, 'The loaded entity is the original one.'); + + // Add another translation and check that everything works as expected. A + // new translation object can be obtained also by just specifying a valid + // language. + $langcode2 = $this->langcodes[2]; + $translation = $entity->addTranslation($langcode2); + $value = $entity !== $translation && $translation->language()->getId() == $langcode2 && $entity->hasTranslation($langcode2); + $this->assertTrue($value, 'A new translation object can be obtained also by specifying a valid language.'); + $this->assertEqual($entity->language()->getId(), $default_langcode, 'The original language has been preserved.'); + $translation->save(); + $hooks = $this->getHooksInfo(); + + $this->assertEqual($hooks['entity_translation_create'], $langcode2, 'The generic entity translation creation hook has fired.'); + $this->assertEqual($hooks[$entity_type . '_translation_create'], $langcode2, 'The entity-type-specific entity translation creation hook has fired.'); + + $this->assertEqual($hooks['entity_translation_insert'], $langcode2, 'The generic entity translation insertion hook has fired.'); + $this->assertEqual($hooks[$entity_type . '_translation_insert'], $langcode2, 'The entity-type-specific entity translation insertion hook has fired.'); + + // Verify that trying to manipulate a translation object referring to a + // removed translation results in exceptions being thrown. + $entity = $this->reloadEntity($entity); + $translation = $entity->getTranslation($langcode2); + $entity->removeTranslation($langcode2); + foreach (array('get', 'set', '__get', '__set', 'createDuplicate') as $method) { + $message = format_string('The @method method raises an exception when trying to manipulate a removed translation.', array('@method' => $method)); + try { + $translation->{$method}('name', $this->randomMachineName()); + $this->fail($message); + } + catch (\Exception $e) { + $this->pass($message); + } + } + + // Verify that deletion hooks are fired when saving an entity with a removed + // translation. + $entity->save(); + $hooks = $this->getHooksInfo(); + $this->assertEqual($hooks['entity_translation_delete'], $langcode2, 'The generic entity translation deletion hook has fired.'); + $this->assertEqual($hooks[$entity_type . '_translation_delete'], $langcode2, 'The entity-type-specific entity translation deletion hook has fired.'); + $entity = $this->reloadEntity($entity); + $this->assertFalse($entity->hasTranslation($langcode2), 'The translation does not appear among available translations after saving the entity.'); + + // Check that removing an invalid translation causes an exception to be + // thrown. + foreach (array($default_langcode, LanguageInterface::LANGCODE_DEFAULT, $this->randomMachineName()) as $invalid_langcode) { + $message = format_string('Removing an invalid translation (@langcode) causes an exception to be thrown.', array('@langcode' => $invalid_langcode)); + try { + $entity->removeTranslation($invalid_langcode); + $this->fail($message); + } + catch (\Exception $e) { + $this->pass($message); + } + } + + // Check that hooks are fired only when actually storing data. + $entity = $this->reloadEntity($entity); + $entity->addTranslation($langcode2); + $entity->removeTranslation($langcode2); + $entity->save(); + $hooks = $this->getHooksInfo(); + + $this->assertTrue(isset($hooks['entity_translation_create']), 'The generic entity translation creation hook is run when adding and removing a translation without storing it.'); + unset($hooks['entity_translation_create']); + $this->assertTrue(isset($hooks[$entity_type . '_translation_create']), 'The entity-type-specific entity translation creation hook is run when adding and removing a translation without storing it.'); + unset($hooks[$entity_type . '_translation_create']); + + $this->assertFalse($hooks, 'No other hooks beyond the entity translation creation hooks are run when adding and removing a translation without storing it.'); + + // Check that hooks are fired only when actually storing data. + $entity = $this->reloadEntity($entity); + $entity->addTranslation($langcode2); + $entity->save(); + $entity = $this->reloadEntity($entity); + $this->assertTrue($entity->hasTranslation($langcode2), 'Entity has translation after adding one and saving.'); + $entity->removeTranslation($langcode2); + $entity->save(); + $entity = $this->reloadEntity($entity); + $this->assertFalse($entity->hasTranslation($langcode2), 'Entity does not have translation after removing it and saving.'); + // Reset hook firing information. + $this->getHooksInfo(); + + // Verify that entity serialization does not cause stale references to be + // left around. + $entity = $this->reloadEntity($entity); + $translation = $entity->getTranslation($langcode); + $entity = unserialize(serialize($entity)); + $entity->name->value = $this->randomMachineName(); + $name = $default_langcode . '_' . $this->randomMachineName(); + $entity->getTranslation($default_langcode)->name->value = $name; + $this->assertEqual($entity->name->value, $name, 'No stale reference for the translation object corresponding to the original language.'); + $translation2 = $entity->getTranslation($langcode); + $translation2->name->value .= $this->randomMachineName(); + $this->assertNotEqual($translation->name->value, $translation2->name->value, 'No stale reference for the actual translation object.'); + $this->assertEqual($entity, $translation2->getUntranslated(), 'No stale reference in the actual translation object.'); + + // Verify that deep-cloning is still available when we are not instantiating + // a translation object, which instead relies on shallow cloning. + $entity = $this->reloadEntity($entity); + $entity->getTranslation($langcode); + $cloned = clone $entity; + $translation = $cloned->getTranslation($langcode); + $this->assertNotIdentical($entity, $translation->getUntranslated(), 'A cloned entity object has no reference to the original one.'); + $entity->removeTranslation($langcode); + $this->assertFalse($entity->hasTranslation($langcode)); + $this->assertTrue($cloned->hasTranslation($langcode)); + + // Check that untranslatable field references keep working after serializing + // and cloning the entity. + $entity = $this->reloadEntity($entity); + $type = $this->randomMachineName(); + $entity->getTranslation($langcode)->type->value = $type; + $entity = unserialize(serialize($entity)); + $cloned = clone $entity; + $translation = $cloned->getTranslation($langcode); + $translation->type->value = strrev($type); + $this->assertEqual($cloned->type->value, $translation->type->value, 'Untranslatable field references keep working after serializing and cloning the entity.'); + + // Check that per-language defaults are properly populated. The + // 'entity_test_mul_default_value' entity type is translatable and uses + // entity_test_field_default_value() as a "default value callback" for its + // 'description' field. + $entity = $this->entityManager + ->getStorage('entity_test_mul_default_value') + ->create(['name' => $this->randomMachineName(), 'langcode' => $langcode]); + $translation = $entity->addTranslation($langcode2); + $expected = array( + array( + 'shape' => "shape:0:description_$langcode2", + 'color' => "color:0:description_$langcode2", + ), + array( + 'shape' => "shape:1:description_$langcode2", + 'color' => "color:1:description_$langcode2", + ), + ); + $this->assertEqual($translation->description->getValue(), $expected, 'Language-aware default values correctly populated.'); + $this->assertEqual($translation->description->getLangcode(), $langcode2, 'Field object has the expected langcode.'); + + // Reset hook firing information. + $this->getHooksInfo(); + } + + /** + * Tests language fallback applied to field and entity translations. + */ + function testLanguageFallback() { + // Test all entity variations with data table support. + foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) { + $this->doTestLanguageFallback($entity_type); + } + } + + /** + * Executes the language fallback test for the given entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function doTestLanguageFallback($entity_type) { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = $this->container->get('renderer'); + + $current_langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); + $this->langcodes[] = $current_langcode; + + $values = array(); + foreach ($this->langcodes as $langcode) { + $values[$langcode]['name'] = $this->randomMachineName(); + $values[$langcode]['user_id'] = mt_rand(0, 127); + } + + $default_langcode = $this->langcodes[0]; + $langcode = $this->langcodes[1]; + $langcode2 = $this->langcodes[2]; + $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); + $languages = $this->languageManager->getLanguages(); + $language = ConfigurableLanguage::load($languages[$langcode]->getId()); + $language->set('weight', 1); + $language->save(); + $this->languageManager->reset(); + + $controller = $this->entityManager->getStorage($entity_type); + $entity = $controller->create(array($langcode_key => $default_langcode) + $values[$default_langcode]); + $entity->save(); + + $entity->addTranslation($langcode, $values[$langcode]); + $entity->save(); + + // Check that retrieving the current translation works as expected. + $entity = $this->reloadEntity($entity); + $translation = $this->entityManager->getTranslationFromContext($entity, $langcode2); + $this->assertEqual($translation->language()->getId(), $default_langcode, 'The current translation language matches the expected one.'); + + // Check that language fallback respects language weight by default. + $language = ConfigurableLanguage::load($languages[$langcode]->getId()); + $language->set('weight', -1); + $language->save(); + $translation = $this->entityManager->getTranslationFromContext($entity, $langcode2); + $this->assertEqual($translation->language()->getId(), $langcode, 'The current translation language matches the expected one.'); + + // Check that the current translation is properly returned. + $translation = $this->entityManager->getTranslationFromContext($entity); + $this->assertEqual($langcode, $translation->language()->getId(), 'The current translation language matches the topmost language fallback candidate.'); + $entity->addTranslation($current_langcode, $values[$current_langcode]); + $translation = $this->entityManager->getTranslationFromContext($entity); + $this->assertEqual($current_langcode, $translation->language()->getId(), 'The current translation language matches the current language.'); + + // Check that if the entity has no translation no fallback is applied. + $entity2 = $controller->create(array($langcode_key => $default_langcode)); + // Get an view builder. + $controller = $this->entityManager->getViewBuilder($entity_type); + $entity2_build = $controller->view($entity2); + $entity2_output = (string) $renderer->renderRoot($entity2_build); + $translation = $this->entityManager->getTranslationFromContext($entity2, $default_langcode); + $translation_build = $controller->view($translation); + $translation_output = (string) $renderer->renderRoot($translation_build); + $this->assertIdentical($entity2_output, $translation_output, 'When the entity has no translation no fallback is applied.'); + + // Checks that entity translations are rendered properly. + $controller = $this->entityManager->getViewBuilder($entity_type); + $build = $controller->view($entity); + $renderer->renderRoot($build); + $this->assertEqual($build['label']['#markup'], $values[$current_langcode]['name'], 'By default the entity is rendered in the current language.'); + + $langcodes = array_combine($this->langcodes, $this->langcodes); + // We have no translation for the $langcode2 language, hence the expected + // result is the topmost existing translation, that is $langcode. + $langcodes[$langcode2] = $langcode; + foreach ($langcodes as $desired => $expected) { + $build = $controller->view($entity, 'full', $desired); + // Unset the #cache key so that a fresh render is produced with each pass, + // making the renderable array keys available to compare. + unset($build['#cache']); + $renderer->renderRoot($build); + $this->assertEqual($build['label']['#markup'], $values[$expected]['name'], 'The entity is rendered in the expected language.'); + } + } + + /** + * Check that field translatability is handled properly. + */ + function testFieldDefinitions() { + // Check that field translatability can be altered to be enabled or disabled + // in field definitions. + $entity_type = 'entity_test_mulrev'; + $this->state->set('entity_test.field_definitions.translatable', array('name' => FALSE)); + $this->entityManager->clearCachedFieldDefinitions(); + $definitions = $this->entityManager->getBaseFieldDefinitions($entity_type); + $this->assertFalse($definitions['name']->isTranslatable(), 'Field translatability can be disabled programmatically.'); + + $this->state->set('entity_test.field_definitions.translatable', array('name' => TRUE)); + $this->entityManager->clearCachedFieldDefinitions(); + $definitions = $this->entityManager->getBaseFieldDefinitions($entity_type); + $this->assertTrue($definitions['name']->isTranslatable(), 'Field translatability can be enabled programmatically.'); + + // Check that field translatability is disabled by default. + $base_field_definitions = EntityTestMulRev::baseFieldDefinitions($this->entityManager->getDefinition($entity_type)); + $this->assertTrue(!isset($base_field_definitions['id']->translatable), 'Translatability for the id field is not defined.'); + $this->assertFalse($definitions['id']->isTranslatable(), 'Field translatability is disabled by default.'); + + // Check that entity id keys have the expect translatability. + $translatable_fields = array( + 'id' => TRUE, + 'uuid' => TRUE, + 'revision_id' => TRUE, + 'type' => TRUE, + 'langcode' => FALSE, + ); + foreach ($translatable_fields as $name => $translatable) { + $this->state->set('entity_test.field_definitions.translatable', array($name => $translatable)); + $this->entityManager->clearCachedFieldDefinitions(); + $message = format_string('Field %field cannot be translatable.', array('%field' => $name)); + + try { + $this->entityManager->getBaseFieldDefinitions($entity_type); + $this->fail($message); + } + catch (\LogicException $e) { + $this->pass($message); + } + } + } + + /** + * Tests that changing entity language does not break field language. + */ + public function testLanguageChange() { + // Test all entity variations with data table support. + foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) { + $this->doTestLanguageChange($entity_type); + } + } + + /** + * Executes the entity language change test for the given entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function doTestLanguageChange($entity_type) { + $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); + $controller = $this->entityManager->getStorage($entity_type); + $langcode = $this->langcodes[0]; + + // check that field languages match entity language regardless of field + // translatability. + $values = array( + $langcode_key => $langcode, + $this->fieldName => $this->randomMachineName(), + $this->untranslatableFieldName => $this->randomMachineName(), + ); + $entity = $controller->create($values); + foreach (array($this->fieldName, $this->untranslatableFieldName) as $field_name) { + $this->assertEqual($entity->get($field_name)->getLangcode(), $langcode, 'Field language works as expected.'); + } + + // Check that field languages keep matching entity language even after + // changing it. + $langcode = $this->langcodes[1]; + $entity->{$langcode_key}->value = $langcode; + foreach (array($this->fieldName, $this->untranslatableFieldName) as $field_name) { + $this->assertEqual($entity->get($field_name)->getLangcode(), $langcode, 'Field language works as expected after changing entity language.'); + } + + // Check that entity translation does not affect the language of original + // field values and untranslatable ones. + $langcode = $this->langcodes[0]; + $entity->addTranslation($this->langcodes[2], array($this->fieldName => $this->randomMachineName())); + $entity->{$langcode_key}->value = $langcode; + foreach (array($this->fieldName, $this->untranslatableFieldName) as $field_name) { + $this->assertEqual($entity->get($field_name)->getLangcode(), $langcode, 'Field language works as expected after translating the entity and changing language.'); + } + + // Check that setting the default language to an existing translation + // language causes an exception to be thrown. + $message = 'An exception is thrown when setting the default language to an existing translation language'; + try { + $entity->{$langcode_key}->value = $this->langcodes[2]; + $this->fail($message); + } + catch (\InvalidArgumentException $e) { + $this->pass($message); + } + } + + /** + * Tests how entity adapters work with translations. + */ + function testEntityAdapter() { + $entity_type = 'entity_test'; + $default_langcode = 'en'; + $values[$default_langcode] = array('name' => $this->randomString()); + $controller = $this->entityManager->getStorage($entity_type); + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ + $entity = $controller->create($values[$default_langcode]); + + foreach ($this->langcodes as $langcode) { + $values[$langcode] = array('name' => $this->randomString()); + $entity->addTranslation($langcode, $values[$langcode]); + } + + $langcodes = array_merge(array($default_langcode), $this->langcodes); + foreach ($langcodes as $langcode) { + $adapter = $entity->getTranslation($langcode)->getTypedData(); + $name = $adapter->get('name')->value; + $this->assertEqual($name, $values[$langcode]['name'], SafeMarkup::format('Name correctly retrieved from "@langcode" adapter', array('@langcode' => $langcode))); + } + } + + /** + * Tests if entity references are correct after adding a new translation. + */ + public function testFieldEntityReference() { + $entity_type = 'entity_test_mul'; + $controller = $this->entityManager->getStorage($entity_type); + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ + $entity = $controller->create(); + + foreach ($this->langcodes as $langcode) { + $entity->addTranslation($langcode); + } + + $default_langcode = $entity->getUntranslated()->language()->getId(); + foreach (array_keys($entity->getTranslationLanguages()) as $langcode) { + $translation = $entity->getTranslation($langcode); + foreach ($translation->getFields() as $field_name => $field) { + if ($field->getFieldDefinition()->isTranslatable()) { + $args = ['%field_name' => $field_name, '%langcode' => $langcode]; + $this->assertEqual($langcode, $field->getEntity()->language()->getId(), format_string('Translatable field %field_name on translation %langcode has correct entity reference in translation %langcode.', $args)); + } + else { + $args = ['%field_name' => $field_name, '%langcode' => $langcode, '%default_langcode' => $default_langcode]; + $this->assertEqual($default_langcode, $field->getEntity()->language()->getId(), format_string('Non translatable field %field_name on translation %langcode has correct entity reference in the default translation %default_langcode.', $args)); + } + } + } + } + + /** + * Tests if entity translation statuses are correct after removing two + * translation. + */ + public function testDeleteEntityTranslation() { + $entity_type = 'entity_test_mul'; + $controller = $this->entityManager->getStorage($entity_type); + + // Create a translatable test field. + $field_storage = FieldStorageConfig::create([ + 'entity_type' => $entity_type, + 'field_name' => 'translatable_test_field', + 'type' => 'field_test', + ]); + $field_storage->save(); + + $field = FieldConfig::create([ + 'field_storage' => $field_storage, + 'label' => $this->randomMachineName(), + 'bundle' => $entity_type, + ]); + $field->save(); + + // Create an untranslatable test field. + $field_storage = FieldStorageConfig::create([ + 'entity_type' => $entity_type, + 'field_name' => 'untranslatable_test_field', + 'type' => 'field_test', + 'translatable' => FALSE, + ]); + $field_storage->save(); + + $field = FieldConfig::create([ + 'field_storage' => $field_storage, + 'label' => $this->randomMachineName(), + 'bundle' => $entity_type, + ]); + $field->save(); + + // Create an entity with both translatable and untranslatable test fields. + $values = array( + 'name' => $this->randomString(), + 'translatable_test_field' => $this->randomString(), + 'untranslatable_test_field' => $this->randomString(), + ); + + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ + $entity = $controller->create($values); + + foreach ($this->langcodes as $langcode) { + $entity->addTranslation($langcode, $values); + } + $entity->save(); + + // Assert there are no deleted languages in the lists yet. + $this->assertNull(\Drupal::state()->get('entity_test.delete.translatable_test_field')); + $this->assertNull(\Drupal::state()->get('entity_test.delete.untranslatable_test_field')); + + // Remove the second and third langcodes from the entity. + $entity->removeTranslation('l1'); + $entity->removeTranslation('l2'); + $entity->save(); + + // Ensure that for the translatable test field the second and third + // langcodes are in the deleted languages list. + $actual = \Drupal::state()->get('entity_test.delete.translatable_test_field'); + $expected_translatable = ['l1', 'l2']; + sort($actual); + sort($expected_translatable); + $this->assertEqual($actual, $expected_translatable); + // Ensure that the untranslatable test field is untouched. + $this->assertNull(\Drupal::state()->get('entity_test.delete.untranslatable_test_field')); + + // Delete the entity, which removes all remaining translations. + $entity->delete(); + + // All languages have been deleted now. + $actual = \Drupal::state()->get('entity_test.delete.translatable_test_field'); + $expected_translatable[] = 'en'; + $expected_translatable[] = 'l0'; + sort($actual); + sort($expected_translatable); + $this->assertEqual($actual, $expected_translatable); + + // The untranslatable field is shared and only deleted once, for the + // default langcode. + $actual = \Drupal::state()->get('entity_test.delete.untranslatable_test_field'); + $expected_untranslatable = ['en']; + sort($actual); + sort($expected_untranslatable); + $this->assertEqual($actual, $expected_untranslatable); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityTypeConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityTypeConstraintValidatorTest.php new file mode 100644 index 0000000..40031e5 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityTypeConstraintValidatorTest.php @@ -0,0 +1,65 @@ +typedData = $this->container->get('typed_data_manager'); + } + + /** + * Tests the EntityTypeConstraintValidator. + */ + public function testValidation() { + // Create a typed data definition with an EntityType constraint. + $entity_type = 'node'; + $definition = DataDefinition::create('entity_reference') + ->setConstraints(array( + 'EntityType' => $entity_type, + ) + ); + + // Test the validation. + $node = $this->container->get('entity.manager')->getStorage('node')->create(array('type' => 'page')); + $typed_data = $this->typedData->create($definition, $node); + $violations = $typed_data->validate(); + $this->assertEqual($violations->count(), 0, 'Validation passed for correct value.'); + + // Test the validation when an invalid value (in this case a user entity) + // is passed. + $account = $this->createUser(); + + $typed_data = $this->typedData->create($definition, $account); + $violations = $typed_data->validate(); + $this->assertEqual($violations->count(), 1, 'Validation failed for incorrect value.'); + + // Make sure the information provided by a violation is correct. + $violation = $violations[0]; + $this->assertEqual($violation->getMessage(), t('The entity must be of type %type.', array('%type' => $entity_type)), 'The message for invalid value is correct.'); + $this->assertEqual($violation->getRoot(), $typed_data, 'Violation root is correct.'); + $this->assertEqual($violation->getInvalidValue(), $account, 'The invalid value is set correctly in the violation.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityTypeConstraintsTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityTypeConstraintsTest.php new file mode 100644 index 0000000..f0995b8 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityTypeConstraintsTest.php @@ -0,0 +1,74 @@ +installEntitySchema('entity_test_constraints'); + } + + /** + * Tests defining entity constraints via entity type annotations and hooks. + */ + public function testConstraintDefinition() { + // Test reading the annotation. There should be two constraints, the defined + // constraint and the automatically added EntityChanged constraint. + $entity_type = $this->entityManager->getDefinition('entity_test_constraints'); + $default_constraints = ['NotNull' => [], 'EntityChanged' => NULL]; + $this->assertEqual($default_constraints, $entity_type->getConstraints()); + + // Enable our test module and test extending constraints. + $this->enableModules(['entity_test_constraints']); + $this->container->get('module_handler')->resetImplementations(); + + $extra_constraints = ['Test' => []]; + $this->state->set('entity_test_constraints.build', $extra_constraints); + // Re-fetch the entity manager from the new container built after the new + // modules were enabled. + $this->entityManager = $this->container->get('entity.manager'); + $this->entityManager->clearCachedDefinitions(); + $entity_type = $this->entityManager->getDefinition('entity_test_constraints'); + $this->assertEqual($default_constraints + $extra_constraints, $entity_type->getConstraints()); + + // Test altering constraints. + $altered_constraints = ['Test' => [ 'some_setting' => TRUE]]; + $this->state->set('entity_test_constraints.alter', $altered_constraints); + // Clear the cache in state instance in the Drupal container, so it can pick + // up the modified value. + \Drupal::state()->resetCache(); + $this->entityManager->clearCachedDefinitions(); + $entity_type = $this->entityManager->getDefinition('entity_test_constraints'); + $this->assertEqual($altered_constraints, $entity_type->getConstraints()); + } + + /** + * Tests entity constraints are validated. + */ + public function testConstraintValidation() { + $entity = $this->entityManager->getStorage('entity_test_constraints')->create(); + $entity->user_id->target_id = 0; + $violations = $entity->validate(); + $this->assertEqual($violations->count(), 0, 'Validation passed.'); + $entity->save(); + $entity->changed->value = REQUEST_TIME - 86400; + $violations = $entity->validate(); + $this->assertEqual($violations->count(), 1, 'Validation failed.'); + $this->assertEqual($violations[0]->getMessage(), t('The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.')); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityTypedDataDefinitionTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityTypedDataDefinitionTest.php new file mode 100644 index 0000000..2afdbd4 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityTypedDataDefinitionTest.php @@ -0,0 +1,134 @@ +typedDataManager = $this->container->get('typed_data_manager'); + } + + /** + * Tests deriving metadata about fields. + */ + public function testFields() { + $field_definition = BaseFieldDefinition::create('integer'); + // Fields are lists of complex data. + $this->assertTrue($field_definition instanceof ListDataDefinitionInterface); + $this->assertFalse($field_definition instanceof ComplexDataDefinitionInterface); + $field_item_definition = $field_definition->getItemDefinition(); + $this->assertFalse($field_item_definition instanceof ListDataDefinitionInterface); + $this->assertTrue($field_item_definition instanceof ComplexDataDefinitionInterface); + + // Derive metadata about field item properties. + $this->assertEqual(array_keys($field_item_definition->getPropertyDefinitions()), array('value')); + $this->assertEqual($field_item_definition->getPropertyDefinition('value')->getDataType(), 'integer'); + $this->assertEqual($field_item_definition->getMainPropertyName(), 'value'); + $this->assertNull($field_item_definition->getPropertyDefinition('invalid')); + + // Test accessing field item property metadata via the field definition. + $this->assertTrue($field_definition instanceof FieldDefinitionInterface); + $this->assertEqual(array_keys($field_definition->getPropertyDefinitions()), array('value')); + $this->assertEqual($field_definition->getPropertyDefinition('value')->getDataType(), 'integer'); + $this->assertEqual($field_definition->getMainPropertyName(), 'value'); + $this->assertNull($field_definition->getPropertyDefinition('invalid')); + + // Test using the definition factory for field item lists and field items. + $field_item = $this->typedDataManager->createDataDefinition('field_item:integer'); + $this->assertFalse($field_item instanceof ListDataDefinitionInterface); + $this->assertTrue($field_item instanceof ComplexDataDefinitionInterface); + // Comparison should ignore the internal static cache, so compare the + // serialized objects instead. + $this->assertEqual(serialize($field_item_definition), serialize($field_item)); + + $field_definition2 = $this->typedDataManager->createListDataDefinition('field_item:integer'); + $this->assertTrue($field_definition2 instanceof ListDataDefinitionInterface); + $this->assertFalse($field_definition2 instanceof ComplexDataDefinitionInterface); + $this->assertEqual(serialize($field_definition), serialize($field_definition2)); + } + + /** + * Tests deriving metadata about entities. + */ + public function testEntities() { + $entity_definition = EntityDataDefinition::create('node'); + // Entities are complex data. + $this->assertFalse($entity_definition instanceof ListDataDefinitionInterface); + $this->assertTrue($entity_definition instanceof ComplexDataDefinitionInterface); + + $field_definitions = $entity_definition->getPropertyDefinitions(); + // Comparison should ignore the internal static cache, so compare the + // serialized objects instead. + $this->assertEqual(serialize($field_definitions), serialize(\Drupal::entityManager()->getBaseFieldDefinitions('node'))); + $this->assertEqual($entity_definition->getPropertyDefinition('title')->getItemDefinition()->getDataType(), 'field_item:string'); + $this->assertNull($entity_definition->getMainPropertyName()); + $this->assertNull($entity_definition->getPropertyDefinition('invalid')); + + $entity_definition2 = $this->typedDataManager->createDataDefinition('entity:node'); + $this->assertFalse($entity_definition2 instanceof ListDataDefinitionInterface); + $this->assertTrue($entity_definition2 instanceof ComplexDataDefinitionInterface); + $this->assertEqual(serialize($entity_definition), serialize($entity_definition2)); + + // Test that the definition factory creates the right definitions for all + // entity data types variants. + $this->assertEqual($this->typedDataManager->createDataDefinition('entity'), EntityDataDefinition::create()); + $this->assertEqual($this->typedDataManager->createDataDefinition('entity:node'), EntityDataDefinition::create('node')); + + // Config entities don't support typed data. + $entity_definition = EntityDataDefinition::create('node_type'); + $this->assertEqual(array(), $entity_definition->getPropertyDefinitions()); + } + + /** + * Tests deriving metadata from entity references. + */ + public function testEntityReferences() { + $reference_definition = DataReferenceDefinition::create('entity'); + $this->assertTrue($reference_definition instanceof DataReferenceDefinitionInterface); + + // Test retrieving metadata about the referenced data. + $this->assertEqual($reference_definition->getTargetDefinition()->getDataType(), 'entity'); + $this->assertTrue($reference_definition->getTargetDefinition() instanceof EntityDataDefinitionInterface); + + // Test that the definition factory creates the right definition object. + $reference_definition2 = $this->typedDataManager->createDataDefinition('entity_reference'); + $this->assertTrue($reference_definition2 instanceof DataReferenceDefinitionInterface); + $this->assertEqual($reference_definition2, $reference_definition); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityUUIDTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityUUIDTest.php new file mode 100644 index 0000000..b985c15 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityUUIDTest.php @@ -0,0 +1,108 @@ +installEntitySchema($entity_type_id); + } + } + } + + /** + * Tests UUID generation in entity CRUD operations. + */ + function testCRUD() { + // All entity variations have to have the same results. + foreach (entity_test_entity_types() as $entity_type) { + $this->assertCRUD($entity_type); + } + } + + /** + * Executes the UUID CRUD tests for the given entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function assertCRUD($entity_type) { + // Verify that no UUID is auto-generated when passing one for creation. + $uuid_service = $this->container->get('uuid'); + $uuid = $uuid_service->generate(); + $custom_entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + 'name' => $this->randomMachineName(), + 'uuid' => $uuid, + )); + $this->assertIdentical($custom_entity->uuid(), $uuid); + // Save this entity, so we have more than one later. + $custom_entity->save(); + + // Verify that a new UUID is generated upon creating an entity. + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array('name' => $this->randomMachineName())); + $uuid = $entity->uuid(); + $this->assertTrue($uuid); + + // Verify that the new UUID is different. + $this->assertNotEqual($custom_entity->uuid(), $uuid); + + // Verify that the UUID is retained upon saving. + $entity->save(); + $this->assertIdentical($entity->uuid(), $uuid); + + // Verify that the UUID is retained upon loading. + $entity_loaded = entity_load($entity_type, $entity->id(), TRUE); + $this->assertIdentical($entity_loaded->uuid(), $uuid); + + // Verify that \Drupal::entityManager()->loadEntityByUuid() loads the same entity. + $entity_loaded_by_uuid = \Drupal::entityManager()->loadEntityByUuid($entity_type, $uuid, TRUE); + $this->assertIdentical($entity_loaded_by_uuid->uuid(), $uuid); + $this->assertEqual($entity_loaded_by_uuid->id(), $entity_loaded->id()); + + // Creating a duplicate needs to result in a new UUID. + $entity_duplicate = $entity->createDuplicate(); + foreach ($entity->getFields() as $property => $value) { + switch($property) { + case 'uuid': + $this->assertNotNull($entity_duplicate->uuid()); + $this->assertNotNull($entity->uuid()); + $this->assertNotEqual($entity_duplicate->uuid(), $entity->uuid()); + break; + case 'id': + $this->assertNull($entity_duplicate->id()); + $this->assertNotNull($entity->id()); + $this->assertNotEqual($entity_duplicate->id(), $entity->id()); + break; + case 'revision_id': + $this->assertNull($entity_duplicate->getRevisionId()); + $this->assertNotNull($entity->getRevisionId()); + $this->assertNotEqual($entity_duplicate->getRevisionId(), $entity->getRevisionId()); + $this->assertNotEqual($entity_duplicate->{$property}->getValue(), $entity->{$property}->getValue()); + break; + default: + $this->assertEqual($entity_duplicate->{$property}->getValue(), $entity->{$property}->getValue()); + } + } + $entity_duplicate->save(); + $this->assertNotEqual($entity->id(), $entity_duplicate->id()); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityValidationTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityValidationTest.php new file mode 100644 index 0000000..a1fbc3a --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityValidationTest.php @@ -0,0 +1,208 @@ +installConfig(array('system', 'filter')); + } + + /** + * Creates a test entity. + * + * @param string $entity_type + * An entity type. + * + * @return \Drupal\Core\Entity\EntityInterface + * The created test entity. + */ + protected function createTestEntity($entity_type) { + $this->entityName = $this->randomMachineName(); + $this->entityUser = $this->createUser(); + + // Pass in the value of the name field when creating. With the user + // field we test setting a field after creation. + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(); + $entity->user_id->target_id = $this->entityUser->id(); + $entity->name->value = $this->entityName; + + // Set a value for the test field. + if ($entity->hasField('field_test_text')) { + $this->entityFieldText = $this->randomMachineName(); + $entity->field_test_text->value = $this->entityFieldText; + } + + return $entity; + } + + /** + * Tests validating test entity types. + */ + public function testValidation() { + // Ensure that the constraint manager is marked as cached cleared. + + // Use the protected property on the cache_clearer first to check whether + // the constraint manager is added there. + + // Ensure that the proxy class is initialized, which has the necessary + // method calls attached. + \Drupal::service('plugin.cache_clearer'); + $plugin_cache_clearer = \Drupal::service('drupal.proxy_original_service.plugin.cache_clearer'); + $get_cached_discoveries = function () { + return $this->cachedDiscoveries; + }; + $get_cached_discoveries = $get_cached_discoveries->bindTo($plugin_cache_clearer, $plugin_cache_clearer); + $cached_discoveries = $get_cached_discoveries(); + $cached_discovery_classes = []; + foreach ($cached_discoveries as $cached_discovery) { + $cached_discovery_classes[] = get_class($cached_discovery); + } + $this->assertTrue(in_array('Drupal\Core\Validation\ConstraintManager', $cached_discovery_classes)); + + // All entity variations have to have the same results. + foreach (entity_test_entity_types() as $entity_type) { + $this->checkValidation($entity_type); + } + } + + /** + * Executes the validation test set for a defined entity type. + * + * @param string $entity_type + * The entity type to run the tests with. + */ + protected function checkValidation($entity_type) { + $entity = $this->createTestEntity($entity_type); + $violations = $entity->validate(); + $this->assertEqual($violations->count(), 0, 'Validation passes.'); + + // Test triggering a fail for each of the constraints specified. + $test_entity = clone $entity; + $test_entity->id->value = -1; + $violations = $test_entity->validate(); + $this->assertEqual($violations->count(), 1, 'Validation failed.'); + $this->assertEqual($violations[0]->getMessage(), t('%name: The integer must be larger or equal to %min.', array('%name' => 'ID', '%min' => 0))); + + $test_entity = clone $entity; + $test_entity->uuid->value = $this->randomString(129); + $violations = $test_entity->validate(); + $this->assertEqual($violations->count(), 1, 'Validation failed.'); + $this->assertEqual($violations[0]->getMessage(), t('%name: may not be longer than @max characters.', array('%name' => 'UUID', '@max' => 128))); + + $test_entity = clone $entity; + $langcode_key = $this->entityManager->getDefinition($entity_type)->getKey('langcode'); + $test_entity->{$langcode_key}->value = $this->randomString(13); + $violations = $test_entity->validate(); + // This should fail on AllowedValues and Length constraints. + $this->assertEqual($violations->count(), 2, 'Validation failed.'); + $this->assertEqual($violations[0]->getMessage(), t('This value is too long. It should have %limit characters or less.', array('%limit' => '12'))); + $this->assertEqual($violations[1]->getMessage(), t('The value you selected is not a valid choice.')); + + $test_entity = clone $entity; + $test_entity->type->value = NULL; + $violations = $test_entity->validate(); + $this->assertEqual($violations->count(), 1, 'Validation failed.'); + $this->assertEqual($violations[0]->getMessage(), t('This value should not be null.')); + + $test_entity = clone $entity; + $test_entity->name->value = $this->randomString(33); + $violations = $test_entity->validate(); + $this->assertEqual($violations->count(), 1, 'Validation failed.'); + $this->assertEqual($violations[0]->getMessage(), t('%name: may not be longer than @max characters.', array('%name' => 'Name', '@max' => 32))); + + // Make sure the information provided by a violation is correct. + $violation = $violations[0]; + $this->assertEqual($violation->getRoot()->getValue(), $test_entity, 'Violation root is entity.'); + $this->assertEqual($violation->getPropertyPath(), 'name.0.value', 'Violation property path is correct.'); + $this->assertEqual($violation->getInvalidValue(), $test_entity->name->value, 'Violation contains invalid value.'); + + $test_entity = clone $entity; + $test_entity->set('user_id', 9999); + $violations = $test_entity->validate(); + $this->assertEqual($violations->count(), 1, 'Validation failed.'); + $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', array('%type' => 'user', '%id' => 9999))); + + $test_entity = clone $entity; + $test_entity->field_test_text->format = $this->randomString(33); + $violations = $test_entity->validate(); + $this->assertEqual($violations->count(), 1, 'Validation failed.'); + $this->assertEqual($violations[0]->getMessage(), t('The value you selected is not a valid choice.')); + + // Make sure the information provided by a violation is correct. + $violation = $violations[0]; + $this->assertEqual($violation->getRoot()->getValue(), $test_entity, 'Violation root is entity.'); + $this->assertEqual($violation->getPropertyPath(), 'field_test_text.0.format', 'Violation property path is correct.'); + $this->assertEqual($violation->getInvalidValue(), $test_entity->field_test_text->format, 'Violation contains invalid value.'); + } + + /** + * Tests composite constraints. + */ + public function testCompositeConstraintValidation() { + $entity = $this->createTestEntity('entity_test_composite_constraint'); + $violations = $entity->validate(); + $this->assertEqual($violations->count(), 0); + + // Trigger violation condition. + $entity->name->value = 'test'; + $entity->type->value = 'test2'; + $violations = $entity->validate(); + $this->assertEqual($violations->count(), 1); + + // Make sure we can determine this is composite constraint. + $constraint = $violations[0]->getConstraint(); + $this->assertTrue($constraint instanceof CompositeConstraintBase, 'Constraint is composite constraint.'); + $this->assertEqual('type', $violations[0]->getPropertyPath()); + + /** @var CompositeConstraintBase $constraint */ + $this->assertEqual($constraint->coversFields(), ['name', 'type'], 'Information about covered fields can be retrieved.'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityViewBuilderTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityViewBuilderTest.php new file mode 100644 index 0000000..60ac065 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityViewBuilderTest.php @@ -0,0 +1,217 @@ +installConfig(array('user', 'entity_test')); + + // Give anonymous users permission to view test entities. + Role::load(RoleInterface::ANONYMOUS_ID) + ->grantPermission('view test entity') + ->save(); + } + + /** + * Tests entity render cache handling. + */ + public function testEntityViewBuilderCache() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = $this->container->get('renderer'); + $cache_contexts_manager = \Drupal::service("cache_contexts_manager"); + $cache = \Drupal::cache(); + + // Force a request via GET so we can get drupal_render() cache working. + $request = \Drupal::request(); + $request_method = $request->server->get('REQUEST_METHOD'); + $request->setMethod('GET'); + + $entity_test = $this->createTestEntity('entity_test'); + + // Test that new entities (before they are saved for the first time) do not + // generate a cache entry. + $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); + $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age'], 'The render array element of new (unsaved) entities is not cached, but does have cache tags set.'); + + // Get a fully built entity view render array. + $entity_test->save(); + $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); + $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys()); + $cid = implode(':', $cid_parts); + $bin = $build['#cache']['bin']; + + // Mock the build array to not require the theme registry. + unset($build['#theme']); + $build['#markup'] = 'entity_render_test'; + + // Test that a cache entry is created. + $renderer->renderRoot($build); + $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.'); + + // Re-save the entity and check that the cache entry has been deleted. + $cache->set('kittens', 'Kitten data', Cache::PERMANENT, $build['#cache']['tags']); + $entity_test->save(); + $this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was saved.'); + $this->assertFalse($cache->get('kittens'), 'The entity saving has invalidated cache tags.'); + + // Rebuild the render array (creating a new cache entry in the process) and + // delete the entity to check the cache entry is deleted. + unset($build['#printed']); + $renderer->renderRoot($build); + $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.'); + $entity_test->delete(); + $this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was deleted.'); + + // Restore the previous request method. + $request->setMethod($request_method); + } + + /** + * Tests entity render cache with references. + */ + public function testEntityViewBuilderCacheWithReferences() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = $this->container->get('renderer'); + $cache_contexts_manager = \Drupal::service("cache_contexts_manager"); + + // Force a request via GET so we can get drupal_render() cache working. + $request = \Drupal::request(); + $request_method = $request->server->get('REQUEST_METHOD'); + $request->setMethod('GET'); + + // Create an entity reference field and an entity that will be referenced. + $this->createEntityReferenceField('entity_test', 'entity_test', 'reference_field', 'Reference', 'entity_test'); + entity_get_display('entity_test', 'entity_test', 'full')->setComponent('reference_field', [ + 'type' => 'entity_reference_entity_view', + 'settings' => ['link' => FALSE], + ])->save(); + $entity_test_reference = $this->createTestEntity('entity_test'); + $entity_test_reference->save(); + + // Get a fully built entity view render array for the referenced entity. + $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test_reference, 'full'); + $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys()); + $cid_reference = implode(':', $cid_parts); + $bin_reference = $build['#cache']['bin']; + + // Mock the build array to not require the theme registry. + unset($build['#theme']); + $build['#markup'] = 'entity_render_test'; + $renderer->renderRoot($build); + + // Test that a cache entry was created for the referenced entity. + $this->assertTrue($this->container->get('cache.' . $bin_reference)->get($cid_reference), 'The entity render element for the referenced entity has been cached.'); + + // Create another entity that references the first one. + $entity_test = $this->createTestEntity('entity_test'); + $entity_test->reference_field->entity = $entity_test_reference; + $entity_test->save(); + + // Get a fully built entity view render array. + $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); + $cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys()); + $cid = implode(':', $cid_parts); + $bin = $build['#cache']['bin']; + + // Mock the build array to not require the theme registry. + unset($build['#theme']); + $build['#markup'] = 'entity_render_test'; + $renderer->renderRoot($build); + + // Test that a cache entry is created. + $this->assertTrue($this->container->get('cache.' . $bin)->get($cid), 'The entity render element has been cached.'); + + // Save the entity and verify that both cache entries have been deleted. + $entity_test_reference->save(); + $this->assertFalse($this->container->get('cache.' . $bin)->get($cid), 'The entity render cache has been cleared when the entity was deleted.'); + $this->assertFalse($this->container->get('cache.' . $bin_reference)->get($cid_reference), 'The entity render cache for the referenced entity has been cleared when the entity was deleted.'); + + // Restore the previous request method. + $request->setMethod($request_method); + } + + /** + * Tests entity render cache toggling. + */ + public function testEntityViewBuilderCacheToggling() { + $entity_test = $this->createTestEntity('entity_test'); + $entity_test->save(); + + // Test a view mode in default conditions: render caching is enabled for + // the entity type and the view mode. + $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); + $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age', 'keys', 'bin'] , 'A view mode with render cache enabled has the correct output (cache tags, keys, contexts, max-age and bin).'); + + // Test that a view mode can opt out of render caching. + $build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'test'); + $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age'], 'A view mode with render cache disabled has the correct output (only cache tags, contexts and max-age).'); + + // Test that an entity type can opt out of render caching completely. + $entity_test_no_cache = $this->createTestEntity('entity_test_label'); + $entity_test_no_cache->save(); + $build = $this->container->get('entity.manager')->getViewBuilder('entity_test_label')->view($entity_test_no_cache, 'full'); + $this->assertTrue(isset($build['#cache']) && array_keys($build['#cache']) == ['tags', 'contexts', 'max-age'], 'An entity type can opt out of render caching regardless of view mode configuration, but always has cache tags, contexts and max-age set.'); + } + + /** + * Tests weighting of display components. + */ + public function testEntityViewBuilderWeight() { + /** @var \Drupal\Core\Render\RendererInterface $renderer */ + $renderer = $this->container->get('renderer'); + + // Set a weight for the label component. + entity_get_display('entity_test', 'entity_test', 'full') + ->setComponent('label', array('weight' => 20)) + ->save(); + + // Create and build a test entity. + $entity_test = $this->createTestEntity('entity_test'); + $view = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full'); + $renderer->renderRoot($view); + + // Check that the weight is respected. + $this->assertEqual($view['label']['#weight'], 20, 'The weight of a display component is respected.'); + } + + /** + * Creates an entity for testing. + * + * @param string $entity_type + * The entity type. + * + * @return \Drupal\Core\Entity\EntityInterface + * The created entity. + */ + protected function createTestEntity($entity_type) { + $data = array( + 'bundle' => $entity_type, + 'name' => $this->randomMachineName(), + ); + return $this->container->get('entity.manager')->getStorage($entity_type)->create($data); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/FieldSqlStorageTest.php b/core/tests/Drupal/KernelTests/Core/Entity/FieldSqlStorageTest.php new file mode 100644 index 0000000..612c441 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/FieldSqlStorageTest.php @@ -0,0 +1,564 @@ +installEntitySchema('entity_test_rev'); + $entity_type = 'entity_test_rev'; + + $this->fieldName = strtolower($this->randomMachineName()); + $this->fieldCardinality = 4; + $this->fieldStorage = FieldStorageConfig::create(array( + 'field_name' => $this->fieldName, + 'entity_type' => $entity_type, + 'type' => 'test_field', + 'cardinality' => $this->fieldCardinality, + )); + $this->fieldStorage->save(); + $this->field = FieldConfig::create([ + 'field_storage' => $this->fieldStorage, + 'bundle' => $entity_type + ]); + $this->field->save(); + + /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ + $table_mapping = \Drupal::entityManager()->getStorage($entity_type)->getTableMapping(); + $this->tableMapping = $table_mapping; + $this->table = $table_mapping->getDedicatedDataTableName($this->fieldStorage); + $this->revisionTable = $table_mapping->getDedicatedRevisionTableName($this->fieldStorage); + } + + /** + * Tests field loading works correctly by inserting directly in the tables. + */ + function testFieldLoad() { + $entity_type = $bundle = 'entity_test_rev'; + $storage = $this->container->get('entity.manager')->getStorage($entity_type); + + $columns = array('bundle', 'deleted', 'entity_id', 'revision_id', 'delta', 'langcode', $this->tableMapping->getFieldColumnName($this->fieldStorage, 'value')); + + // Create an entity with four revisions. + $revision_ids = array(); + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(); + $entity->save(); + $revision_ids[] = $entity->getRevisionId(); + for ($i = 0; $i < 4; $i++) { + $entity->setNewRevision(); + $entity->save(); + $revision_ids[] = $entity->getRevisionId(); + } + + // Generate values and insert them directly in the storage tables. + $values = array(); + $query = db_insert($this->revisionTable)->fields($columns); + foreach ($revision_ids as $revision_id) { + // Put one value too many. + for ($delta = 0; $delta <= $this->fieldCardinality; $delta++) { + $value = mt_rand(1, 127); + $values[$revision_id][] = $value; + $query->values(array($bundle, 0, $entity->id(), $revision_id, $delta, $entity->language()->getId(), $value)); + } + $query->execute(); + } + $query = db_insert($this->table)->fields($columns); + foreach ($values[$revision_id] as $delta => $value) { + $query->values(array($bundle, 0, $entity->id(), $revision_id, $delta, $entity->language()->getId(), $value)); + } + $query->execute(); + + // Load every revision and check the values. + foreach ($revision_ids as $revision_id) { + $entity = $storage->loadRevision($revision_id); + foreach ($values[$revision_id] as $delta => $value) { + if ($delta < $this->fieldCardinality) { + $this->assertEqual($entity->{$this->fieldName}[$delta]->value, $value); + } + else { + $this->assertFalse(array_key_exists($delta, $entity->{$this->fieldName})); + } + } + } + + // Load the "current revision" and check the values. + $entity = $storage->load($entity->id()); + foreach ($values[$revision_id] as $delta => $value) { + if ($delta < $this->fieldCardinality) { + $this->assertEqual($entity->{$this->fieldName}[$delta]->value, $value); + } + else { + $this->assertFalse(array_key_exists($delta, $entity->{$this->fieldName})); + } + } + + // Add a translation in an unavailable language code and verify it is not + // loaded. + $unavailable_langcode = 'xx'; + $values = array($bundle, 0, $entity->id(), $entity->getRevisionId(), 0, $unavailable_langcode, mt_rand(1, 127)); + db_insert($this->table)->fields($columns)->values($values)->execute(); + db_insert($this->revisionTable)->fields($columns)->values($values)->execute(); + $entity = $storage->load($entity->id()); + $this->assertFalse(array_key_exists($unavailable_langcode, $entity->{$this->fieldName})); + } + + /** + * Tests field saving works correctly by reading directly from the tables. + */ + function testFieldWrite() { + $entity_type = $bundle = 'entity_test_rev'; + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(); + + $revision_values = array(); + + // Check insert. Add one value too many. + $values = array(); + for ($delta = 0; $delta <= $this->fieldCardinality; $delta++) { + $values[$delta]['value'] = mt_rand(1, 127); + } + $entity->{$this->fieldName} = $values; + $entity->save(); + + // Read the tables and check the correct values have been stored. + $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', \PDO::FETCH_ASSOC); + $this->assertEqual(count($rows), $this->fieldCardinality); + foreach ($rows as $delta => $row) { + $expected = array( + 'bundle' => $bundle, + 'deleted' => 0, + 'entity_id' => $entity->id(), + 'revision_id' => $entity->getRevisionId(), + 'langcode' => $entity->language()->getId(), + 'delta' => $delta, + $this->fieldName . '_value' => $values[$delta]['value'], + ); + $this->assertEqual($row, $expected, "Row $delta was stored as expected."); + } + + // Test update. Add less values and check that the previous values did not + // persist. + $values = array(); + for ($delta = 0; $delta <= $this->fieldCardinality - 2; $delta++) { + $values[$delta]['value'] = mt_rand(1, 127); + } + $entity->{$this->fieldName} = $values; + $entity->save(); + $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', \PDO::FETCH_ASSOC); + $this->assertEqual(count($rows), count($values)); + foreach ($rows as $delta => $row) { + $expected = array( + 'bundle' => $bundle, + 'deleted' => 0, + 'entity_id' => $entity->id(), + 'revision_id' => $entity->getRevisionId(), + 'langcode' => $entity->language()->getId(), + 'delta' => $delta, + $this->fieldName . '_value' => $values[$delta]['value'], + ); + $this->assertEqual($row, $expected, "Row $delta was stored as expected."); + } + + // Create a new revision. + $revision_values[$entity->getRevisionId()] = $values; + $values = array(); + for ($delta = 0; $delta < $this->fieldCardinality; $delta++) { + $values[$delta]['value'] = mt_rand(1, 127); + } + $entity->{$this->fieldName} = $values; + $entity->setNewRevision(); + $entity->save(); + $revision_values[$entity->getRevisionId()] = $values; + + // Check that data for both revisions are in the revision table. + foreach ($revision_values as $revision_id => $values) { + $rows = db_select($this->revisionTable, 't')->fields('t')->condition('revision_id', $revision_id)->execute()->fetchAllAssoc('delta', \PDO::FETCH_ASSOC); + $this->assertEqual(count($rows), min(count($values), $this->fieldCardinality)); + foreach ($rows as $delta => $row) { + $expected = array( + 'bundle' => $bundle, + 'deleted' => 0, + 'entity_id' => $entity->id(), + 'revision_id' => $revision_id, + 'langcode' => $entity->language()->getId(), + 'delta' => $delta, + $this->fieldName . '_value' => $values[$delta]['value'], + ); + $this->assertEqual($row, $expected, "Row $delta was stored as expected."); + } + } + + // Test emptying the field. + $entity->{$this->fieldName} = NULL; + $entity->save(); + $rows = db_select($this->table, 't')->fields('t')->execute()->fetchAllAssoc('delta', \PDO::FETCH_ASSOC); + $this->assertEqual(count($rows), 0); + } + + /** + * Tests that long entity type and field names do not break. + */ + function testLongNames() { + // Use one of the longest entity_type names in core. + $entity_type = $bundle = 'entity_test_label_callback'; + $storage = $this->container->get('entity.manager')->getStorage($entity_type); + + // Create two fields and generate random values. + $name_base = Unicode::strtolower($this->randomMachineName(FieldStorageConfig::NAME_MAX_LENGTH - 1)); + $field_names = array(); + $values = array(); + for ($i = 0; $i < 2; $i++) { + $field_names[$i] = $name_base . $i; + FieldStorageConfig::create(array( + 'field_name' => $field_names[$i], + 'entity_type' => $entity_type, + 'type' => 'test_field', + ))->save(); + FieldConfig::create([ + 'field_name' => $field_names[$i], + 'entity_type' => $entity_type, + 'bundle' => $bundle, + ])->save(); + $values[$field_names[$i]] = mt_rand(1, 127); + } + + // Save an entity with values. + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create($values); + $entity->save(); + + // Load the entity back and check the values. + $entity = $storage->load($entity->id()); + foreach ($field_names as $field_name) { + $this->assertEqual($entity->get($field_name)->value, $values[$field_name]); + } + } + + /** + * Test trying to update a field with data. + */ + function testUpdateFieldSchemaWithData() { + $entity_type = 'entity_test_rev'; + // Create a decimal 5.2 field and add some data. + $field_storage = FieldStorageConfig::create(array( + 'field_name' => 'decimal52', + 'entity_type' => $entity_type, + 'type' => 'decimal', + 'settings' => array('precision' => 5, 'scale' => 2), + )); + $field_storage->save(); + $field = FieldConfig::create([ + 'field_storage' => $field_storage, + 'bundle' => $entity_type, + ]); + $field->save(); + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + 'id' => 0, + 'revision_id' => 0, + )); + $entity->decimal52->value = '1.235'; + $entity->save(); + + // Attempt to update the field in a way that would work without data. + $field_storage->setSetting('scale', 3); + try { + $field_storage->save(); + $this->fail(t('Cannot update field schema with data.')); + } + catch (FieldStorageDefinitionUpdateForbiddenException $e) { + $this->pass(t('Cannot update field schema with data.')); + } + } + + /** + * Test that failure to create fields is handled gracefully. + */ + function testFieldUpdateFailure() { + // Create a text field. + $field_storage = FieldStorageConfig::create(array( + 'field_name' => 'test_text', + 'entity_type' => 'entity_test_rev', + 'type' => 'text', + 'settings' => array('max_length' => 255), + )); + $field_storage->save(); + + // Attempt to update the field in a way that would break the storage. The + // parenthesis suffix is needed because SQLite has *very* relaxed rules for + // data types, so we actually need to provide an invalid SQL syntax in order + // to break it. + // @see https://www.sqlite.org/datatype3.html + $prior_field_storage = $field_storage; + $field_storage->setSetting('max_length', '-1)'); + try { + $field_storage->save(); + $this->fail(t('Update succeeded.')); + } + catch (\Exception $e) { + $this->pass(t('Update properly failed.')); + } + + // Ensure that the field tables are still there. + $tables = array( + $this->tableMapping->getDedicatedDataTableName($prior_field_storage), + $this->tableMapping->getDedicatedRevisionTableName($prior_field_storage), + ); + foreach ($tables as $table_name) { + $this->assertTrue(db_table_exists($table_name), t('Table %table exists.', array('%table' => $table_name))); + } + } + + /** + * Test adding and removing indexes while data is present. + */ + function testFieldUpdateIndexesWithData() { + // Create a decimal field. + $field_name = 'testfield'; + $entity_type = 'entity_test_rev'; + $field_storage = FieldStorageConfig::create(array( + 'field_name' => $field_name, + 'entity_type' => $entity_type, + 'type' => 'text', + )); + $field_storage->save(); + $field = FieldConfig::create([ + 'field_storage' => $field_storage, + 'bundle' => $entity_type, + ]); + $field->save(); + $tables = array($this->tableMapping->getDedicatedDataTableName($field_storage), $this->tableMapping->getDedicatedRevisionTableName($field_storage)); + + // Verify the indexes we will create do not exist yet. + foreach ($tables as $table) { + $this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value'), t("No index named value exists in @table", array('@table' => $table))); + $this->assertFalse(Database::getConnection()->schema()->indexExists($table, 'value_format'), t("No index named value_format exists in @table", array('@table' => $table))); + } + + // Add data so the table cannot be dropped. + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array( + 'id' => 1, + 'revision_id' => 1, + )); + $entity->$field_name->value = 'field data'; + $entity->enforceIsNew(); + $entity->save(); + + // Add an index. + $field_storage->setIndexes(['value' => [['value', 255]]]); + $field_storage->save(); + foreach ($tables as $table) { + $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value created in @table", array('@table' => $table))); + } + + // Add a different index, removing the existing custom one. + $field_storage->setIndexes(['value_format' => [['value', 127], ['format', 127]]]); + $field_storage->save(); + foreach ($tables as $table) { + $this->assertTrue(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value_format"), t("Index on value_format created in @table", array('@table' => $table))); + $this->assertFalse(Database::getConnection()->schema()->indexExists($table, "{$field_name}_value"), t("Index on value removed in @table", array('@table' => $table))); + } + + // Verify that the tables were not dropped in the process. + $entity = $this->container->get('entity.manager')->getStorage($entity_type)->load(1); + $this->assertEqual($entity->$field_name->value, 'field data', t("Index changes performed without dropping the tables")); + } + + /** + * Test foreign key support. + */ + function testFieldSqlStorageForeignKeys() { + // Create a 'shape' field, with a configurable foreign key (see + // field_test_field_schema()). + $field_name = 'testfield'; + $foreign_key_name = 'shape'; + $field_storage = FieldStorageConfig::create(array( + 'field_name' => $field_name, + 'entity_type' => 'entity_test', + 'type' => 'shape', + 'settings' => array('foreign_key_name' => $foreign_key_name), + )); + $field_storage->save(); + // Get the field schema. + $schema = $field_storage->getSchema(); + + // Retrieve the field definition and check that the foreign key is in place. + $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name preserved through CRUD'); + $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name preserved through CRUD'); + + // Update the field settings, it should update the foreign key definition too. + $foreign_key_name = 'color'; + $field_storage->setSetting('foreign_key_name', $foreign_key_name); + $field_storage->save(); + // Reload the field schema after the update. + $schema = $field_storage->getSchema(); + + // Check that the foreign key is in place. + $this->assertEqual($schema['foreign keys'][$foreign_key_name]['table'], $foreign_key_name, 'Foreign key table name modified after update'); + $this->assertEqual($schema['foreign keys'][$foreign_key_name]['columns'][$foreign_key_name], 'id', 'Foreign key column name modified after update'); + } + + /** + * Tests table name generation. + */ + public function testTableNames() { + // Note: we need to test entity types with long names. We therefore use + // fields on imaginary entity types (works as long as we don't actually save + // them), and just check the generated table names. + + // Short entity type and field name. + $entity_type = 'short_entity_type'; + $field_name = 'short_field_name'; + $field_storage = FieldStorageConfig::create(array( + 'entity_type' => $entity_type, + 'field_name' => $field_name, + 'type' => 'test_field', + )); + $expected = 'short_entity_type__short_field_name'; + $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $expected); + $expected = 'short_entity_type_revision__short_field_name'; + $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $expected); + + // Short entity type, long field name + $entity_type = 'short_entity_type'; + $field_name = 'long_field_name_abcdefghijklmnopqrstuvwxyz'; + $field_storage = FieldStorageConfig::create(array( + 'entity_type' => $entity_type, + 'field_name' => $field_name, + 'type' => 'test_field', + )); + $expected = 'short_entity_type__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); + $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $expected); + $expected = 'short_entity_type_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); + $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $expected); + + // Long entity type, short field name + $entity_type = 'long_entity_type_abcdefghijklmnopqrstuvwxyz'; + $field_name = 'short_field_name'; + $field_storage = FieldStorageConfig::create(array( + 'entity_type' => $entity_type, + 'field_name' => $field_name, + 'type' => 'test_field', + )); + $expected = 'long_entity_type_abcdefghijklmnopq__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); + $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $expected); + $expected = 'long_entity_type_abcdefghijklmnopq_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); + $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $expected); + + // Long entity type and field name. + $entity_type = 'long_entity_type_abcdefghijklmnopqrstuvwxyz'; + $field_name = 'long_field_name_abcdefghijklmnopqrstuvwxyz'; + $field_storage = FieldStorageConfig::create(array( + 'entity_type' => $entity_type, + 'field_name' => $field_name, + 'type' => 'test_field', + )); + $expected = 'long_entity_type_abcdefghijklmnopq__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); + $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $expected); + $expected = 'long_entity_type_abcdefghijklmnopq_r__' . substr(hash('sha256', $field_storage->uuid()), 0, 10); + $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $expected); + // Try creating a second field and check there are no clashes. + $field_storage2 = FieldStorageConfig::create(array( + 'entity_type' => $entity_type, + 'field_name' => $field_name . '2', + 'type' => 'test_field', + )); + $this->assertNotEqual($this->tableMapping->getDedicatedDataTableName($field_storage), $this->tableMapping->getDedicatedDataTableName($field_storage2)); + $this->assertNotEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage), $this->tableMapping->getDedicatedRevisionTableName($field_storage2)); + + // Deleted field. + $field_storage = FieldStorageConfig::create(array( + 'entity_type' => 'some_entity_type', + 'field_name' => 'some_field_name', + 'type' => 'test_field', + 'deleted' => TRUE, + )); + $expected = 'field_deleted_data_' . substr(hash('sha256', $field_storage->uuid()), 0, 10); + $this->assertEqual($this->tableMapping->getDedicatedDataTableName($field_storage, TRUE), $expected); + $expected = 'field_deleted_revision_' . substr(hash('sha256', $field_storage->uuid()), 0, 10); + $this->assertEqual($this->tableMapping->getDedicatedRevisionTableName($field_storage, TRUE), $expected); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/FieldTranslationSqlStorageTest.php b/core/tests/Drupal/KernelTests/Core/Entity/FieldTranslationSqlStorageTest.php new file mode 100644 index 0000000..0adee64 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/FieldTranslationSqlStorageTest.php @@ -0,0 +1,109 @@ +entityManager->getStorage($entity_type); + $values = array( + $this->fieldName => $this->randomMachineName(), + $this->untranslatableFieldName => $this->randomMachineName(), + ); + $entity = $controller->create($values); + $entity->save(); + + // Tests that when changing language field language codes are still correct. + $langcode = $this->langcodes[0]; + $entity->langcode->value = $langcode; + $entity->save(); + $this->assertFieldStorageLangcode($entity, 'Field language successfully changed from language neutral.'); + $langcode = $this->langcodes[1]; + $entity->langcode->value = $langcode; + $entity->save(); + $this->assertFieldStorageLangcode($entity, 'Field language successfully changed.'); + $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED; + $entity->langcode->value = $langcode; + $entity->save(); + $this->assertFieldStorageLangcode($entity, 'Field language successfully changed to language neutral.'); + + // Test that after switching field translatability things keep working as + // before. + $this->toggleFieldTranslatability($entity_type, $entity_type); + $entity = $this->reloadEntity($entity); + foreach (array($this->fieldName, $this->untranslatableFieldName) as $field_name) { + $this->assertEqual($entity->get($field_name)->value, $values[$field_name], 'Field language works as expected after switching translatability.'); + } + + // Test that after disabling field translatability translated values are not + // loaded. + $this->toggleFieldTranslatability($entity_type, $entity_type); + $entity = $this->reloadEntity($entity); + $entity->langcode->value = $this->langcodes[0]; + $translation = $entity->addTranslation($this->langcodes[1]); + $translated_value = $this->randomMachineName(); + $translation->get($this->fieldName)->value = $translated_value; + $translation->save(); + $this->toggleFieldTranslatability($entity_type, $entity_type); + $entity = $this->reloadEntity($entity); + $this->assertEqual($entity->getTranslation($this->langcodes[1])->get($this->fieldName)->value, $values[$this->fieldName], 'Existing field translations are not loaded for untranslatable fields.'); + } + + /** + * Checks whether field languages are correctly stored for the given entity. + * + * @param \Drupal\Core\Entity\FieldableEntityInterface $entity + * The entity fields are attached to. + * @param string $message + * (optional) A message to display with the assertion. + */ + protected function assertFieldStorageLangcode(FieldableEntityInterface $entity, $message = '') { + $status = TRUE; + $entity_type = $entity->getEntityTypeId(); + $id = $entity->id(); + $langcode = $entity->getUntranslated()->language()->getId(); + $fields = array($this->fieldName, $this->untranslatableFieldName); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ + $table_mapping = \Drupal::entityManager()->getStorage($entity_type)->getTableMapping(); + + foreach ($fields as $field_name) { + $field_storage = FieldStorageConfig::loadByName($entity_type, $field_name); + $table = $table_mapping->getDedicatedDataTableName($field_storage); + + $record = \Drupal::database() + ->select($table, 'f') + ->fields('f') + ->condition('f.entity_id', $id) + ->condition('f.revision_id', $id) + ->execute() + ->fetchObject(); + + if ($record->langcode != $langcode) { + $status = FALSE; + break; + } + } + + return $this->assertTrue($status, $message); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/FieldWidgetConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Entity/FieldWidgetConstraintValidatorTest.php new file mode 100644 index 0000000..49d451d --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/FieldWidgetConstraintValidatorTest.php @@ -0,0 +1,159 @@ +installSchema('system', ['key_value']); + $this->container->get('router.builder')->rebuild(); + + $this->installEntitySchema('user'); + $this->installEntitySchema('entity_test_composite_constraint'); + } + + /** + * Tests widget constraint validation. + */ + public function testValidation() { + $entity_type = 'entity_test_constraint_violation'; + $entity = $this->container->get('entity_type.manager') + ->getStorage($entity_type) + ->create(array('id' => 1, 'revision_id' => 1)); + $display = entity_get_form_display($entity_type, $entity_type, 'default'); + $form = array(); + $form_state = new FormState(); + $display->buildForm($entity, $form, $form_state); + + // Pretend the form has been built. + $form_state->setFormObject(\Drupal::entityManager()->getFormObject($entity_type, 'default')); + \Drupal::formBuilder()->prepareForm('field_test_entity_form', $form, $form_state); + \Drupal::formBuilder()->processForm('field_test_entity_form', $form, $form_state); + + // Validate the field constraint. + $form_state->getFormObject()->setEntity($entity)->setFormDisplay($display, $form_state); + $entity = $form_state->getFormObject()->buildEntity($form, $form_state); + $display->validateFormValues($entity, $form, $form_state); + + $errors = $form_state->getErrors(); + $this->assertEqual($errors['name'], 'Widget constraint has failed.', 'Constraint violation is generated correctly'); + } + + /** + * Gets the form errors for a given entity. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity + * @param array $hidden_fields + * (optional) A list of hidden fields. + * + * @return array + * The form errors. + */ + protected function getErrorsForEntity(EntityInterface $entity, $hidden_fields = []) { + $entity_type_id = 'entity_test_composite_constraint'; + $display = entity_get_form_display($entity_type_id, $entity_type_id, 'default'); + + foreach ($hidden_fields as $hidden_field) { + $display->removeComponent($hidden_field); + } + + $form = []; + $form_state = new FormState(); + $display->buildForm($entity, $form, $form_state); + + $form_state->setFormObject(\Drupal::entityManager()->getFormObject($entity_type_id, 'default')); + \Drupal::formBuilder()->prepareForm('field_test_entity_form', $form, $form_state); + \Drupal::formBuilder()->processForm('field_test_entity_form', $form, $form_state); + + // Validate the field constraint. + /** @var \Drupal\Core\Entity\ContentEntityFormInterface $form_object */ + $form_object = $form_state->getFormObject(); + $form_object + ->setEntity($entity) + ->setFormDisplay($display, $form_state) + ->validateForm($form, $form_state); + + return $form_state->getErrors(); + } + + /** + * Tests widget constraint validation with composite constraints. + */ + public function testValidationWithCompositeConstraint() { + // First provide a valid value, this should cause no validation. + $entity = EntityTestCompositeConstraint::create([ + 'name' => 'valid-value', + ]); + $entity->save(); + + $errors = $this->getErrorsForEntity($entity); + $this->assertFalse(isset($errors['name'])); + $this->assertFalse(isset($errors['type'])); + + // Provide an invalid value for the name field. + $entity = EntityTestCompositeConstraint::create([ + 'name' => 'failure-field-name', + ]); + $errors = $this->getErrorsForEntity($entity); + $this->assertTrue(isset($errors['name'])); + $this->assertFalse(isset($errors['type'])); + + // Hide the second field (type) and ensure the validation still happens. The + // error message appears on the first field (name). + $entity = EntityTestCompositeConstraint::create([ + 'name' => 'failure-field-name', + ]); + $errors = $this->getErrorsForEntity($entity, ['type']); + $this->assertTrue(isset($errors['name'])); + $this->assertFalse(isset($errors['type'])); + + // Provide a violation again, but this time hide the first field (name). + // Ensure that the validation still happens and the error message is moved + // from the field to the second field and have a custom error message. + $entity = EntityTestCompositeConstraint::create([ + 'name' => 'failure-field-name', + ]); + $errors = $this->getErrorsForEntity($entity, ['name']); + $this->assertFalse(isset($errors['name'])); + $this->assertTrue(isset($errors['type'])); + $this->assertEqual($errors['type'], SafeMarkup::format('The validation failed because the value conflicts with the value in %field_name, which you cannot access.', ['%field_name' => 'name'])); + } + + /** + * Tests entity level constraint validation. + */ + public function testEntityLevelConstraintValidation() { + $entity = EntityTestCompositeConstraint::create([ + 'name' => 'entity-level-violation' + ]); + $entity->save(); + + $errors = $this->getErrorsForEntity($entity); + $this->assertEqual($errors[''], 'Entity level validation'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/RevisionableContentEntityBaseTest.php b/core/tests/Drupal/KernelTests/Core/Entity/RevisionableContentEntityBaseTest.php new file mode 100644 index 0000000..c8f0ce5 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/RevisionableContentEntityBaseTest.php @@ -0,0 +1,61 @@ +installEntitySchema('entity_test_revlog'); + $this->installEntitySchema('user'); + $this->installSchema('system', 'sequences'); + } + + public function testRevisionableContentEntity() { + $user = User::create(['name' => 'test name']); + $user->save(); + /** @var \Drupal\entity_test\Entity\EntityTestWithRevisionLog $entity */ + $entity = EntityTestWithRevisionLog::create([ + 'type' => 'entity_test_revlog', + ]); + $entity->save(); + + $entity->setNewRevision(TRUE); + $random_timestamp = rand(1e8, 2e8); + $entity->setRevisionCreationTime($random_timestamp); + $entity->setRevisionUserId($user->id()); + $entity->setRevisionLogMessage('This is my log message'); + $entity->save(); + + $revision_id = $entity->getRevisionId(); + + $entity = \Drupal::entityTypeManager()->getStorage('entity_test_revlog')->loadRevision($revision_id); + $this->assertEquals($random_timestamp, $entity->getRevisionCreationTime()); + $this->assertEquals($user->id(), $entity->getRevisionUserId()); + $this->assertEquals($user->id(), $entity->getRevisionUser()->id()); + $this->assertEquals('This is my log message', $entity->getRevisionLogMessage()); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Entity/RouteProviderTest.php b/core/tests/Drupal/KernelTests/Core/Entity/RouteProviderTest.php index 3b7af41..c97f03a 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/RouteProviderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/RouteProviderTest.php @@ -36,14 +36,6 @@ protected function setUp() { $this->installEntitySchema('user'); $this->installEntitySchema('entity_test_mul'); $this->installEntitySchema('entity_test_admin_routes'); - $this->installSchema('system', 'router'); - - $router_builder = \Drupal::service('router.builder'); - $router_builder->rebuild(); - - /** @var \Drupal\Core\Routing\RouteBuilderInterface $router_builder */ - $router_builder = \Drupal::service('router.builder'); - $router_builder->rebuild(); /** @var \Drupal\user\RoleInterface $role */ $role = Role::create([ diff --git a/core/tests/Drupal/KernelTests/Core/Entity/ValidReferenceConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Entity/ValidReferenceConstraintValidatorTest.php new file mode 100644 index 0000000..8b8aabf --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/ValidReferenceConstraintValidatorTest.php @@ -0,0 +1,74 @@ +installSchema('user', array('users_data')); + $this->typedData = $this->container->get('typed_data_manager'); + } + + /** + * Tests the ValidReferenceConstraintValidator. + */ + public function testValidation() { + // Create a test entity to be referenced. + $entity = $this->createUser(); + // By default entity references already have the ValidReference constraint. + $definition = BaseFieldDefinition::create('entity_reference') + ->setSettings(array('target_type' => 'user')); + + $typed_data = $this->typedData->create($definition, array('target_id' => $entity->id())); + $violations = $typed_data->validate(); + $this->assertFalse($violations->count(), 'Validation passed for correct value.'); + + // NULL is also considered a valid reference. + $typed_data = $this->typedData->create($definition, array('target_id' => NULL)); + $violations = $typed_data->validate(); + $this->assertFalse($violations->count(), 'Validation passed for correct value.'); + + $typed_data = $this->typedData->create($definition, array('target_id' => $entity->id())); + // Delete the referenced entity. + $entity->delete(); + $violations = $typed_data->validate(); + $this->assertTrue($violations->count(), 'Validation failed for incorrect value.'); + + // Make sure the information provided by a violation is correct. + $violation = $violations[0]; + $this->assertEqual($violation->getMessage(), t('The referenced entity (%type: %id) does not exist.', array( + '%type' => 'user', + '%id' => $entity->id(), + )), 'The message for invalid value is correct.'); + $this->assertEqual($violation->getRoot(), $typed_data, 'Violation root is correct.'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Field/FieldAccessTest.php b/core/tests/Drupal/KernelTests/Core/Field/FieldAccessTest.php new file mode 100644 index 0000000..059cca7 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Field/FieldAccessTest.php @@ -0,0 +1,83 @@ +installConfig(array('field')); + // The users table is needed for creating dummy user accounts. + $this->installEntitySchema('user'); + // Register entity_test text field. + module_load_install('entity_test'); + entity_test_install(); + } + + /** + * Tests hook_entity_field_access() and hook_entity_field_access_alter(). + * + * @see entity_test_entity_field_access() + * @see entity_test_entity_field_access_alter() + */ + function testFieldAccess() { + $values = array( + 'name' => $this->randomMachineName(), + 'user_id' => 1, + 'field_test_text' => array( + 'value' => 'no access value', + 'format' => 'full_html', + ), + ); + $entity = EntityTest::create($values); + + // Create a dummy user account for testing access with. + $values = array('name' => 'test'); + $account = User::create($values); + + $this->assertFalse($entity->field_test_text->access('view', $account), 'Access to the field was denied.'); + $expected = AccessResult::forbidden()->addCacheableDependency($entity); + $this->assertEqual($expected, $entity->field_test_text->access('view', $account, TRUE), 'Access to the field was denied.'); + + $entity->field_test_text = 'access alter value'; + $this->assertFalse($entity->field_test_text->access('view', $account), 'Access to the field was denied.'); + $this->assertEqual($expected, $entity->field_test_text->access('view', $account, TRUE), 'Access to the field was denied.'); + + $entity->field_test_text = 'standard value'; + $this->assertTrue($entity->field_test_text->access('view', $account), 'Access to the field was granted.'); + $this->assertEqual(AccessResult::allowed(), $entity->field_test_text->access('view', $account, TRUE), 'Access to the field was granted.'); + } +} diff --git a/core/tests/Drupal/KernelTests/Core/Field/FieldItemTest.php b/core/tests/Drupal/KernelTests/Core/Field/FieldItemTest.php new file mode 100644 index 0000000..533484c --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Field/FieldItemTest.php @@ -0,0 +1,110 @@ +container->get('state')->set('entity_test.field_test_item', TRUE); + $this->entityManager->clearCachedDefinitions(); + + $entity_type_id = 'entity_test_mulrev'; + $this->installEntitySchema($entity_type_id); + + $this->fieldName = Unicode::strtolower($this->randomMachineName()); + + /** @var \Drupal\field\Entity\FieldStorageConfig $field_storage */ + FieldStorageConfig::create([ + 'field_name' => $this->fieldName, + 'type' => 'field_test', + 'entity_type' => $entity_type_id, + 'cardinality' => 1, + ])->save(); + + FieldConfig::create([ + 'entity_type' => $entity_type_id, + 'field_name' => $this->fieldName, + 'bundle' => $entity_type_id, + 'label' => 'Test field', + ])->save(); + + $this->entityManager->clearCachedDefinitions(); + $definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id); + $this->assertTrue(!empty($definitions[$this->fieldName])); + } + + /** + * Tests the field item save workflow. + */ + public function testSaveWorkflow() { + $entity = EntityTestMulRev::create([ + 'name' => $this->randomString(), + 'field_test_item' => $this->randomString(), + $this->fieldName => $this->randomString(), + ]); + + // Save a new entity and verify that the initial field value is overwritten + // with a value containing the entity id, which implies a resave. Check that + // the entity data structure and the stored values match. + $this->assertSavedFieldItemValue($entity, "field_test:{$this->fieldName}:1:1"); + + // Update the entity and verify that the field value is overwritten on + // presave if it is not resaved. + $this->assertSavedFieldItemValue($entity, 'overwritten'); + + // Flag the field value as needing to be resaved and verify it actually is. + $entity->field_test_item->value = $entity->{$this->fieldName}->value = 'resave'; + $this->assertSavedFieldItemValue($entity, "field_test:{$this->fieldName}:1:3"); + } + + /** + * Checks that the saved field item value matches the expected one. + * + * @param \Drupal\entity_test\Entity\EntityTest $entity + * The test entity. + * @param $expected_value + * The expected field item value. + * + * @return bool + * TRUE if the item value matches expectations, FALSE otherwise. + */ + protected function assertSavedFieldItemValue(EntityTest $entity, $expected_value) { + $entity->setNewRevision(TRUE); + $entity->save(); + $base_field_expected_value = str_replace($this->fieldName, 'field_test_item', $expected_value); + $result = $this->assertEqual($entity->field_test_item->value, $base_field_expected_value); + $result = $result && $this->assertEqual($entity->{$this->fieldName}->value, $expected_value); + $entity = $this->reloadEntity($entity); + $result = $result && $this->assertEqual($entity->field_test_item->value, $base_field_expected_value); + $result = $result && $this->assertEqual($entity->{$this->fieldName}->value, $expected_value); + return $result; + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Field/FieldModuleUninstallValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Field/FieldModuleUninstallValidatorTest.php new file mode 100644 index 0000000..9b10ccf --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Field/FieldModuleUninstallValidatorTest.php @@ -0,0 +1,145 @@ +installSchema('user', 'users_data'); + $this->entityDefinitionUpdateManager = $this->container->get('entity.definition_update_manager'); + + // Setup some fields for entity_test_extra to create. + $definitions['extra_base_field'] = BaseFieldDefinition::create('string') + ->setName('extra_base_field') + ->setTargetEntityTypeId('entity_test') + ->setTargetBundle('entity_test'); + $this->state->set('entity_test.additional_base_field_definitions', $definitions); + $definitions['extra_bundle_field'] = FieldStorageDefinition::create('string') + ->setName('extra_bundle_field') + ->setTargetEntityTypeId('entity_test') + ->setTargetBundle('entity_test'); + $this->state->set('entity_test.additional_field_storage_definitions', $definitions); + $this->state->set('entity_test.entity_test.additional_bundle_field_definitions', $definitions); + $this->entityManager->clearCachedDefinitions(); + } + + /** + * Tests uninstall entity_test module with and without content for the field. + */ + public function testUninstallingModule() { + // Test uninstall works fine without content. + $this->assertModuleInstallUninstall('entity_test_extra'); + + // Test uninstalling works fine with content having no field values. + $entity = $this->entityManager->getStorage('entity_test')->create([ + 'name' => $this->randomString(), + ]); + $entity->save(); + $this->assertModuleInstallUninstall('entity_test_extra'); + $entity->delete(); + + // Verify uninstall works fine without content again. + $this->assertModuleInstallUninstall('entity_test_extra'); + // Verify uninstalling entity_test is not possible when there is content for + // the base field. + $this->enableModules(['entity_test_extra']); + $this->entityDefinitionUpdateManager->applyUpdates(); + $entity = $this->entityManager->getStorage('entity_test')->create([ + 'name' => $this->randomString(), + 'extra_base_field' => $this->randomString(), + ]); + $entity->save(); + + try { + $message = 'Module uninstallation fails as the module provides a base field which has content.'; + $this->getModuleInstaller()->uninstall(array('entity_test_extra')); + $this->fail($message); + } + catch (ModuleUninstallValidatorException $e) { + $this->pass($message); + $this->assertEqual($e->getMessage(), 'The following reasons prevent the modules from being uninstalled: There is data for the field extra_base_field on entity type Test entity'); + } + + // Verify uninstalling entity_test is not possible when there is content for + // the bundle field. + $entity->delete(); + $this->assertModuleInstallUninstall('entity_test_extra'); + $this->enableModules(['entity_test_extra']); + $this->entityDefinitionUpdateManager->applyUpdates(); + $entity = $this->entityManager->getStorage('entity_test')->create([ + 'name' => $this->randomString(), + 'extra_bundle_field' => $this->randomString(), + ]); + $entity->save(); + try { + $this->getModuleInstaller()->uninstall(array('entity_test_extra')); + $this->fail('Module uninstallation fails as the module provides a bundle field which has content.'); + } + catch (ModuleUninstallValidatorException $e) { + $this->pass('Module uninstallation fails as the module provides a bundle field which has content.'); + } + } + + /** + * Asserts the given module can be installed and uninstalled. + * + * @param string $module_name + * The module to install and uninstall. + */ + protected function assertModuleInstallUninstall($module_name) { + // Install the module if it is not installed yet. + if (!\Drupal::moduleHandler()->moduleExists($module_name)) { + $this->enableModules([$module_name]); + } + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertTrue($this->getModuleHandler()->moduleExists($module_name), $module_name .' module is enabled.'); + $this->getModuleInstaller()->uninstall([$module_name]); + $this->entityDefinitionUpdateManager->applyUpdates(); + $this->assertFalse($this->getModuleHandler()->moduleExists($module_name), $module_name . ' module is disabled.'); + } + + /** + * Returns the ModuleHandler. + * + * @return \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected function getModuleHandler() { + return $this->container->get('module_handler'); + } + + /** + * Returns the ModuleInstaller. + * + * @return \Drupal\Core\Extension\ModuleInstallerInterface + */ + protected function getModuleInstaller() { + return $this->container->get('module_installer'); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Field/FieldSettingsTest.php b/core/tests/Drupal/KernelTests/Core/Field/FieldSettingsTest.php new file mode 100644 index 0000000..7234e61 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Field/FieldSettingsTest.php @@ -0,0 +1,120 @@ + 'dummy test string', + 'changeable' => 'a changeable field storage setting', + 'unchangeable' => 'an unchangeable field storage setting', + 'translatable_storage_setting' => 'a translatable field storage setting', + 'test_field_setting' => 'dummy test string', + 'translatable_field_setting' => 'a translatable field setting', + ]; + $this->assertEqual($base_field->getSettings(), $expected_settings); + + // Change one single setting using setSettings(), and check that the other + // expected settings are still present. + $expected_settings['test_field_setting'] = 'another test string'; + $base_field->setSettings(['test_field_setting' => $expected_settings['test_field_setting']]); + $this->assertEqual($base_field->getSettings(), $expected_settings); + } + + /** + * @covers \Drupal\field\Entity\FieldStorageConfig::getSettings + * @covers \Drupal\field\Entity\FieldStorageConfig::setSettings + */ + public function testConfigurableFieldStorageSettings() { + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'test_field', + 'entity_type' => 'entity_test', + 'type' => 'test_field' + ]); + + // Check that the default settings have been populated. + $expected_settings = [ + 'test_field_storage_setting' => 'dummy test string', + 'changeable' => 'a changeable field storage setting', + 'unchangeable' => 'an unchangeable field storage setting', + 'translatable_storage_setting' => 'a translatable field storage setting', + ]; + $this->assertEqual($field_storage->getSettings(), $expected_settings); + + // Change one single setting using setSettings(), and check that the other + // expected settings are still present. + $expected_settings['test_field_storage_setting'] = 'another test string'; + $field_storage->setSettings(['test_field_storage_setting' => $expected_settings['test_field_storage_setting']]); + $this->assertEqual($field_storage->getSettings(), $expected_settings); + } + + /** + * @covers \Drupal\field\Entity\FieldStorageConfig::getSettings + * @covers \Drupal\field\Entity\FieldStorageConfig::setSettings + */ + public function testConfigurableFieldSettings() { + $field_storage = FieldStorageConfig::create([ + 'field_name' => 'test_field', + 'entity_type' => 'entity_test', + 'type' => 'test_field' + ]); + $field = FieldConfig::create([ + 'field_storage' => $field_storage, + 'bundle' => 'entity_test' + ]); + // Note: FieldConfig does not populate default settings until the config + // is saved. + // @todo Remove once https://www.drupal.org/node/2327883 is fixed. + $field->save(); + + // Check that the default settings have been populated. Note: getSettings() + // returns both storage and field settings. + $expected_settings = [ + 'test_field_storage_setting' => 'dummy test string', + 'changeable' => 'a changeable field storage setting', + 'unchangeable' => 'an unchangeable field storage setting', + 'translatable_storage_setting' => 'a translatable field storage setting', + 'test_field_setting' => 'dummy test string', + 'translatable_field_setting' => 'a translatable field setting', + 'field_setting_from_config_data' => 'TRUE', + ]; + $this->assertEqual($field->getSettings(), $expected_settings); + + // Change one single setting using setSettings(), and check that the other + // expected settings are still present. + $expected_settings['test_field_setting'] = 'another test string'; + $field->setSettings(['test_field_setting' => $expected_settings['test_field_setting']]); + $this->assertEqual($field->getSettings(), $expected_settings); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Path/AliasStorageTest.php b/core/tests/Drupal/KernelTests/Core/Path/AliasStorageTest.php index 98180e3..17084f5 100644 --- a/core/tests/Drupal/KernelTests/Core/Path/AliasStorageTest.php +++ b/core/tests/Drupal/KernelTests/Core/Path/AliasStorageTest.php @@ -30,7 +30,6 @@ class AliasStorageTest extends KernelTestBase { protected function setUp() { parent::setUp(); - $this->installSchema('system', 'url_alias'); $this->storage = $this->container->get('path.alias_storage'); } diff --git a/core/tests/Drupal/KernelTests/Core/Theme/StableTemplateOverrideTest.php b/core/tests/Drupal/KernelTests/Core/Theme/StableTemplateOverrideTest.php index d0578af..01fed2c 100644 --- a/core/tests/Drupal/KernelTests/Core/Theme/StableTemplateOverrideTest.php +++ b/core/tests/Drupal/KernelTests/Core/Theme/StableTemplateOverrideTest.php @@ -54,7 +54,6 @@ protected function setUp() { $this->container->get('theme_installer')->install(['stable']); - $this->installSchema('system', 'router'); $this->installAllModules(); } diff --git a/core/tests/Drupal/KernelTests/Core/Theme/ThemeRenderAndAutoescapeTest.php b/core/tests/Drupal/KernelTests/Core/Theme/ThemeRenderAndAutoescapeTest.php index efd84ff..cbbb75a 100644 --- a/core/tests/Drupal/KernelTests/Core/Theme/ThemeRenderAndAutoescapeTest.php +++ b/core/tests/Drupal/KernelTests/Core/Theme/ThemeRenderAndAutoescapeTest.php @@ -31,7 +31,6 @@ class ThemeRenderAndAutoescapeTest extends KernelTestBase { protected function setUp() { parent::setUp(); - $this->installSchema('system', 'router'); \Drupal::service('router.builder')->rebuild(); } diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php index e60abc1..d9aee35 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBase.php +++ b/core/tests/Drupal/KernelTests/KernelTestBase.php @@ -25,6 +25,7 @@ use Drupal\simpletest\AssertContentTrait; use Drupal\simpletest\AssertHelperTrait; use Drupal\simpletest\RandomGeneratorTrait; +use Drupal\simpletest\TestServiceProvider; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpFoundation\Request; use org\bovigo\vfs\vfsStream; @@ -192,6 +193,22 @@ protected $strictConfigSchema = TRUE; /** + * An array of config object names that are excluded from schema checking. + * + * @var string[] + */ + protected static $configSchemaCheckerExclusions = array( + // Following are used to test lack of or partial schema. Where partial + // schema is provided, that is explicitly tested in specific tests. + 'config_schema_test.noschema', + 'config_schema_test.someschema', + 'config_schema_test.schema_data_types', + 'config_schema_test.no_schema_data_types', + // Used to test application of schema to filtering of configuration. + 'config_test.dynamic.system', + ); + + /** * {@inheritdoc} */ public static function setUpBeforeClass() { @@ -287,6 +304,13 @@ protected function bootEnvironment() { } /** + * @return string + */ + public function getDatabasePrefix() { + return $this->databasePrefix; + } + + /** * Bootstraps a kernel for a test. */ private function bootKernel() { @@ -358,6 +382,11 @@ private function bootKernel() { $this->container->get('module_handler')->loadAll(); } + $this->container->get('request_stack')->push($request); + + // Setup the destion to the be frontpage by default. + \Drupal::destination()->set('/'); + // Write the core.extension configuration. // Required for ConfigInstaller::installDefaultConfig() to work. $this->container->get('config.storage')->write('core.extension', array( @@ -584,6 +613,7 @@ public function register(ContainerBuilder $container) { $container ->register('simpletest.config_schema_checker', 'Drupal\Core\Config\Testing\ConfigSchemaChecker') ->addArgument(new Reference('config.typed')) + ->addArgument($this->getConfigSchemaExclusions()) ->addTag('event_subscriber'); } @@ -601,6 +631,26 @@ public function register(ContainerBuilder $container) { $container->getDefinition('password') ->setArguments(array(1)); } + TestServiceProvider::addRouteProvider($container); + } + + /** + * Gets the config schema exclusions for this test. + * + * @return string[] + * An array of config object names that are excluded from schema checking. + */ + protected function getConfigSchemaExclusions() { + $class = get_class($this); + $exceptions = []; + while ($class) { + if (property_exists($class, 'configSchemaCheckerExclusions')) { + $exceptions = array_merge($exceptions, $class::$configSchemaCheckerExclusions); + } + $class = get_parent_class($class); + } + // Filter out any duplicates. + return array_unique($exceptions); } /** @@ -746,6 +796,13 @@ protected function installSchema($module, $tables) { foreach ($tables as $table) { $schema = drupal_get_module_schema($module, $table); if (empty($schema)) { + // BC layer to avoid some contrib tests to fail. + // @todo Remove the BC layer before 8.1.x release. + // @see https://www.drupal.org/node/2670360 + // @see https://www.drupal.org/node/2670454 + if ($module == 'system') { + continue; + } throw new \LogicException("$module module does not define a schema for table '$table'."); } $this->container->get('database')->schema()->createTable($table, $schema); @@ -916,11 +973,15 @@ protected function disableModules(array $modules) { * The rendered string output (typically HTML). */ protected function render(array &$elements) { + // \Drupal\Core\Render\BareHtmlPageRenderer::renderBarePage calls out to + // system_page_attachments() directly. + if (!\Drupal::moduleHandler()->moduleExists('system')) { + throw new \Exception(__METHOD__ . ' requires system module to be installed.'); + } + // Use the bare HTML page renderer to render our links. $renderer = $this->container->get('bare_html_page_renderer'); - $response = $renderer->renderBarePage( - $elements, '', $this->container->get('theme.manager')->getActiveTheme()->getName() - ); + $response = $renderer->renderBarePage($elements, '', 'maintenance_page'); // Glean the content from the response object. $content = $response->getContent(); @@ -1155,5 +1216,4 @@ public static function assertEquals($expected, $actual, $message = '', $delta = parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); } - } diff --git a/core/tests/Drupal/KernelTests/RequestProcessing/RedirectOnExceptionTest.php b/core/tests/Drupal/KernelTests/RequestProcessing/RedirectOnExceptionTest.php index 4c7a686..5434c0e 100644 --- a/core/tests/Drupal/KernelTests/RequestProcessing/RedirectOnExceptionTest.php +++ b/core/tests/Drupal/KernelTests/RequestProcessing/RedirectOnExceptionTest.php @@ -29,7 +29,6 @@ class RedirectOnExceptionTest extends KernelTestBase { protected function setUp() { parent::setUp(); - $this->installSchema('system', ['router', 'url_alias']); \Drupal::service('router.builder')->rebuild(); } diff --git a/core/tests/Drupal/Tests/Component/Assertion/InspectorTest.php b/core/tests/Drupal/Tests/Component/Assertion/InspectorTest.php index 9075128..6042b6f 100644 --- a/core/tests/Drupal/Tests/Component/Assertion/InspectorTest.php +++ b/core/tests/Drupal/Tests/Component/Assertion/InspectorTest.php @@ -11,7 +11,7 @@ /** * @coversDefaultClass \Drupal\Component\Assertion\Inspector - * @group Inspector + * @group Assertion */ class InspectorTest extends PHPUnit_Framework_TestCase { diff --git a/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php b/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php index 5721c64..2076895 100644 --- a/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php +++ b/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php @@ -9,7 +9,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; -use Symfony\Component\DependencyInjection\Exception\LogicException; use Prophecy\Argument; /** @@ -132,7 +131,7 @@ public function testSetParameterWithUnfrozenContainer() { * * @covers ::setParameter * - * @expectedException LogicException + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException */ public function testSetParameterWithFrozenContainer() { $this->container = new $this->containerClass($this->containerDefinition); diff --git a/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php b/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php index cf4d822..c1c6058 100644 --- a/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php +++ b/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php @@ -267,6 +267,10 @@ public function getDefinitionsDataProvider() { ) + $base_service_definition; $service_definitions[] = array( + 'shared' => FALSE, + ) + $base_service_definition; + + $service_definitions[] = array( 'lazy' => TRUE, ) + $base_service_definition; @@ -358,6 +362,10 @@ public function getDefinitionsDataProvider() { 'shared' => FALSE, ) + $base_service_definition; + $service_definitions[] = array( + 'shared' => FALSE, + ) + $base_service_definition; + // Test factory. $service_definitions[] = array( 'factory' => array(new Reference('bar'), 'factoryMethod'), @@ -397,6 +405,7 @@ public function getDefinitionsDataProvider() { $definition->getProperties()->willReturn($service_definition['properties']); $definition->getMethodCalls()->willReturn($service_definition['calls']); $definition->getScope()->willReturn($service_definition['scope']); + $definition->isShared()->willReturn($service_definition['shared']); $definition->getDecoratedService()->willReturn(NULL); $definition->getFactory()->willReturn($service_definition['factory']); $definition->getConfigurator()->willReturn($service_definition['configurator']); diff --git a/core/tests/Drupal/Tests/Component/Discovery/YamlDirectoryDiscoveryTest.php b/core/tests/Drupal/Tests/Component/Discovery/YamlDirectoryDiscoveryTest.php new file mode 100644 index 0000000..3e46605 --- /dev/null +++ b/core/tests/Drupal/Tests/Component/Discovery/YamlDirectoryDiscoveryTest.php @@ -0,0 +1,157 @@ + [ + 'subdir1' => [ + 'item_1.test.yml' => "id: item1\nname: 'test1 item 1'", + ], + 'subdir2' => [ + 'item_2.test.yml' => "id: item2\nname: 'test1 item 2'", + ], + ], + 'test_2' => [ + 'subdir1' => [ + 'item_3.test.yml' => "id: item3\nname: 'test2 item 3'", + ], + 'subdir2' => [], + ], + 'test_3' => [], + 'test_4' => [ + 'subdir1' => [ + 'item_4.test.yml' => "id: item4\nname: 'test4 item 4'", + 'item_5.test.yml' => "id: item5\nname: 'test4 item 5'", + 'item_6.test.yml' => "id: item6\nname: 'test4 item 6'", + ], + ], + ]); + + // Set up the directories to search. + $directories = [ + // Multiple directories both with valid items. + 'test_1' => [ + vfsStream::url('modules/test_1/subdir1'), + vfsStream::url('modules/test_1/subdir2'), + ], + // The subdir2 directory is empty. + 'test_2' => [ + vfsStream::url('modules/test_2/subdir1'), + vfsStream::url('modules/test_2/subdir2'), + ], + // Directories that do not exist. + 'test_3' => [ + vfsStream::url('modules/test_3/subdir1'), + vfsStream::url('modules/test_3/subdir2'), + ], + // A single directory. + 'test_4' => vfsStream::url('modules/test_4/subdir1'), + ]; + + $discovery = new YamlDirectoryDiscovery($directories, 'test'); + $data = $discovery->findAll(); + + $this->assertSame(['id' => 'item1', 'name' => 'test1 item 1', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_1/subdir1/item_1.test.yml'], $data['test_1']['item1']); + $this->assertSame(['id' => 'item2', 'name' => 'test1 item 2', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_1/subdir2/item_2.test.yml'], $data['test_1']['item2']); + $this->assertCount(2, $data['test_1']); + + $this->assertSame(['id' => 'item3', 'name' => 'test2 item 3', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_2/subdir1/item_3.test.yml'], $data['test_2']['item3']); + $this->assertCount(1, $data['test_2']); + + $this->assertTrue(empty($data['test_3']), 'test_3 provides 0 items'); + + $this->assertSame(['id' => 'item4', 'name' => 'test4 item 4', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_4/subdir1/item_4.test.yml'], $data['test_4']['item4']); + $this->assertSame(['id' => 'item5', 'name' => 'test4 item 5', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_4/subdir1/item_5.test.yml'], $data['test_4']['item5']); + $this->assertSame(['id' => 'item6', 'name' => 'test4 item 6', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_4/subdir1/item_6.test.yml'], $data['test_4']['item6']); + $this->assertCount(3, $data['test_4']); + } + + /** + * Tests YAML directory discovery with an alternate ID key. + * + * @covers ::findAll + */ + public function testDiscoveryAlternateId() { + vfsStream::setup('modules', NULL, [ + 'test_1' => [ + 'item_1.test.yml' => "alt_id: item1\nid: ignored", + ], + ]); + + // Set up the directories to search. + $directories = ['test_1' => vfsStream::url('modules/test_1')]; + + $discovery = new YamlDirectoryDiscovery($directories, 'test', 'alt_id'); + $data = $discovery->findAll(); + + $this->assertSame(['alt_id' => 'item1', 'id' => 'ignored', YamlDirectoryDiscovery::FILE_KEY => 'vfs://modules/test_1/item_1.test.yml'], $data['test_1']['item1']); + $this->assertCount(1, $data['test_1']); + } + + /** + * Tests YAML directory discovery with a missing ID key. + * + * @covers ::findAll + * @covers ::getIdentifier + */ + public function testDiscoveryNoIdException() { + $this->setExpectedException(DiscoveryException::class, 'The vfs://modules/test_1/item_1.test.yml contains no data in the identifier key \'id\''); + vfsStream::setup('modules', NULL, [ + 'test_1' => [ + 'item_1.test.yml' => "", + ], + ]); + + // Set up the directories to search. + $directories = ['test_1' => vfsStream::url('modules/test_1')]; + + $discovery = new YamlDirectoryDiscovery($directories, 'test'); + $discovery->findAll(); + } + + /** + * Tests YAML directory discovery with invalid YAML. + * + * @covers ::findAll + */ + public function testDiscoveryInvalidYamlException() { + $this->setExpectedException(DiscoveryException::class, 'The vfs://modules/test_1/item_1.test.yml contains invalid YAML'); + vfsStream::setup('modules', NULL, [ + 'test_1' => [ + 'item_1.test.yml' => "id: invalid\nfoo : [bar}", + ], + ]); + + // Set up the directories to search. + $directories = ['test_1' => vfsStream::url('modules/test_1')]; + + $discovery = new YamlDirectoryDiscovery($directories, 'test'); + $discovery->findAll(); + } + +} diff --git a/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php b/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php index 22b745b..4a4c52a 100644 --- a/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php +++ b/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php @@ -10,9 +10,9 @@ use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher; use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\EventDispatcher\Tests\AbstractEventDispatcherTest; use Symfony\Component\EventDispatcher\Tests\CallableClass; use Symfony\Component\EventDispatcher\Tests\TestEventListener; +use Symfony\Component\EventDispatcher\Tests\ContainerAwareEventDispatcherTest as SymfonyContainerAwareEventDispatcherTest; /** * Unit tests for the ContainerAwareEventDispatcher. @@ -27,7 +27,7 @@ * * @group EventDispatcher */ -class ContainerAwareEventDispatcherTest extends AbstractEventDispatcherTest +class ContainerAwareEventDispatcherTest extends SymfonyContainerAwareEventDispatcherTest { protected function createEventDispatcher() { @@ -175,4 +175,11 @@ public function testRemoveService() $otherService = $container->get('other_listener_service'); $this->assertTrue($otherService->preFooInvoked); } -} + + public function testGetListenerPriority() + { + // Override the parent test as our implementation doesn't define + // getListenerPriority(). + } + + } diff --git a/core/tests/Drupal/Tests/Component/FileSystem/RegexDirectoryIteratorTest.php b/core/tests/Drupal/Tests/Component/FileSystem/RegexDirectoryIteratorTest.php new file mode 100644 index 0000000..b4b75c8 --- /dev/null +++ b/core/tests/Drupal/Tests/Component/FileSystem/RegexDirectoryIteratorTest.php @@ -0,0 +1,114 @@ +getFilename(); + }, array_values(iterator_to_array($iterator))); + + $this->assertSame($expected, $file_list); + } + + /** + * Provider for self::testRegexDirectoryIterator(). + */ + public function providerTestRegexDirectoryIterator() { + return [ + [ + [ + '1.yml' => '', + ], + '/\.yml$/', + [ + '1.yml', + ], + ], + [ + [ + '1.yml' => '', + '2.yml' => '', + '3.txt' => '', + ], + '/\.yml$/', + [ + '1.yml', + '2.yml', + ], + ], + [ + [ + '1.yml' => '', + '2.yml' => '', + '3.txt' => '', + ], + '/\.txt/', + [ + '3.txt', + ], + ], + [ + [ + '1.yml' => '', + // Ensure we don't recurse in directories even if that match the + // regex. + '2.yml' => [ + '3.yml' => '', + '4.yml' => '', + ], + '3.txt' => '', + ], + '/\.yml$/', + [ + '1.yml', + ], + ], + [ + [ + '1.yml' => '', + '2.yml' => '', + '3.txt' => '', + ], + '/^\d/', + [ + '1.yml', + '2.yml', + '3.txt' + ], + ], + [ + [ + '1.yml' => '', + '2.yml' => '', + '3.txt' => '', + ], + '/^\D/', + [], + ], + ]; + } + +} diff --git a/core/tests/Drupal/Tests/Component/Transliteration/PhpTransliterationTest.php b/core/tests/Drupal/Tests/Component/Transliteration/PhpTransliterationTest.php index de7f428..470d255 100644 --- a/core/tests/Drupal/Tests/Component/Transliteration/PhpTransliterationTest.php +++ b/core/tests/Drupal/Tests/Component/Transliteration/PhpTransliterationTest.php @@ -118,7 +118,7 @@ public function providerTestPhpTransliteration() { // http://www.unicode.org/charts/PDF/U1400.pdf $four_byte = html_entity_decode('ᐑ', ENT_NOQUOTES, 'UTF-8'); // These are two Gothic alphabet letters. See - // http://en.wikipedia.org/wiki/Gothic_alphabet + // http://wikipedia.org/wiki/Gothic_alphabet // They are not in our tables, but should at least give us '?' (unknown). $five_byte = html_entity_decode('𐌰𐌸', ENT_NOQUOTES, 'UTF-8'); diff --git a/core/tests/Drupal/Tests/Component/Utility/NumberTest.php b/core/tests/Drupal/Tests/Component/Utility/NumberTest.php index bce3873..786abec 100644 --- a/core/tests/Drupal/Tests/Component/Utility/NumberTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/NumberTest.php @@ -3,8 +3,6 @@ /** * @file * Contains \Drupal\Tests\Component\Utility\NumberTest. - * - * @see \Drupal\Component\Utility\Number */ namespace Drupal\Tests\Component\Utility; @@ -18,6 +16,8 @@ * @group Utility * * @coversDefaultClass \Drupal\Component\Utility\Number + * + * @see \Drupal\Component\Utility\Number */ class NumberTest extends UnitTestCase { diff --git a/core/tests/Drupal/Tests/Component/Utility/RectangleTest.php b/core/tests/Drupal/Tests/Component/Utility/RectangleTest.php new file mode 100644 index 0000000..bb01113 --- /dev/null +++ b/core/tests/Drupal/Tests/Component/Utility/RectangleTest.php @@ -0,0 +1,4333 @@ +rotate($angle); + $this->assertEquals($exp_width, $rect->getBoundingWidth()); + $this->assertEquals($exp_height, $rect->getBoundingHeight()); + } + + /** + * Provides data for image dimension rotation tests. + * + * This dataset sample was generated by running on PHP 5.5 the function below + * - first, for all integer rotation angles (-360 to 360) on a rectangle + * 40x20; + * - second, for 500 random float rotation angle in the range -360 to 360 on + * a rectangle 40x20; + * - third, on 1000 rectangles of random WxH rotated to a random float angle + * in the range -360 to 360 + * - fourth, on 2000 rectangles of random WxH rotated to a random integer + * angle multiple of 30 degrees in the range -360 to 360 (which is the most + * tricky case). + * Using the GD toolkit operations gives us true data coming from the GD + * library that can be used to match against the Rectangle class under test. + * @code + * protected function rotateResults($width, $height, $angle, &$new_width, &$new_height) { + * $image = \Drupal::service('image.factory')->get(NULL, 'gd'); + * $image->createNew($width, $height); + * $old_res = $image->getToolkit()->getResource(); + * $image->rotate($angle); + * $new_width = $image->getWidth(); + * $new_height = $image->getHeight(); + * if (is_resource($old_res)) { + * imagedestroy($old_res); + * } + * } + * @endcode + * + * @return array[] + * A simple array of simple arrays, each having the following elements: + * - original image width + * - original image height + * - rotation angle in degrees + * - expected image width after rotation + * - expected image height after rotation + * + * @see testRotateDimensions() + */ + public function providerPhp55RotateDimensions() { + return [ + [ 40, 20, -360, 40, 20 ], + [ 40, 20, -359, 39, 20 ], + [ 40, 20, -358, 40, 21 ], + [ 40, 20, -357, 40, 22 ], + [ 40, 20, -356, 40, 22 ], + [ 40, 20, -355, 41, 23 ], + [ 40, 20, -354, 41, 24 ], + [ 40, 20, -353, 41, 24 ], + [ 40, 20, -352, 42, 25 ], + [ 40, 20, -351, 42, 26 ], + [ 40, 20, -350, 42, 26 ], + [ 40, 20, -349, 43, 27 ], + [ 40, 20, -348, 43, 28 ], + [ 40, 20, -347, 42, 27 ], + [ 40, 20, -346, 43, 28 ], + [ 40, 20, -345, 43, 29 ], + [ 40, 20, -344, 44, 30 ], + [ 40, 20, -343, 44, 30 ], + [ 40, 20, -342, 44, 31 ], + [ 40, 20, -341, 44, 32 ], + [ 40, 20, -340, 44, 32 ], + [ 40, 20, -339, 44, 33 ], + [ 40, 20, -338, 44, 33 ], + [ 40, 20, -337, 44, 33 ], + [ 40, 20, -336, 44, 34 ], + [ 40, 20, -335, 44, 34 ], + [ 40, 20, -334, 44, 35 ], + [ 40, 20, -333, 44, 36 ], + [ 40, 20, -332, 44, 36 ], + [ 40, 20, -331, 44, 36 ], + [ 40, 20, -330, 44, 36 ], + [ 40, 20, -329, 44, 37 ], + [ 40, 20, -328, 44, 38 ], + [ 40, 20, -327, 44, 38 ], + [ 40, 20, -326, 44, 39 ], + [ 40, 20, -325, 43, 38 ], + [ 40, 20, -324, 44, 39 ], + [ 40, 20, -323, 43, 40 ], + [ 40, 20, -322, 43, 40 ], + [ 40, 20, -321, 44, 41 ], + [ 40, 20, -320, 43, 40 ], + [ 40, 20, -319, 43, 41 ], + [ 40, 20, -318, 42, 41 ], + [ 40, 20, -317, 43, 42 ], + [ 40, 20, -316, 42, 41 ], + [ 40, 20, -315, 42, 42 ], + [ 40, 20, -314, 41, 42 ], + [ 40, 20, -313, 42, 43 ], + [ 40, 20, -312, 41, 42 ], + [ 40, 20, -311, 41, 43 ], + [ 40, 20, -310, 40, 43 ], + [ 40, 20, -309, 41, 44 ], + [ 40, 20, -308, 40, 43 ], + [ 40, 20, -307, 40, 43 ], + [ 40, 20, -306, 39, 44 ], + [ 40, 20, -305, 38, 43 ], + [ 40, 20, -304, 39, 44 ], + [ 40, 20, -303, 38, 44 ], + [ 40, 20, -302, 38, 44 ], + [ 40, 20, -301, 37, 44 ], + [ 40, 20, -300, 36, 44 ], + [ 40, 20, -299, 36, 44 ], + [ 40, 20, -298, 36, 44 ], + [ 40, 20, -297, 36, 44 ], + [ 40, 20, -296, 35, 44 ], + [ 40, 20, -295, 34, 44 ], + [ 40, 20, -294, 34, 44 ], + [ 40, 20, -293, 33, 44 ], + [ 40, 20, -292, 33, 44 ], + [ 40, 20, -291, 33, 44 ], + [ 40, 20, -290, 32, 44 ], + [ 40, 20, -289, 32, 44 ], + [ 40, 20, -288, 31, 44 ], + [ 40, 20, -287, 30, 44 ], + [ 40, 20, -286, 30, 44 ], + [ 40, 20, -285, 29, 43 ], + [ 40, 20, -284, 28, 43 ], + [ 40, 20, -283, 27, 42 ], + [ 40, 20, -282, 28, 43 ], + [ 40, 20, -281, 27, 43 ], + [ 40, 20, -280, 26, 42 ], + [ 40, 20, -279, 26, 42 ], + [ 40, 20, -278, 25, 42 ], + [ 40, 20, -277, 24, 41 ], + [ 40, 20, -276, 24, 41 ], + [ 40, 20, -275, 23, 41 ], + [ 40, 20, -274, 22, 40 ], + [ 40, 20, -273, 22, 40 ], + [ 40, 20, -272, 21, 40 ], + [ 40, 20, -271, 20, 39 ], + [ 40, 20, -270, 20, 40 ], + [ 40, 20, -269, 20, 39 ], + [ 40, 20, -268, 21, 39 ], + [ 40, 20, -267, 22, 39 ], + [ 40, 20, -266, 22, 39 ], + [ 40, 20, -265, 23, 40 ], + [ 40, 20, -264, 24, 40 ], + [ 40, 20, -263, 24, 40 ], + [ 40, 20, -262, 25, 41 ], + [ 40, 20, -261, 26, 41 ], + [ 40, 20, -260, 26, 41 ], + [ 40, 20, -259, 27, 42 ], + [ 40, 20, -258, 28, 42 ], + [ 40, 20, -257, 27, 41 ], + [ 40, 20, -256, 28, 42 ], + [ 40, 20, -255, 29, 42 ], + [ 40, 20, -254, 30, 43 ], + [ 40, 20, -253, 30, 43 ], + [ 40, 20, -252, 31, 43 ], + [ 40, 20, -251, 32, 43 ], + [ 40, 20, -250, 32, 43 ], + [ 40, 20, -249, 33, 43 ], + [ 40, 20, -248, 33, 43 ], + [ 40, 20, -247, 33, 43 ], + [ 40, 20, -246, 34, 43 ], + [ 40, 20, -245, 34, 43 ], + [ 40, 20, -244, 35, 43 ], + [ 40, 20, -243, 36, 43 ], + [ 40, 20, -242, 36, 43 ], + [ 40, 20, -241, 36, 43 ], + [ 40, 20, -240, 36, 43 ], + [ 40, 20, -239, 37, 43 ], + [ 40, 20, -238, 38, 43 ], + [ 40, 20, -237, 38, 43 ], + [ 40, 20, -236, 39, 43 ], + [ 40, 20, -235, 38, 42 ], + [ 40, 20, -234, 39, 43 ], + [ 40, 20, -233, 40, 42 ], + [ 40, 20, -232, 40, 42 ], + [ 40, 20, -231, 41, 43 ], + [ 40, 20, -230, 40, 42 ], + [ 40, 20, -229, 41, 42 ], + [ 40, 20, -228, 41, 41 ], + [ 40, 20, -227, 42, 42 ], + [ 40, 20, -226, 41, 41 ], + [ 40, 20, -225, 42, 41 ], + [ 40, 20, -224, 42, 40 ], + [ 40, 20, -223, 43, 41 ], + [ 40, 20, -222, 42, 40 ], + [ 40, 20, -221, 43, 40 ], + [ 40, 20, -220, 43, 39 ], + [ 40, 20, -219, 44, 40 ], + [ 40, 20, -218, 43, 39 ], + [ 40, 20, -217, 43, 39 ], + [ 40, 20, -216, 44, 38 ], + [ 40, 20, -215, 43, 37 ], + [ 40, 20, -214, 44, 38 ], + [ 40, 20, -213, 44, 37 ], + [ 40, 20, -212, 44, 37 ], + [ 40, 20, -211, 44, 36 ], + [ 40, 20, -210, 44, 35 ], + [ 40, 20, -209, 44, 35 ], + [ 40, 20, -208, 44, 35 ], + [ 40, 20, -207, 44, 35 ], + [ 40, 20, -206, 44, 34 ], + [ 40, 20, -205, 44, 33 ], + [ 40, 20, -204, 44, 33 ], + [ 40, 20, -203, 44, 32 ], + [ 40, 20, -202, 44, 32 ], + [ 40, 20, -201, 44, 32 ], + [ 40, 20, -200, 44, 31 ], + [ 40, 20, -199, 44, 31 ], + [ 40, 20, -198, 44, 30 ], + [ 40, 20, -197, 44, 29 ], + [ 40, 20, -196, 44, 29 ], + [ 40, 20, -195, 43, 28 ], + [ 40, 20, -194, 43, 27 ], + [ 40, 20, -193, 42, 26 ], + [ 40, 20, -192, 43, 27 ], + [ 40, 20, -191, 43, 26 ], + [ 40, 20, -190, 42, 25 ], + [ 40, 20, -189, 42, 25 ], + [ 40, 20, -188, 42, 24 ], + [ 40, 20, -187, 41, 23 ], + [ 40, 20, -186, 41, 23 ], + [ 40, 20, -185, 41, 22 ], + [ 40, 20, -184, 40, 21 ], + [ 40, 20, -183, 40, 21 ], + [ 40, 20, -182, 40, 20 ], + [ 40, 20, -181, 39, 19 ], + [ 40, 20, -180, 40, 20 ], + [ 40, 20, -179, 39, 19 ], + [ 40, 20, -178, 39, 20 ], + [ 40, 20, -177, 39, 21 ], + [ 40, 20, -176, 39, 21 ], + [ 40, 20, -175, 40, 22 ], + [ 40, 20, -174, 40, 23 ], + [ 40, 20, -173, 40, 23 ], + [ 40, 20, -172, 41, 24 ], + [ 40, 20, -171, 41, 25 ], + [ 40, 20, -170, 41, 25 ], + [ 40, 20, -169, 42, 26 ], + [ 40, 20, -168, 42, 27 ], + [ 40, 20, -167, 41, 26 ], + [ 40, 20, -166, 42, 27 ], + [ 40, 20, -165, 42, 28 ], + [ 40, 20, -164, 43, 29 ], + [ 40, 20, -163, 43, 29 ], + [ 40, 20, -162, 43, 30 ], + [ 40, 20, -161, 43, 31 ], + [ 40, 20, -160, 43, 31 ], + [ 40, 20, -159, 43, 32 ], + [ 40, 20, -158, 43, 32 ], + [ 40, 20, -157, 43, 32 ], + [ 40, 20, -156, 43, 33 ], + [ 40, 20, -155, 43, 33 ], + [ 40, 20, -154, 43, 34 ], + [ 40, 20, -153, 43, 35 ], + [ 40, 20, -152, 43, 35 ], + [ 40, 20, -151, 43, 35 ], + [ 40, 20, -150, 43, 36 ], + [ 40, 20, -149, 43, 36 ], + [ 40, 20, -148, 43, 37 ], + [ 40, 20, -147, 43, 37 ], + [ 40, 20, -146, 43, 38 ], + [ 40, 20, -145, 42, 37 ], + [ 40, 20, -144, 43, 38 ], + [ 40, 20, -143, 42, 39 ], + [ 40, 20, -142, 42, 39 ], + [ 40, 20, -141, 43, 40 ], + [ 40, 20, -140, 42, 39 ], + [ 40, 20, -139, 42, 40 ], + [ 40, 20, -138, 41, 40 ], + [ 40, 20, -137, 42, 41 ], + [ 40, 20, -136, 41, 40 ], + [ 40, 20, -135, 41, 41 ], + [ 40, 20, -134, 40, 41 ], + [ 40, 20, -133, 41, 42 ], + [ 40, 20, -132, 40, 41 ], + [ 40, 20, -131, 40, 42 ], + [ 40, 20, -130, 39, 42 ], + [ 40, 20, -129, 40, 43 ], + [ 40, 20, -128, 39, 42 ], + [ 40, 20, -127, 39, 42 ], + [ 40, 20, -126, 38, 43 ], + [ 40, 20, -125, 37, 42 ], + [ 40, 20, -124, 38, 43 ], + [ 40, 20, -123, 37, 43 ], + [ 40, 20, -122, 37, 43 ], + [ 40, 20, -121, 36, 43 ], + [ 40, 20, -120, 36, 43 ], + [ 40, 20, -119, 35, 43 ], + [ 40, 20, -118, 35, 43 ], + [ 40, 20, -117, 35, 43 ], + [ 40, 20, -116, 34, 43 ], + [ 40, 20, -115, 33, 43 ], + [ 40, 20, -114, 33, 43 ], + [ 40, 20, -113, 32, 43 ], + [ 40, 20, -112, 32, 43 ], + [ 40, 20, -111, 32, 43 ], + [ 40, 20, -110, 31, 43 ], + [ 40, 20, -109, 31, 43 ], + [ 40, 20, -108, 30, 43 ], + [ 40, 20, -107, 29, 43 ], + [ 40, 20, -106, 29, 43 ], + [ 40, 20, -105, 28, 42 ], + [ 40, 20, -104, 27, 42 ], + [ 40, 20, -103, 26, 41 ], + [ 40, 20, -102, 27, 42 ], + [ 40, 20, -101, 26, 42 ], + [ 40, 20, -100, 25, 41 ], + [ 40, 20, -99, 25, 41 ], + [ 40, 20, -98, 24, 41 ], + [ 40, 20, -97, 23, 40 ], + [ 40, 20, -96, 23, 40 ], + [ 40, 20, -95, 22, 40 ], + [ 40, 20, -94, 21, 39 ], + [ 40, 20, -93, 21, 39 ], + [ 40, 20, -92, 20, 39 ], + [ 40, 20, -91, 19, 39 ], + [ 40, 20, -90, 20, 40 ], + [ 40, 20, -89, 19, 39 ], + [ 40, 20, -88, 20, 40 ], + [ 40, 20, -87, 21, 40 ], + [ 40, 20, -86, 21, 40 ], + [ 40, 20, -85, 22, 41 ], + [ 40, 20, -84, 23, 41 ], + [ 40, 20, -83, 23, 41 ], + [ 40, 20, -82, 24, 42 ], + [ 40, 20, -81, 25, 42 ], + [ 40, 20, -80, 25, 42 ], + [ 40, 20, -79, 26, 43 ], + [ 40, 20, -78, 27, 43 ], + [ 40, 20, -77, 26, 42 ], + [ 40, 20, -76, 27, 43 ], + [ 40, 20, -75, 28, 43 ], + [ 40, 20, -74, 29, 44 ], + [ 40, 20, -73, 29, 44 ], + [ 40, 20, -72, 30, 44 ], + [ 40, 20, -71, 31, 44 ], + [ 40, 20, -70, 31, 44 ], + [ 40, 20, -69, 32, 44 ], + [ 40, 20, -68, 32, 44 ], + [ 40, 20, -67, 32, 44 ], + [ 40, 20, -66, 33, 44 ], + [ 40, 20, -65, 33, 44 ], + [ 40, 20, -64, 34, 44 ], + [ 40, 20, -63, 35, 44 ], + [ 40, 20, -62, 35, 44 ], + [ 40, 20, -61, 35, 44 ], + [ 40, 20, -60, 35, 44 ], + [ 40, 20, -59, 36, 44 ], + [ 40, 20, -58, 37, 44 ], + [ 40, 20, -57, 37, 44 ], + [ 40, 20, -56, 38, 44 ], + [ 40, 20, -55, 37, 43 ], + [ 40, 20, -54, 38, 44 ], + [ 40, 20, -53, 39, 43 ], + [ 40, 20, -52, 39, 43 ], + [ 40, 20, -51, 40, 44 ], + [ 40, 20, -50, 39, 43 ], + [ 40, 20, -49, 40, 43 ], + [ 40, 20, -48, 40, 42 ], + [ 40, 20, -47, 41, 43 ], + [ 40, 20, -46, 40, 42 ], + [ 40, 20, -45, 41, 42 ], + [ 40, 20, -44, 41, 41 ], + [ 40, 20, -43, 42, 42 ], + [ 40, 20, -42, 41, 41 ], + [ 40, 20, -41, 42, 41 ], + [ 40, 20, -40, 42, 40 ], + [ 40, 20, -39, 43, 41 ], + [ 40, 20, -38, 42, 40 ], + [ 40, 20, -37, 42, 40 ], + [ 40, 20, -36, 43, 39 ], + [ 40, 20, -35, 42, 38 ], + [ 40, 20, -34, 43, 39 ], + [ 40, 20, -33, 43, 38 ], + [ 40, 20, -32, 43, 38 ], + [ 40, 20, -31, 43, 37 ], + [ 40, 20, -30, 43, 37 ], + [ 40, 20, -29, 43, 36 ], + [ 40, 20, -28, 43, 36 ], + [ 40, 20, -27, 43, 36 ], + [ 40, 20, -26, 43, 35 ], + [ 40, 20, -25, 43, 34 ], + [ 40, 20, -24, 43, 34 ], + [ 40, 20, -23, 43, 33 ], + [ 40, 20, -22, 43, 33 ], + [ 40, 20, -21, 43, 33 ], + [ 40, 20, -20, 43, 32 ], + [ 40, 20, -19, 43, 32 ], + [ 40, 20, -18, 43, 31 ], + [ 40, 20, -17, 43, 30 ], + [ 40, 20, -16, 43, 30 ], + [ 40, 20, -15, 42, 29 ], + [ 40, 20, -14, 42, 28 ], + [ 40, 20, -13, 41, 27 ], + [ 40, 20, -12, 42, 28 ], + [ 40, 20, -11, 42, 27 ], + [ 40, 20, -10, 41, 26 ], + [ 40, 20, -9, 41, 26 ], + [ 40, 20, -8, 41, 25 ], + [ 40, 20, -7, 40, 24 ], + [ 40, 20, -6, 40, 24 ], + [ 40, 20, -5, 40, 23 ], + [ 40, 20, -4, 39, 22 ], + [ 40, 20, -3, 39, 22 ], + [ 40, 20, -2, 39, 21 ], + [ 40, 20, -1, 39, 20 ], + [ 40, 20, 0, 40, 20 ], + [ 40, 20, 1, 39, 20 ], + [ 40, 20, 2, 40, 21 ], + [ 40, 20, 3, 40, 22 ], + [ 40, 20, 4, 40, 22 ], + [ 40, 20, 5, 41, 23 ], + [ 40, 20, 6, 41, 24 ], + [ 40, 20, 7, 41, 24 ], + [ 40, 20, 8, 42, 25 ], + [ 40, 20, 9, 42, 26 ], + [ 40, 20, 10, 42, 26 ], + [ 40, 20, 11, 43, 27 ], + [ 40, 20, 12, 43, 28 ], + [ 40, 20, 13, 42, 27 ], + [ 40, 20, 14, 43, 28 ], + [ 40, 20, 15, 43, 29 ], + [ 40, 20, 16, 44, 30 ], + [ 40, 20, 17, 44, 30 ], + [ 40, 20, 18, 44, 31 ], + [ 40, 20, 19, 44, 32 ], + [ 40, 20, 20, 44, 32 ], + [ 40, 20, 21, 44, 33 ], + [ 40, 20, 22, 44, 33 ], + [ 40, 20, 23, 44, 33 ], + [ 40, 20, 24, 44, 34 ], + [ 40, 20, 25, 44, 34 ], + [ 40, 20, 26, 44, 35 ], + [ 40, 20, 27, 44, 36 ], + [ 40, 20, 28, 44, 36 ], + [ 40, 20, 29, 44, 36 ], + [ 40, 20, 30, 44, 36 ], + [ 40, 20, 31, 44, 37 ], + [ 40, 20, 32, 44, 38 ], + [ 40, 20, 33, 44, 38 ], + [ 40, 20, 34, 44, 39 ], + [ 40, 20, 35, 43, 38 ], + [ 40, 20, 36, 44, 39 ], + [ 40, 20, 37, 43, 40 ], + [ 40, 20, 38, 43, 40 ], + [ 40, 20, 39, 44, 41 ], + [ 40, 20, 40, 43, 40 ], + [ 40, 20, 41, 43, 41 ], + [ 40, 20, 42, 42, 41 ], + [ 40, 20, 43, 43, 42 ], + [ 40, 20, 44, 42, 41 ], + [ 40, 20, 45, 42, 42 ], + [ 40, 20, 46, 41, 42 ], + [ 40, 20, 47, 42, 43 ], + [ 40, 20, 48, 41, 42 ], + [ 40, 20, 49, 41, 43 ], + [ 40, 20, 50, 40, 43 ], + [ 40, 20, 51, 41, 44 ], + [ 40, 20, 52, 40, 43 ], + [ 40, 20, 53, 40, 43 ], + [ 40, 20, 54, 39, 44 ], + [ 40, 20, 55, 38, 43 ], + [ 40, 20, 56, 39, 44 ], + [ 40, 20, 57, 38, 44 ], + [ 40, 20, 58, 38, 44 ], + [ 40, 20, 59, 37, 44 ], + [ 40, 20, 60, 36, 44 ], + [ 40, 20, 61, 36, 44 ], + [ 40, 20, 62, 36, 44 ], + [ 40, 20, 63, 36, 44 ], + [ 40, 20, 64, 35, 44 ], + [ 40, 20, 65, 34, 44 ], + [ 40, 20, 66, 34, 44 ], + [ 40, 20, 67, 33, 44 ], + [ 40, 20, 68, 33, 44 ], + [ 40, 20, 69, 33, 44 ], + [ 40, 20, 70, 32, 44 ], + [ 40, 20, 71, 32, 44 ], + [ 40, 20, 72, 31, 44 ], + [ 40, 20, 73, 30, 44 ], + [ 40, 20, 74, 30, 44 ], + [ 40, 20, 75, 29, 43 ], + [ 40, 20, 76, 28, 43 ], + [ 40, 20, 77, 27, 42 ], + [ 40, 20, 78, 28, 43 ], + [ 40, 20, 79, 27, 43 ], + [ 40, 20, 80, 26, 42 ], + [ 40, 20, 81, 26, 42 ], + [ 40, 20, 82, 25, 42 ], + [ 40, 20, 83, 24, 41 ], + [ 40, 20, 84, 24, 41 ], + [ 40, 20, 85, 23, 41 ], + [ 40, 20, 86, 22, 40 ], + [ 40, 20, 87, 22, 40 ], + [ 40, 20, 88, 21, 40 ], + [ 40, 20, 89, 20, 39 ], + [ 40, 20, 90, 20, 40 ], + [ 40, 20, 91, 20, 39 ], + [ 40, 20, 92, 21, 39 ], + [ 40, 20, 93, 22, 39 ], + [ 40, 20, 94, 22, 39 ], + [ 40, 20, 95, 23, 40 ], + [ 40, 20, 96, 24, 40 ], + [ 40, 20, 97, 24, 40 ], + [ 40, 20, 98, 25, 41 ], + [ 40, 20, 99, 26, 41 ], + [ 40, 20, 100, 26, 41 ], + [ 40, 20, 101, 27, 42 ], + [ 40, 20, 102, 28, 42 ], + [ 40, 20, 103, 27, 41 ], + [ 40, 20, 104, 28, 42 ], + [ 40, 20, 105, 29, 42 ], + [ 40, 20, 106, 30, 43 ], + [ 40, 20, 107, 30, 43 ], + [ 40, 20, 108, 31, 43 ], + [ 40, 20, 109, 32, 43 ], + [ 40, 20, 110, 32, 43 ], + [ 40, 20, 111, 33, 43 ], + [ 40, 20, 112, 33, 43 ], + [ 40, 20, 113, 33, 43 ], + [ 40, 20, 114, 34, 43 ], + [ 40, 20, 115, 34, 43 ], + [ 40, 20, 116, 35, 43 ], + [ 40, 20, 117, 36, 43 ], + [ 40, 20, 118, 36, 43 ], + [ 40, 20, 119, 36, 43 ], + [ 40, 20, 120, 36, 43 ], + [ 40, 20, 121, 37, 43 ], + [ 40, 20, 122, 38, 43 ], + [ 40, 20, 123, 38, 43 ], + [ 40, 20, 124, 39, 43 ], + [ 40, 20, 125, 38, 42 ], + [ 40, 20, 126, 39, 43 ], + [ 40, 20, 127, 40, 42 ], + [ 40, 20, 128, 40, 42 ], + [ 40, 20, 129, 41, 43 ], + [ 40, 20, 130, 40, 42 ], + [ 40, 20, 131, 41, 42 ], + [ 40, 20, 132, 41, 41 ], + [ 40, 20, 133, 42, 42 ], + [ 40, 20, 134, 41, 41 ], + [ 40, 20, 135, 42, 41 ], + [ 40, 20, 136, 42, 40 ], + [ 40, 20, 137, 43, 41 ], + [ 40, 20, 138, 42, 40 ], + [ 40, 20, 139, 43, 40 ], + [ 40, 20, 140, 43, 39 ], + [ 40, 20, 141, 44, 40 ], + [ 40, 20, 142, 43, 39 ], + [ 40, 20, 143, 43, 39 ], + [ 40, 20, 144, 44, 38 ], + [ 40, 20, 145, 43, 37 ], + [ 40, 20, 146, 44, 38 ], + [ 40, 20, 147, 44, 37 ], + [ 40, 20, 148, 44, 37 ], + [ 40, 20, 149, 44, 36 ], + [ 40, 20, 150, 44, 35 ], + [ 40, 20, 151, 44, 35 ], + [ 40, 20, 152, 44, 35 ], + [ 40, 20, 153, 44, 35 ], + [ 40, 20, 154, 44, 34 ], + [ 40, 20, 155, 44, 33 ], + [ 40, 20, 156, 44, 33 ], + [ 40, 20, 157, 44, 32 ], + [ 40, 20, 158, 44, 32 ], + [ 40, 20, 159, 44, 32 ], + [ 40, 20, 160, 44, 31 ], + [ 40, 20, 161, 44, 31 ], + [ 40, 20, 162, 44, 30 ], + [ 40, 20, 163, 44, 29 ], + [ 40, 20, 164, 44, 29 ], + [ 40, 20, 165, 43, 28 ], + [ 40, 20, 166, 43, 27 ], + [ 40, 20, 167, 42, 26 ], + [ 40, 20, 168, 43, 27 ], + [ 40, 20, 169, 43, 26 ], + [ 40, 20, 170, 42, 25 ], + [ 40, 20, 171, 42, 25 ], + [ 40, 20, 172, 42, 24 ], + [ 40, 20, 173, 41, 23 ], + [ 40, 20, 174, 41, 23 ], + [ 40, 20, 175, 41, 22 ], + [ 40, 20, 176, 40, 21 ], + [ 40, 20, 177, 40, 21 ], + [ 40, 20, 178, 40, 20 ], + [ 40, 20, 179, 39, 19 ], + [ 40, 20, 180, 40, 20 ], + [ 40, 20, 181, 39, 19 ], + [ 40, 20, 182, 39, 20 ], + [ 40, 20, 183, 39, 21 ], + [ 40, 20, 184, 39, 21 ], + [ 40, 20, 185, 40, 22 ], + [ 40, 20, 186, 40, 23 ], + [ 40, 20, 187, 40, 23 ], + [ 40, 20, 188, 41, 24 ], + [ 40, 20, 189, 41, 25 ], + [ 40, 20, 190, 41, 25 ], + [ 40, 20, 191, 42, 26 ], + [ 40, 20, 192, 42, 27 ], + [ 40, 20, 193, 41, 26 ], + [ 40, 20, 194, 42, 27 ], + [ 40, 20, 195, 42, 28 ], + [ 40, 20, 196, 43, 29 ], + [ 40, 20, 197, 43, 29 ], + [ 40, 20, 198, 43, 30 ], + [ 40, 20, 199, 43, 31 ], + [ 40, 20, 200, 43, 31 ], + [ 40, 20, 201, 43, 32 ], + [ 40, 20, 202, 43, 32 ], + [ 40, 20, 203, 43, 32 ], + [ 40, 20, 204, 43, 33 ], + [ 40, 20, 205, 43, 33 ], + [ 40, 20, 206, 43, 34 ], + [ 40, 20, 207, 43, 35 ], + [ 40, 20, 208, 43, 35 ], + [ 40, 20, 209, 43, 35 ], + [ 40, 20, 210, 43, 36 ], + [ 40, 20, 211, 43, 36 ], + [ 40, 20, 212, 43, 37 ], + [ 40, 20, 213, 43, 37 ], + [ 40, 20, 214, 43, 38 ], + [ 40, 20, 215, 42, 37 ], + [ 40, 20, 216, 43, 38 ], + [ 40, 20, 217, 42, 39 ], + [ 40, 20, 218, 42, 39 ], + [ 40, 20, 219, 43, 40 ], + [ 40, 20, 220, 42, 39 ], + [ 40, 20, 221, 42, 40 ], + [ 40, 20, 222, 41, 40 ], + [ 40, 20, 223, 42, 41 ], + [ 40, 20, 224, 41, 40 ], + [ 40, 20, 225, 41, 41 ], + [ 40, 20, 226, 40, 41 ], + [ 40, 20, 227, 41, 42 ], + [ 40, 20, 228, 40, 41 ], + [ 40, 20, 229, 40, 42 ], + [ 40, 20, 230, 39, 42 ], + [ 40, 20, 231, 40, 43 ], + [ 40, 20, 232, 39, 42 ], + [ 40, 20, 233, 39, 42 ], + [ 40, 20, 234, 38, 43 ], + [ 40, 20, 235, 37, 42 ], + [ 40, 20, 236, 38, 43 ], + [ 40, 20, 237, 37, 43 ], + [ 40, 20, 238, 37, 43 ], + [ 40, 20, 239, 36, 43 ], + [ 40, 20, 240, 36, 43 ], + [ 40, 20, 241, 35, 43 ], + [ 40, 20, 242, 35, 43 ], + [ 40, 20, 243, 35, 43 ], + [ 40, 20, 244, 34, 43 ], + [ 40, 20, 245, 33, 43 ], + [ 40, 20, 246, 33, 43 ], + [ 40, 20, 247, 32, 43 ], + [ 40, 20, 248, 32, 43 ], + [ 40, 20, 249, 32, 43 ], + [ 40, 20, 250, 31, 43 ], + [ 40, 20, 251, 31, 43 ], + [ 40, 20, 252, 30, 43 ], + [ 40, 20, 253, 29, 43 ], + [ 40, 20, 254, 29, 43 ], + [ 40, 20, 255, 28, 42 ], + [ 40, 20, 256, 27, 42 ], + [ 40, 20, 257, 26, 41 ], + [ 40, 20, 258, 27, 42 ], + [ 40, 20, 259, 26, 42 ], + [ 40, 20, 260, 25, 41 ], + [ 40, 20, 261, 25, 41 ], + [ 40, 20, 262, 24, 41 ], + [ 40, 20, 263, 23, 40 ], + [ 40, 20, 264, 23, 40 ], + [ 40, 20, 265, 22, 40 ], + [ 40, 20, 266, 21, 39 ], + [ 40, 20, 267, 21, 39 ], + [ 40, 20, 268, 20, 39 ], + [ 40, 20, 269, 19, 39 ], + [ 40, 20, 270, 20, 40 ], + [ 40, 20, 271, 19, 39 ], + [ 40, 20, 272, 20, 40 ], + [ 40, 20, 273, 21, 40 ], + [ 40, 20, 274, 21, 40 ], + [ 40, 20, 275, 22, 41 ], + [ 40, 20, 276, 23, 41 ], + [ 40, 20, 277, 23, 41 ], + [ 40, 20, 278, 24, 42 ], + [ 40, 20, 279, 25, 42 ], + [ 40, 20, 280, 25, 42 ], + [ 40, 20, 281, 26, 43 ], + [ 40, 20, 282, 27, 43 ], + [ 40, 20, 283, 26, 42 ], + [ 40, 20, 284, 27, 43 ], + [ 40, 20, 285, 28, 43 ], + [ 40, 20, 286, 29, 44 ], + [ 40, 20, 287, 29, 44 ], + [ 40, 20, 288, 30, 44 ], + [ 40, 20, 289, 31, 44 ], + [ 40, 20, 290, 31, 44 ], + [ 40, 20, 291, 32, 44 ], + [ 40, 20, 292, 32, 44 ], + [ 40, 20, 293, 32, 44 ], + [ 40, 20, 294, 33, 44 ], + [ 40, 20, 295, 33, 44 ], + [ 40, 20, 296, 34, 44 ], + [ 40, 20, 297, 35, 44 ], + [ 40, 20, 298, 35, 44 ], + [ 40, 20, 299, 35, 44 ], + [ 40, 20, 300, 35, 44 ], + [ 40, 20, 301, 36, 44 ], + [ 40, 20, 302, 37, 44 ], + [ 40, 20, 303, 37, 44 ], + [ 40, 20, 304, 38, 44 ], + [ 40, 20, 305, 37, 43 ], + [ 40, 20, 306, 38, 44 ], + [ 40, 20, 307, 39, 43 ], + [ 40, 20, 308, 39, 43 ], + [ 40, 20, 309, 40, 44 ], + [ 40, 20, 310, 39, 43 ], + [ 40, 20, 311, 40, 43 ], + [ 40, 20, 312, 40, 42 ], + [ 40, 20, 313, 41, 43 ], + [ 40, 20, 314, 40, 42 ], + [ 40, 20, 315, 41, 42 ], + [ 40, 20, 316, 41, 41 ], + [ 40, 20, 317, 42, 42 ], + [ 40, 20, 318, 41, 41 ], + [ 40, 20, 319, 42, 41 ], + [ 40, 20, 320, 42, 40 ], + [ 40, 20, 321, 43, 41 ], + [ 40, 20, 322, 42, 40 ], + [ 40, 20, 323, 42, 40 ], + [ 40, 20, 324, 43, 39 ], + [ 40, 20, 325, 42, 38 ], + [ 40, 20, 326, 43, 39 ], + [ 40, 20, 327, 43, 38 ], + [ 40, 20, 328, 43, 38 ], + [ 40, 20, 329, 43, 37 ], + [ 40, 20, 330, 43, 37 ], + [ 40, 20, 331, 43, 36 ], + [ 40, 20, 332, 43, 36 ], + [ 40, 20, 333, 43, 36 ], + [ 40, 20, 334, 43, 35 ], + [ 40, 20, 335, 43, 34 ], + [ 40, 20, 336, 43, 34 ], + [ 40, 20, 337, 43, 33 ], + [ 40, 20, 338, 43, 33 ], + [ 40, 20, 339, 43, 33 ], + [ 40, 20, 340, 43, 32 ], + [ 40, 20, 341, 43, 32 ], + [ 40, 20, 342, 43, 31 ], + [ 40, 20, 343, 43, 30 ], + [ 40, 20, 344, 43, 30 ], + [ 40, 20, 345, 42, 29 ], + [ 40, 20, 346, 42, 28 ], + [ 40, 20, 347, 41, 27 ], + [ 40, 20, 348, 42, 28 ], + [ 40, 20, 349, 42, 27 ], + [ 40, 20, 350, 41, 26 ], + [ 40, 20, 351, 41, 26 ], + [ 40, 20, 352, 41, 25 ], + [ 40, 20, 353, 40, 24 ], + [ 40, 20, 354, 40, 24 ], + [ 40, 20, 355, 40, 23 ], + [ 40, 20, 356, 39, 22 ], + [ 40, 20, 357, 39, 22 ], + [ 40, 20, 358, 39, 21 ], + [ 40, 20, 359, 39, 20 ], + [ 40, 20, 360, 40, 20 ], + [ 40, 20, 110.1, 32, 43 ], + [ 40, 20, 344.9, 42, 29 ], + [ 40, 20, 142.85, 43, 39 ], + [ 40, 20, -106.52, 29, 43 ], + [ 40, 20, -295.78, 35, 45 ], + [ 40, 20, 287.58, 30, 44 ], + [ 40, 20, -3.62, 39, 22 ], + [ 40, 20, -33.96, 43, 39 ], + [ 40, 20, 136.46, 42, 40 ], + [ 40, 20, -293.72, 34, 44 ], + [ 40, 20, 10.71, 43, 27 ], + [ 40, 20, -80.02, 25, 42 ], + [ 40, 20, 258.53, 26, 42 ], + [ 40, 20, -110.41, 31, 43 ], + [ 40, 20, 340.87, 43, 32 ], + [ 40, 20, 347.4, 42, 28 ], + [ 40, 20, -277.97, 25, 42 ], + [ 40, 20, 126.74, 39, 43 ], + [ 40, 20, 70.99, 32, 44 ], + [ 40, 20, 57.54, 38, 44 ], + [ 40, 20, 251.66, 30, 42 ], + [ 40, 20, -91.15, 19, 39 ], + [ 40, 20, 164.92, 43, 28 ], + [ 40, 20, -27.14, 43, 36 ], + [ 40, 20, 230.28, 39, 42 ], + [ 40, 20, 271.37, 19, 39 ], + [ 40, 20, -207.19, 44, 35 ], + [ 40, 20, -58.19, 37, 44 ], + [ 40, 20, -339.34, 44, 33 ], + [ 40, 20, 352.45, 41, 25 ], + [ 40, 20, -245.62, 34, 43 ], + [ 40, 20, 130.77, 41, 42 ], + [ 40, 20, 337.34, 43, 33 ], + [ 40, 20, 257.24, 27, 42 ], + [ 40, 20, -335.75, 44, 34 ], + [ 40, 20, -318.44, 42, 41 ], + [ 40, 20, 184.82, 40, 22 ], + [ 40, 20, 20.64, 44, 33 ], + [ 40, 20, 7.6, 42, 25 ], + [ 40, 20, -38.72, 43, 41 ], + [ 40, 20, 86.92, 22, 40 ], + [ 40, 20, -341.69, 43, 31 ], + [ 40, 20, 241.26, 36, 44 ], + [ 40, 20, -14.56, 42, 29 ], + [ 40, 20, -92.1, 20, 39 ], + [ 40, 20, 222.12, 41, 40 ], + [ 40, 20, -27.16, 43, 36 ], + [ 40, 20, -10.06, 41, 26 ], + [ 40, 20, -11.15, 42, 27 ], + [ 40, 20, -316.18, 42, 41 ], + [ 40, 20, -312.53, 42, 43 ], + [ 40, 20, -119.49, 35, 43 ], + [ 40, 20, -47.32, 41, 43 ], + [ 40, 20, 212.4, 43, 37 ], + [ 40, 20, 213.37, 43, 38 ], + [ 40, 20, -177.05, 39, 21 ], + [ 40, 20, 123.76, 39, 43 ], + [ 40, 20, -353.82, 41, 24 ], + [ 40, 20, 124.76, 38, 42 ], + [ 40, 20, 144.43, 44, 38 ], + [ 40, 20, 358.63, 39, 20 ], + [ 40, 20, 239.15, 36, 43 ], + [ 40, 20, -84.81, 22, 41 ], + [ 40, 20, 335.97, 43, 34 ], + [ 40, 20, 136.38, 42, 40 ], + [ 40, 20, -60.55, 35, 44 ], + [ 40, 20, -342.47, 44, 31 ], + [ 40, 20, -38.8, 43, 41 ], + [ 40, 20, 320.09, 42, 40 ], + [ 40, 20, 25.13, 44, 34 ], + [ 40, 20, 282.48, 27, 43 ], + [ 40, 20, 47, 42, 43 ], + [ 40, 20, 43.45, 43, 42 ], + [ 40, 20, 163.73, 44, 29 ], + [ 40, 20, -327.56, 44, 38 ], + [ 40, 20, 311.35, 40, 43 ], + [ 40, 20, 25.85, 44, 35 ], + [ 40, 20, 5.28, 41, 23 ], + [ 40, 20, -58.72, 36, 44 ], + [ 40, 20, -345.3, 43, 29 ], + [ 40, 20, 49.11, 41, 43 ], + [ 40, 20, -11.25, 42, 27 ], + [ 40, 20, -104.78, 28, 42 ], + [ 40, 20, -358.22, 40, 21 ], + [ 40, 20, -158.86, 43, 32 ], + [ 40, 20, -251.42, 31, 42 ], + [ 40, 20, -175.27, 40, 22 ], + [ 40, 20, 324.91, 42, 38 ], + [ 40, 20, -245.24, 34, 43 ], + [ 40, 20, 309.5, 39, 43 ], + [ 40, 20, 109.34, 32, 43 ], + [ 40, 20, -246.61, 33, 43 ], + [ 40, 20, 188.64, 41, 25 ], + [ 40, 20, -335.47, 44, 34 ], + [ 40, 20, -270.64, 20, 39 ], + [ 40, 20, -34.98, 42, 38 ], + [ 40, 20, -36.02, 43, 39 ], + [ 40, 20, -253.11, 30, 43 ], + [ 40, 20, 286.22, 29, 44 ], + [ 40, 20, -75.94, 27, 43 ], + [ 40, 20, 132.03, 41, 41 ], + [ 40, 20, 208.69, 44, 36 ], + [ 40, 20, 331.07, 44, 37 ], + [ 40, 20, -184.53, 41, 22 ], + [ 40, 20, 12.42, 43, 28 ], + [ 40, 20, -356.5, 40, 22 ], + [ 40, 20, -233.19, 39, 43 ], + [ 40, 20, -321.73, 43, 40 ], + [ 40, 20, 8.78, 42, 26 ], + [ 40, 20, 68.1, 33, 44 ], + [ 40, 20, -307.02, 40, 43 ], + [ 40, 20, -302.11, 38, 44 ], + [ 40, 20, -303.15, 38, 44 ], + [ 40, 20, -51.8, 39, 43 ], + [ 40, 20, -300.33, 37, 44 ], + [ 40, 20, -102, 27, 42 ], + [ 40, 20, 56.78, 38, 44 ], + [ 40, 20, -115.59, 34, 44 ], + [ 40, 20, -137.09, 42, 41 ], + [ 40, 20, 171.55, 42, 24 ], + [ 40, 20, -166.1, 42, 27 ], + [ 40, 20, 332.25, 43, 36 ], + [ 40, 20, 284.95, 28, 43 ], + [ 40, 20, -337.46, 44, 33 ], + [ 40, 20, 356.79, 39, 22 ], + [ 40, 20, -345.7, 43, 28 ], + [ 40, 20, -12.44, 42, 28 ], + [ 40, 20, -39.24, 42, 40 ], + [ 40, 20, -238.81, 37, 43 ], + [ 40, 20, -86.22, 21, 40 ], + [ 40, 20, 244.83, 34, 44 ], + [ 40, 20, 253.22, 29, 43 ], + [ 40, 20, -237.53, 38, 43 ], + [ 40, 20, 215.89, 43, 38 ], + [ 40, 20, -291.31, 33, 44 ], + [ 40, 20, 134.89, 42, 41 ], + [ 40, 20, 219.39, 42, 39 ], + [ 40, 20, -164.5, 42, 28 ], + [ 40, 20, 173.17, 41, 23 ], + [ 40, 20, -131.83, 40, 41 ], + [ 40, 20, 263.61, 23, 40 ], + [ 40, 20, 226.15, 40, 41 ], + [ 40, 20, -73.94, 29, 44 ], + [ 40, 20, 320.47, 42, 40 ], + [ 40, 20, -185.65, 41, 22 ], + [ 40, 20, -14.26, 42, 28 ], + [ 40, 20, -141.54, 42, 39 ], + [ 40, 20, 231.14, 40, 43 ], + [ 40, 20, 230.15, 39, 42 ], + [ 40, 20, 81.38, 25, 42 ], + [ 40, 20, 42.68, 43, 42 ], + [ 40, 20, -295.95, 35, 44 ], + [ 40, 20, 53.63, 39, 44 ], + [ 40, 20, -32.37, 43, 38 ], + [ 40, 20, -273.41, 22, 40 ], + [ 40, 20, 50.41, 40, 43 ], + [ 40, 20, -18.07, 43, 31 ], + [ 40, 20, 74.16, 29, 43 ], + [ 40, 20, -348.83, 43, 27 ], + [ 40, 20, 103.13, 28, 42 ], + [ 40, 20, 347.94, 42, 28 ], + [ 40, 20, 256.01, 27, 42 ], + [ 40, 20, -3.66, 39, 22 ], + [ 40, 20, -249.59, 32, 43 ], + [ 40, 20, 111.89, 33, 43 ], + [ 40, 20, 65.03, 34, 44 ], + [ 40, 20, 245.3, 33, 43 ], + [ 40, 20, -28.72, 44, 37 ], + [ 40, 20, 260.54, 25, 41 ], + [ 40, 20, 58.47, 37, 44 ], + [ 40, 20, 199.45, 43, 31 ], + [ 40, 20, 164.15, 43, 28 ], + [ 40, 20, -75.38, 28, 43 ], + [ 40, 20, -234.49, 39, 43 ], + [ 40, 20, 124.61, 38, 42 ], + [ 40, 20, 98.97, 26, 41 ], + [ 40, 20, 111.25, 33, 43 ], + [ 40, 20, 343.08, 43, 30 ], + [ 40, 20, -29.9, 43, 36 ], + [ 40, 20, -18.6, 42, 31 ], + [ 40, 20, 64.45, 35, 45 ], + [ 40, 20, -347.22, 43, 28 ], + [ 40, 20, 45.46, 42, 42 ], + [ 40, 20, -241.93, 36, 43 ], + [ 40, 20, -19.59, 43, 32 ], + [ 40, 20, 132.05, 41, 41 ], + [ 40, 20, 168.49, 43, 26 ], + [ 40, 20, 322.34, 42, 40 ], + [ 40, 20, -153.79, 43, 34 ], + [ 40, 20, 179.66, 39, 19 ], + [ 40, 20, 65.47, 34, 44 ], + [ 40, 20, -165.85, 42, 27 ], + [ 40, 20, 75.66, 28, 43 ], + [ 40, 20, -298.19, 36, 44 ], + [ 40, 20, -55.44, 37, 43 ], + [ 40, 20, -172.45, 41, 24 ], + [ 40, 20, 126.84, 39, 43 ], + [ 40, 20, -170.14, 41, 25 ], + [ 40, 20, 158.84, 44, 32 ], + [ 40, 20, 27.38, 44, 36 ], + [ 40, 20, 248.33, 32, 43 ], + [ 40, 20, -1.71, 39, 21 ], + [ 40, 20, -168.47, 42, 26 ], + [ 40, 20, -187.06, 41, 23 ], + [ 40, 20, 123.8, 39, 43 ], + [ 40, 20, 316.15, 41, 41 ], + [ 40, 20, 271.92, 20, 40 ], + [ 40, 20, -124.95, 37, 42 ], + [ 40, 20, 299.22, 35, 44 ], + [ 40, 20, -117.99, 35, 43 ], + [ 40, 20, 216.46, 43, 38 ], + [ 40, 20, 3.67, 40, 22 ], + [ 40, 20, -105.2, 28, 42 ], + [ 40, 20, -98.09, 24, 41 ], + [ 40, 20, 121.74, 38, 44 ], + [ 40, 20, 235.21, 37, 42 ], + [ 40, 20, -326.05, 44, 39 ], + [ 40, 20, -69.77, 31, 44 ], + [ 40, 20, 197.55, 43, 30 ], + [ 40, 20, -119.83, 35, 43 ], + [ 40, 20, -250.11, 32, 43 ], + [ 40, 20, -96.99, 23, 40 ], + [ 40, 20, 74.32, 29, 43 ], + [ 40, 20, 185.56, 40, 22 ], + [ 40, 20, -35.18, 43, 39 ], + [ 40, 20, -341.13, 43, 31 ], + [ 40, 20, -346.9, 43, 28 ], + [ 40, 20, -268.34, 21, 39 ], + [ 40, 20, -151.26, 44, 36 ], + [ 40, 20, 171.95, 42, 24 ], + [ 40, 20, 119.05, 36, 43 ], + [ 40, 20, -262.94, 24, 40 ], + [ 40, 20, -189.77, 42, 25 ], + [ 40, 20, 310.58, 40, 43 ], + [ 40, 20, -89.99, 19, 39 ], + [ 40, 20, 294.04, 33, 44 ], + [ 40, 20, 266.72, 21, 39 ], + [ 40, 20, -178.07, 39, 20 ], + [ 40, 20, -190.92, 43, 26 ], + [ 40, 20, 205.94, 43, 34 ], + [ 40, 20, 63.94, 35, 44 ], + [ 40, 20, -334.47, 45, 35 ], + [ 40, 20, -150.4, 43, 35 ], + [ 40, 20, 318.74, 42, 41 ], + [ 40, 20, -72.56, 29, 44 ], + [ 40, 20, 331.35, 44, 37 ], + [ 40, 20, 193.95, 42, 27 ], + [ 40, 20, -38.6, 42, 40 ], + [ 40, 20, -98.43, 24, 41 ], + [ 40, 20, 31.49, 44, 37 ], + [ 40, 20, 201.57, 43, 32 ], + [ 40, 20, 11.46, 43, 27 ], + [ 40, 20, 294.51, 33, 44 ], + [ 40, 20, -84.11, 23, 41 ], + [ 40, 20, -162.99, 43, 29 ], + [ 40, 20, -100.68, 26, 42 ], + [ 40, 20, -65.23, 33, 44 ], + [ 40, 20, -149.88, 43, 36 ], + [ 40, 20, -9.01, 41, 26 ], + [ 40, 20, 143.51, 44, 38 ], + [ 40, 20, -337.94, 45, 34 ], + [ 40, 20, -249.97, 32, 43 ], + [ 40, 20, 240.58, 35, 43 ], + [ 40, 20, -167.7, 42, 27 ], + [ 40, 20, -299.39, 36, 44 ], + [ 40, 20, -209.42, 44, 35 ], + [ 40, 20, -233.67, 39, 43 ], + [ 40, 20, 327.34, 43, 38 ], + [ 40, 20, -27.49, 43, 36 ], + [ 40, 20, -64.59, 34, 45 ], + [ 40, 20, 173.27, 41, 23 ], + [ 40, 20, -323.55, 44, 39 ], + [ 40, 20, -39.05, 43, 41 ], + [ 40, 20, -337.14, 44, 33 ], + [ 40, 20, 355.2, 40, 23 ], + [ 40, 20, 248.4, 32, 43 ], + [ 40, 20, 354.22, 40, 24 ], + [ 40, 20, 189.14, 41, 25 ], + [ 40, 20, -150.21, 43, 35 ], + [ 40, 20, -104.22, 27, 42 ], + [ 40, 20, -139.37, 42, 40 ], + [ 40, 20, -308.64, 40, 43 ], + [ 40, 20, 267.25, 20, 39 ], + [ 40, 20, -204.87, 44, 33 ], + [ 40, 20, -32.74, 43, 38 ], + [ 40, 20, -255.74, 28, 42 ], + [ 40, 20, 54.45, 39, 44 ], + [ 40, 20, 262.03, 24, 41 ], + [ 40, 20, -45.61, 40, 42 ], + [ 40, 20, -314.56, 42, 42 ], + [ 40, 20, 45.53, 42, 42 ], + [ 40, 20, -23.54, 43, 33 ], + [ 40, 20, -204.53, 44, 33 ], + [ 40, 20, -73.9, 29, 44 ], + [ 40, 20, 168.76, 43, 26 ], + [ 40, 20, -143.92, 43, 38 ], + [ 40, 20, 76.69, 28, 43 ], + [ 40, 20, 295.09, 33, 44 ], + [ 40, 20, -176.59, 39, 21 ], + [ 40, 20, -310.8, 41, 43 ], + [ 40, 20, -129.5, 39, 42 ], + [ 40, 20, 356.69, 39, 22 ], + [ 40, 20, -274.35, 23, 41 ], + [ 40, 20, 191.45, 42, 26 ], + [ 40, 20, -340.45, 44, 32 ], + [ 40, 20, -279.15, 26, 42 ], + [ 40, 20, 79.84, 27, 43 ], + [ 40, 20, -346.24, 43, 28 ], + [ 40, 20, 269.99, 19, 39 ], + [ 40, 20, 289.64, 31, 44 ], + [ 40, 20, -90.45, 19, 39 ], + [ 40, 20, -229.38, 41, 42 ], + [ 40, 20, 341.01, 43, 32 ], + [ 40, 20, -183.2, 40, 21 ], + [ 40, 20, -74.25, 28, 43 ], + [ 40, 20, -51.74, 39, 43 ], + [ 40, 20, -78.93, 26, 43 ], + [ 40, 20, 340.21, 43, 32 ], + [ 40, 20, -149.72, 43, 36 ], + [ 40, 20, 235.46, 37, 42 ], + [ 40, 20, -334.36, 45, 35 ], + [ 40, 20, 255.82, 27, 42 ], + [ 40, 20, -148.08, 43, 37 ], + [ 40, 20, -178.88, 39, 19 ], + [ 40, 20, -178.08, 39, 20 ], + [ 40, 20, -339.33, 44, 33 ], + [ 40, 20, 37.2, 43, 40 ], + [ 40, 20, 258.61, 26, 42 ], + [ 40, 20, 315.77, 41, 41 ], + [ 40, 20, 220.62, 42, 40 ], + [ 40, 20, 307.81, 39, 43 ], + [ 40, 20, -173.74, 40, 23 ], + [ 40, 20, 217.3, 42, 39 ], + [ 40, 20, -326.54, 44, 39 ], + [ 40, 20, -342.29, 44, 31 ], + [ 40, 20, 236.86, 37, 43 ], + [ 40, 20, -245.68, 34, 43 ], + [ 40, 20, 97.55, 25, 41 ], + [ 40, 20, 250.62, 31, 43 ], + [ 40, 20, -335.69, 44, 34 ], + [ 40, 20, 27.19, 44, 36 ], + [ 40, 20, -199.84, 44, 31 ], + [ 40, 20, -205.07, 44, 33 ], + [ 40, 20, 8.19, 42, 25 ], + [ 40, 20, -23.03, 43, 33 ], + [ 40, 20, 80.68, 26, 42 ], + [ 40, 20, 316.45, 41, 41 ], + [ 40, 20, 258.04, 27, 42 ], + [ 40, 20, 60.89, 36, 44 ], + [ 40, 20, -193.27, 43, 27 ], + [ 40, 20, 133.49, 42, 42 ], + [ 40, 20, 86.53, 22, 40 ], + [ 40, 20, -297.46, 36, 44 ], + [ 40, 20, 345.41, 42, 29 ], + [ 40, 20, 267.65, 20, 39 ], + [ 40, 20, -115.54, 34, 44 ], + [ 40, 20, -353.92, 41, 24 ], + [ 40, 20, -55.15, 37, 43 ], + [ 40, 20, -216.94, 43, 39 ], + [ 40, 20, 321.85, 42, 40 ], + [ 40, 20, -194.53, 43, 28 ], + [ 40, 20, -269.13, 20, 39 ], + [ 40, 20, -211.89, 44, 37 ], + [ 40, 20, -337.23, 44, 33 ], + [ 40, 20, -235.66, 39, 43 ], + [ 40, 20, -194.18, 43, 27 ], + [ 40, 20, 259.63, 26, 42 ], + [ 40, 20, -121.34, 36, 43 ], + [ 40, 20, 263.37, 23, 40 ], + [ 40, 20, 150.24, 44, 35 ], + [ 40, 20, -97.02, 23, 40 ], + [ 40, 20, -69.44, 32, 44 ], + [ 40, 20, 310.41, 39, 43 ], + [ 40, 20, 57.91, 38, 44 ], + [ 40, 20, 298.75, 36, 45 ], + [ 40, 20, -72.63, 29, 44 ], + [ 40, 20, -221.41, 42, 40 ], + [ 40, 20, 255.19, 28, 42 ], + [ 40, 20, -174.6, 40, 22 ], + [ 40, 20, 199.48, 43, 31 ], + [ 40, 20, -298.08, 36, 44 ], + [ 40, 20, 318.9, 42, 41 ], + [ 40, 20, -73.99, 29, 44 ], + [ 40, 20, -235.54, 38, 42 ], + [ 40, 20, 304.31, 38, 44 ], + [ 40, 20, -166.34, 42, 27 ], + [ 40, 20, 8.92, 42, 26 ], + [ 40, 20, 310.39, 39, 43 ], + [ 40, 20, 138.52, 42, 40 ], + [ 40, 20, 151.99, 44, 35 ], + [ 40, 20, 272.24, 20, 40 ], + [ 40, 20, 303.99, 38, 44 ], + [ 40, 20, 242.87, 35, 43 ], + [ 40, 20, -299.66, 36, 44 ], + [ 40, 20, 326.77, 43, 38 ], + [ 40, 20, -352.79, 42, 25 ], + [ 40, 20, -133.84, 40, 41 ], + [ 40, 20, 226.39, 40, 41 ], + [ 40, 20, -114.12, 33, 43 ], + [ 40, 20, -230.47, 40, 42 ], + [ 40, 20, 16.63, 44, 30 ], + [ 40, 20, 148.86, 44, 36 ], + [ 40, 20, 60.09, 36, 44 ], + [ 40, 20, -32.96, 43, 38 ], + [ 40, 20, -153.24, 43, 35 ], + [ 40, 20, -1.16, 39, 20 ], + [ 40, 20, 254.41, 28, 42 ], + [ 40, 20, -14.65, 42, 29 ], + [ 40, 20, -105.97, 29, 43 ], + [ 40, 20, -280.19, 27, 43 ], + [ 40, 20, -175.17, 40, 22 ], + [ 40, 20, -44.05, 41, 41 ], + [ 40, 20, -321.29, 44, 41 ], + [ 40, 20, 110.85, 33, 43 ], + [ 40, 20, 80.41, 26, 42 ], + [ 40, 20, 343.02, 43, 30 ], + [ 40, 20, 304.51, 37, 43 ], + [ 40, 20, -270.67, 20, 39 ], + [ 40, 20, 293.41, 32, 44 ], + [ 40, 20, 83.02, 24, 41 ], + [ 40, 20, 241.33, 36, 44 ], + [ 40, 20, 205.64, 44, 34 ], + [ 40, 20, 27.01, 44, 36 ], + [ 40, 20, 124.19, 39, 43 ], + [ 40, 20, 265.98, 21, 39 ], + [ 40, 20, -6.23, 40, 24 ], + [ 40, 20, 131.41, 41, 41 ], + [ 40, 20, -227.86, 41, 41 ], + [ 40, 20, -139.85, 42, 39 ], + [ 40, 20, -342.72, 44, 30 ], + [ 40, 20, -98.33, 24, 41 ], + [ 40, 20, 236.79, 37, 43 ], + [ 40, 20, 166.14, 43, 27 ], + [ 40, 20, 321.77, 42, 40 ], + [ 40, 20, -156.18, 43, 33 ], + [ 40, 20, -347.1, 42, 27 ], + [ 40, 20, -39.4, 42, 40 ], + [ 40, 20, -261.77, 25, 41 ], + [ 40, 20, -1.74, 39, 21 ], + [ 40, 20, 214.63, 42, 37 ], + [ 40, 20, -181.95, 40, 20 ], + [ 40, 20, 183.09, 39, 21 ], + [ 40, 20, -189.43, 42, 25 ], + [ 40, 20, -143.24, 43, 38 ], + [ 40, 20, -66.07, 33, 44 ], + [ 40, 20, 250.99, 31, 43 ], + [ 40, 20, -160.22, 43, 31 ], + [ 40, 20, -121.56, 36, 43 ], + [ 40, 20, 340.33, 43, 32 ], + [ 40, 20, -226.82, 42, 42 ], + [ 40, 20, 321.46, 42, 40 ], + [ 40, 20, 221.65, 41, 40 ], + [ 40, 20, 338.83, 43, 33 ], + [ 40, 20, -11.53, 42, 27 ], + [ 40, 20, -14.16, 42, 28 ], + [ 40, 20, 244.81, 34, 44 ], + [ 40, 20, 342.24, 43, 31 ], + [ 40, 20, -242.76, 36, 43 ], + [ 40, 20, -343.06, 44, 30 ], + [ 40, 20, -157.61, 43, 32 ], + [ 40, 20, -225.48, 42, 41 ], + [ 40, 20, -81.38, 24, 42 ], + [ 40, 20, -280.83, 27, 43 ], + [ 40, 20, 300.67, 36, 44 ], + [ 40, 20, -119.62, 35, 43 ], + [ 40, 20, -77, 26, 42 ], + [ 40, 20, 313.57, 40, 42 ], + [ 40, 20, 200.99, 43, 32 ], + [ 40, 20, 21.23, 44, 33 ], + [ 40, 20, -48.18, 40, 42 ], + [ 40, 20, 55.62, 39, 44 ], + [ 40, 20, 199.29, 43, 31 ], + [ 40, 20, -225.09, 42, 41 ], + [ 40, 20, 226.2, 40, 41 ], + [ 40, 20, -303.96, 39, 44 ], + [ 40, 20, 68.84, 33, 44 ], + [ 40, 20, 117.18, 36, 43 ], + [ 40, 20, -104.18, 27, 42 ], + [ 40, 20, 307.29, 39, 43 ], + [ 40, 20, 97.5, 25, 41 ], + [ 40, 20, 29.01, 44, 36 ], + [ 40, 20, 268.74, 19, 39 ], + [ 40, 20, -40.85, 42, 41 ], + [ 40, 20, 7.83, 42, 25 ], + [ 40, 20, -102.79, 27, 42 ], + [ 40, 20, 304.99, 37, 43 ], + [ 40, 20, -107.37, 29, 43 ], + [ 40, 20, -120.56, 36, 43 ], + [ 40, 20, -297.78, 36, 44 ], + [ 40, 20, -90.42, 19, 39 ], + [ 40, 20, 81.84, 25, 42 ], + [ 40, 20, -163.25, 43, 29 ], + [ 40, 20, 188.2, 41, 24 ], + [ 40, 20, 161.02, 44, 31 ], + [ 40, 20, -222.59, 43, 41 ], + [ 40, 20, -291.42, 33, 44 ], + [ 40, 20, -275.99, 24, 41 ], + [ 40, 20, -269.03, 20, 39 ], + [ 40, 20, 269.58, 19, 39 ], + [ 40, 20, 105.25, 29, 42 ], + [ 573, 347, -55.47, 609, 669 ], + [ 765, 205, 0.58, 766, 212 ], + [ 351, 714, 256.41, 775, 508 ], + [ 276, 434, -74.59, 490, 381 ], + [ 146, 6, 293.24, 62, 136 ], + [ 512, 715, -174.13, 581, 762 ], + [ 853, 14, 95.45, 95, 849 ], + [ 457, 224, -76.35, 324, 497 ], + [ 174, 370, -7.76, 221, 390 ], + [ 294, 467, -98.19, 502, 357 ], + [ 955, 129, 227, 744, 785 ], + [ 364, 702, -301.62, 788, 677 ], + [ 877, 197, -353.05, 894, 302 ], + [ 730, 404, 53.02, 762, 826 ], + [ 112, 514, 58.24, 495, 366 ], + [ 17, 129, 8.44, 35, 130 ], + [ 281, 768, 20.87, 536, 818 ], + [ 929, 343, 230.05, 858, 931 ], + [ 308, 473, -143.37, 528, 562 ], + [ 809, 693, 294.19, 962, 1021 ], + [ 158, 641, -305.47, 613, 500 ], + [ 993, 146, 352.59, 1002, 273 ], + [ 52, 36, -182.82, 53, 37 ], + [ 61, 583, 195.61, 214, 576 ], + [ 649, 666, -43.4, 928, 929 ], + [ 220, 678, 77.36, 710, 362 ], + [ 744, 121, 77.35, 280, 751 ], + [ 261, 49, -321.61, 234, 200 ], + [ 77, 280, 121.51, 279, 210 ], + [ 385, 119, 50.72, 335, 373 ], + [ 291, 237, -68.73, 325, 357 ], + [ 369, 213, 67.7, 337, 422 ], + [ 358, 251, 100.59, 312, 396 ], + [ 610, 297, -73.68, 455, 668 ], + [ 376, 15, -169.68, 371, 81 ], + [ 827, 179, -254.18, 397, 843 ], + [ 425, 737, -141.29, 791, 839 ], + [ 23, 164, -95.58, 164, 37 ], + [ 77, 222, 166.62, 125, 232 ], + [ 762, 510, 277.54, 604, 822 ], + [ 322, 728, -221.02, 720, 759 ], + [ 736, 236, -22.05, 770, 495 ], + [ 321, 504, 212.83, 541, 597 ], + [ 976, 193, -229.44, 781, 865 ], + [ 359, 475, -215.81, 569, 594 ], + [ 630, 326, -48.07, 662, 686 ], + [ 780, 644, -77.76, 793, 899 ], + [ 67, 661, 75.98, 657, 225 ], + [ 443, 718, 283.2, 799, 595 ], + [ 168, 522, 40.53, 466, 506 ], + [ 51, 763, 2.14, 78, 763 ], + [ 249, 547, 222.59, 552, 570 ], + [ 729, 19, -25.54, 664, 331 ], + [ 520, 750, 154.94, 789, 898 ], + [ 706, 251, -120.38, 573, 735 ], + [ 911, 723, 185.11, 970, 800 ], + [ 330, 540, 68.77, 622, 503 ], + [ 732, 590, -32.05, 932, 888 ], + [ 328, 154, -78.98, 212, 350 ], + [ 219, 279, -310.49, 354, 347 ], + [ 788, 317, -315.86, 786, 775 ], + [ 279, 503, 196.18, 406, 559 ], + [ 84, 282, 213.32, 224, 281 ], + [ 559, 671, 196.16, 722, 798 ], + [ 268, 432, -288.99, 495, 394 ], + [ 609, 347, -331.79, 700, 593 ], + [ 360, 594, 173.57, 424, 629 ], + [ 970, 375, 5.79, 1003, 470 ], + [ 412, 620, 149.31, 670, 742 ], + [ 812, 16, -309.25, 525, 638 ], + [ 882, 606, -12.36, 990, 780 ], + [ 945, 47, -261.03, 193, 939 ], + [ 712, 110, 2.68, 716, 143 ], + [ 503, 529, -88.4, 542, 517 ], + [ 270, 730, 316.29, 698, 714 ], + [ 371, 418, -79.07, 479, 443 ], + [ 412, 687, -242.53, 800, 681 ], + [ 146, 646, 108.15, 659, 338 ], + [ 667, 187, -31.06, 666, 504 ], + [ 367, 27, -16.94, 358, 132 ], + [ 439, 689, -168.99, 561, 758 ], + [ 934, 629, -125.04, 1050, 1124 ], + [ 50, 395, -22.51, 196, 384 ], + [ 566, 4, -246.96, 225, 521 ], + [ 952, 206, -283.43, 421, 973 ], + [ 890, 484, 107.7, 731, 993 ], + [ 266, 25, 31.52, 239, 160 ], + [ 433, 134, -83.19, 183, 445 ], + [ 75, 634, 91.53, 636, 90 ], + [ 543, 140, 116.69, 368, 547 ], + [ 6, 469, 41.81, 317, 353 ], + [ 278, 401, -88.97, 404, 284 ], + [ 612, 439, 280.54, 542, 681 ], + [ 68, 95, 283.63, 107, 88 ], + [ 229, 41, -243.53, 139, 221 ], + [ 338, 708, 210.11, 646, 780 ], + [ 1003, 139, 232.72, 717, 881 ], + [ 536, 463, 357.89, 551, 482 ], + [ 930, 519, 231.4, 985, 1049 ], + [ 548, 157, -357.44, 554, 181 ], + [ 202, 162, 82.14, 187, 222 ], + [ 773, 370, -262.23, 471, 814 ], + [ 135, 61, 148.43, 147, 121 ], + [ 22, 112, 236.79, 105, 78 ], + [ 937, 283, 274.47, 354, 956 ], + [ 79, 536, 217.74, 389, 471 ], + [ 890, 520, 347.55, 980, 699 ], + [ 708, 154, 61.36, 474, 695 ], + [ 705, 82, -172.68, 708, 169 ], + [ 523, 493, -25.61, 683, 671 ], + [ 526, 644, 125.61, 830, 801 ], + [ 131, 455, -248, 471, 290 ], + [ 270, 556, -191.46, 374, 597 ], + [ 993, 572, -87.31, 616, 1018 ], + [ 818, 506, 177.51, 839, 540 ], + [ 696, 565, -40.43, 894, 881 ], + [ 494, 464, -273.43, 492, 521 ], + [ 476, 227, -129.96, 478, 509 ], + [ 52, 755, -53.22, 635, 493 ], + [ 318, 379, -311.68, 494, 489 ], + [ 794, 5, 291.36, 293, 741 ], + [ 460, 103, -2.3, 462, 121 ], + [ 619, 305, -201.62, 687, 511 ], + [ 859, 282, 333.95, 894, 630 ], + [ 223, 127, 88.01, 134, 226 ], + [ 987, 649, -102.96, 852, 1106 ], + [ 418, 251, 331.17, 486, 421 ], + [ 541, 608, -176.92, 572, 635 ], + [ 868, 646, -189.3, 960, 777 ], + [ 281, 116, 165.4, 300, 181 ], + [ 349, 711, 169.74, 470, 761 ], + [ 252, 288, 265.89, 304, 271 ], + [ 760, 752, -168.4, 894, 888 ], + [ 986, 628, 95.31, 716, 1038 ], + [ 949, 27, 213.8, 802, 548 ], + [ 562, 767, 101.53, 864, 702 ], + [ 927, 312, 336.26, 973, 659 ], + [ 886, 717, 185.51, 949, 798 ], + [ 123, 600, 70.64, 606, 315 ], + [ 365, 42, 178.97, 365, 47 ], + [ 89, 304, 124.91, 299, 245 ], + [ 842, 492, -325.96, 972, 879 ], + [ 708, 294, -341.61, 764, 502 ], + [ 981, 265, 246.51, 633, 1004 ], + [ 604, 208, 271.05, 218, 607 ], + [ 396, 629, 269.55, 631, 399 ], + [ 29, 556, -158.42, 229, 526 ], + [ 1019, 453, -206.3, 1114, 856 ], + [ 771, 544, 355.98, 806, 597 ], + [ 359, 50, -324.83, 322, 247 ], + [ 102, 116, -40.39, 151, 154 ], + [ 791, 747, -299.35, 1038, 1055 ], + [ 840, 510, -23.99, 973, 807 ], + [ 866, 477, 224.07, 953, 944 ], + [ 705, 162, -301.25, 503, 686 ], + [ 578, 459, 288.1, 614, 692 ], + [ 450, 481, 89.13, 487, 456 ], + [ 736, 476, -207, 871, 757 ], + [ 955, 286, 302.83, 756, 957 ], + [ 949, 555, 349.29, 1034, 721 ], + [ 999, 631, -262.23, 760, 1073 ], + [ 430, 457, -281.97, 536, 515 ], + [ 516, 318, 195.96, 582, 446 ], + [ 994, 199, -76.86, 418, 1012 ], + [ 800, 727, 74.89, 910, 962 ], + [ 884, 393, -215, 949, 828 ], + [ 782, 729, 235.01, 1044, 1057 ], + [ 397, 513, -38.84, 630, 648 ], + [ 614, 461, 228.97, 750, 765 ], + [ 533, 404, 29.07, 661, 611 ], + [ 518, 385, -99.58, 465, 573 ], + [ 657, 707, 327.94, 930, 947 ], + [ 768, 326, -94.35, 382, 789 ], + [ 534, 303, 91.98, 321, 542 ], + [ 937, 135, 53.41, 666, 832 ], + [ 532, 29, -299.01, 282, 479 ], + [ 738, 615, -335.72, 925, 864 ], + [ 560, 144, 144.86, 540, 439 ], + [ 1017, 604, -143.59, 1176, 1088 ], + [ 830, 236, 234.95, 668, 814 ], + [ 360, 624, -124.39, 717, 648 ], + [ 730, 348, -181.98, 741, 372 ], + [ 684, 155, 122.85, 501, 657 ], + [ 38, 555, -313.91, 426, 412 ], + [ 681, 490, -188.19, 744, 581 ], + [ 245, 120, -161.22, 269, 191 ], + [ 332, 673, -304.93, 742, 657 ], + [ 366, 325, -170.16, 415, 381 ], + [ 60, 319, -323.93, 236, 293 ], + [ 368, 173, -103.4, 252, 396 ], + [ 190, 443, -239.3, 478, 388 ], + [ 525, 222, 86.17, 257, 538 ], + [ 779, 735, 231.02, 1060, 1066 ], + [ 441, 763, 31.22, 772, 881 ], + [ 507, 506, -230.09, 713, 712 ], + [ 751, 689, -118.02, 959, 985 ], + [ 10, 170, -207.58, 87, 154 ], + [ 88, 444, 96.25, 450, 134 ], + [ 358, 489, -325.38, 572, 605 ], + [ 410, 765, -163.49, 609, 848 ], + [ 774, 139, 250.99, 382, 775 ], + [ 946, 533, -261.54, 666, 1012 ], + [ 557, 348, -293.13, 538, 649 ], + [ 373, 679, -298.13, 774, 648 ], + [ 930, 291, 175.45, 950, 362 ], + [ 90, 86, 101, 101, 103 ], + [ 435, 93, 259.53, 169, 443 ], + [ 651, 159, -44.7, 573, 570 ], + [ 276, 427, -306.82, 507, 476 ], + [ 325, 734, -310.41, 769, 723 ], + [ 605, 546, -180.54, 609, 550 ], + [ 450, 487, 318.4, 658, 662 ], + [ 590, 137, -75.57, 279, 605 ], + [ 685, 416, -159.6, 786, 627 ], + [ 773, 345, 112.41, 613, 845 ], + [ 511, 412, 192.27, 586, 510 ], + [ 142, 738, 278.75, 749, 252 ], + [ 1023, 458, -293.08, 822, 1121 ], + [ 448, 665, 106.65, 765, 619 ], + [ 523, 141, 74, 280, 541 ], + [ 594, 594, -134.45, 838, 839 ], + [ 849, 163, 322.03, 768, 650 ], + [ 790, 605, -270.42, 610, 793 ], + [ 171, 351, 119.26, 389, 320 ], + [ 456, 162, -277.76, 222, 473 ], + [ 103, 545, 108.48, 549, 269 ], + [ 889, 651, 79.93, 796, 989 ], + [ 773, 650, -210.99, 997, 954 ], + [ 868, 218, -308.01, 706, 817 ], + [ 508, 610, -176.53, 543, 638 ], + [ 101, 287, -340.03, 192, 304 ], + [ 422, 155, -187.53, 438, 208 ], + [ 368, 747, -340.56, 596, 826 ], + [ 496, 107, -12.25, 506, 210 ], + [ 153, 448, 139.26, 407, 437 ], + [ 270, 525, -70.57, 583, 429 ], + [ 936, 424, -180.74, 940, 435 ], + [ 538, 235, -291.72, 417, 586 ], + [ 750, 118, -88.21, 140, 753 ], + [ 824, 499, -237.04, 867, 961 ], + [ 61, 575, 31.41, 352, 522 ], + [ 89, 123, 176.59, 95, 127 ], + [ 335, 399, 156.89, 465, 497 ], + [ 362, 2, 256.74, 84, 352 ], + [ 857, 117, -43.45, 701, 674 ], + [ 543, 319, -271.27, 331, 549 ], + [ 955, 253, 125.56, 761, 922 ], + [ 186, 656, 345.76, 340, 681 ], + [ 283, 450, -263.73, 477, 329 ], + [ 669, 300, 203.75, 732, 543 ], + [ 844, 345, 22.18, 911, 637 ], + [ 377, 412, 137.13, 556, 557 ], + [ 116, 663, -209.4, 426, 633 ], + [ 851, 166, -207.8, 829, 542 ], + [ 704, 40, -98.81, 146, 700 ], + [ 130, 447, 199.8, 272, 464 ], + [ 257, 395, -283.5, 443, 341 ], + [ 947, 534, 331.25, 1086, 923 ], + [ 927, 745, 33.05, 1183, 1129 ], + [ 40, 479, 313.89, 371, 360 ], + [ 842, 343, -82.76, 445, 878 ], + [ 361, 626, 303.08, 721, 644 ], + [ 44, 712, 203.88, 327, 667 ], + [ 258, 582, 359.09, 265, 586 ], + [ 475, 342, -323.57, 585, 557 ], + [ 846, 439, 95.2, 513, 881 ], + [ 618, 631, -255.2, 767, 757 ], + [ 727, 573, 244.51, 828, 902 ], + [ 686, 500, 222.61, 842, 831 ], + [ 221, 530, -49.12, 544, 514 ], + [ 156, 392, 272.31, 397, 171 ], + [ 550, 663, 138.28, 851, 860 ], + [ 469, 696, 85.4, 731, 523 ], + [ 247, 121, -89.45, 122, 247 ], + [ 246, 477, 230.48, 523, 492 ], + [ 298, 343, -78.27, 395, 361 ], + [ 945, 39, -206.96, 860, 462 ], + [ 70, 584, 329.99, 351, 541 ], + [ 930, 330, 78.29, 511, 977 ], + [ 734, 496, -145.75, 884, 822 ], + [ 153, 612, 221.61, 519, 558 ], + [ 28, 256, 122.53, 231, 160 ], + [ 736, 608, 54.16, 923, 952 ], + [ 346, 25, 167.09, 343, 100 ], + [ 731, 209, -106.47, 406, 759 ], + [ 546, 431, 214.67, 693, 663 ], + [ 947, 372, 250.3, 668, 1015 ], + [ 141, 424, 76.93, 444, 233 ], + [ 98, 353, -334.21, 242, 360 ], + [ 721, 135, 129.95, 565, 638 ], + [ 2, 249, -16.73, 72, 238 ], + [ 829, 270, 223.01, 789, 761 ], + [ 492, 54, 72.23, 201, 484 ], + [ 57, 313, 94.83, 316, 81 ], + [ 806, 92, 289.88, 360, 788 ], + [ 143, 502, -26.17, 348, 514 ], + [ 960, 444, 321.87, 1028, 941 ], + [ 804, 549, -1.2, 813, 565 ], + [ 402, 622, 329.13, 663, 740 ], + [ 438, 395, -264.53, 434, 473 ], + [ 111, 396, -31.3, 299, 395 ], + [ 599, 250, 221.13, 614, 581 ], + [ 404, 618, 271.02, 624, 414 ], + [ 1019, 661, -156.22, 1198, 1014 ], + [ 642, 497, -70.04, 685, 773 ], + [ 542, 604, -319.97, 803, 810 ], + [ 1017, 555, 95.73, 653, 1065 ], + [ 963, 390, -109.91, 693, 1037 ], + [ 449, 691, -246.89, 812, 682 ], + [ 405, 251, 123.08, 431, 475 ], + [ 541, 334, -225.77, 616, 619 ], + [ 1008, 15, 8.18, 999, 158 ], + [ 811, 318, -132.55, 781, 811 ], + [ 684, 314, -233.79, 657, 735 ], + [ 974, 27, 231.55, 625, 778 ], + [ 363, 434, 76.98, 504, 451 ], + [ 420, 428, -122.82, 586, 583 ], + [ 44, 382, 242.46, 358, 215 ], + [ 399, 718, 169.72, 520, 776 ], + [ 560, 254, -315.23, 576, 574 ], + [ 223, 659, -2.44, 249, 667 ], + [ 414, 647, 11.35, 532, 715 ], + [ 938, 487, 309.16, 969, 1035 ], + [ 237, 232, -116.8, 312, 315 ], + [ 417, 194, -91.67, 205, 421 ], + [ 234, 466, 314.41, 495, 493 ], + [ 856, 12, -4.73, 853, 82 ], + [ 169, 45, 352.78, 172, 66 ], + [ 2, 344, 305.73, 279, 202 ], + [ 755, 764, -176.75, 795, 804 ], + [ 819, 163, -279.19, 291, 834 ], + [ 303, 473, 327.13, 510, 561 ], + [ 832, 408, 63.3, 737, 926 ], + [ 759, 586, 280.36, 711, 851 ], + [ 81, 130, -257.92, 143, 105 ], + [ 463, 305, 178.43, 470, 316 ], + [ 398, 179, 189.44, 420, 241 ], + [ 903, 305, 230.84, 805, 892 ], + [ 893, 306, -167.29, 937, 494 ], + [ 816, 104, -171.82, 821, 218 ], + [ 52, 718, -19.67, 289, 693 ], + [ 167, 177, -296.63, 232, 228 ], + [ 120, 32, 85.5, 41, 122 ], + [ 722, 601, -85.84, 650, 764 ], + [ 609, 662, 35.57, 880, 892 ], + [ 754, 240, 321.29, 737, 658 ], + [ 496, 538, -231.74, 729, 721 ], + [ 254, 447, 53.71, 510, 469 ], + [ 70, 348, 340.06, 183, 350 ], + [ 344, 192, -282.97, 264, 378 ], + [ 612, 231, -330.59, 646, 501 ], + [ 72, 356, -165.26, 159, 361 ], + [ 162, 446, -135.64, 426, 431 ], + [ 795, 219, -292.56, 507, 818 ], + [ 161, 676, 327.18, 500, 655 ], + [ 724, 473, -168.11, 804, 611 ], + [ 669, 77, 336.01, 641, 342 ], + [ 851, 267, 34.78, 850, 704 ], + [ 416, 319, -359.02, 420, 326 ], + [ 387, 576, -179.64, 389, 577 ], + [ 497, 267, 36.17, 559, 509 ], + [ 539, 320, -350.8, 583, 402 ], + [ 816, 441, 66.6, 729, 923 ], + [ 111, 269, 271.66, 271, 118 ], + [ 207, 390, 184.72, 237, 405 ], + [ 160, 164, -92.13, 168, 164 ], + [ 433, 666, -20.46, 637, 775 ], + [ 399, 536, 229.18, 665, 650 ], + [ 960, 79, -192.4, 954, 282 ], + [ 962, 369, 347.54, 1018, 567 ], + [ 194, 741, -122.85, 727, 563 ], + [ 758, 377, 176.79, 777, 417 ], + [ 771, 220, -130.18, 664, 730 ], + [ 353, 303, 121.74, 443, 458 ], + [ 228, 458, -233.29, 503, 455 ], + [ 1002, 577, -79.69, 746, 1088 ], + [ 359, 133, -176.18, 366, 155 ], + [ 842, 432, 325.52, 938, 832 ], + [ 656, 384, -320.92, 751, 711 ], + [ 895, 337, 24.6, 953, 678 ], + [ 877, 482, -0.9, 883, 495 ], + [ 190, 282, 351.99, 226, 305 ], + [ 954, 91, -162, 934, 380 ], + [ 256, 356, 121.52, 436, 403 ], + [ 942, 526, -170.02, 1017, 680 ], + [ 98, 510, -349.45, 189, 518 ], + [ 496, 11, -224.86, 359, 356 ], + [ 758, 642, 179.75, 760, 644 ], + [ 709, 366, -180.86, 713, 375 ], + [ 764, 269, 134.32, 725, 733 ], + [ 287, 158, -134.37, 312, 314 ], + [ 798, 300, 129.27, 737, 806 ], + [ 787, 247, 214.41, 788, 647 ], + [ 44, 439, -172.34, 101, 439 ], + [ 729, 377, 320.69, 802, 753 ], + [ 999, 450, 78.19, 644, 1069 ], + [ 1014, 54, 88.04, 88, 1015 ], + [ 183, 622, -30.22, 470, 629 ], + [ 950, 385, 312.59, 924, 960 ], + [ 181, 190, -155.94, 241, 245 ], + [ 884, 405, -8.25, 931, 527 ], + [ 181, 236, 272.79, 243, 191 ], + [ 877, 57, -216.03, 743, 560 ], + [ 670, 90, 195.13, 668, 260 ], + [ 937, 637, -172.02, 1014, 760 ], + [ 881, 618, 249.25, 889, 1041 ], + [ 480, 610, 299.25, 765, 716 ], + [ 93, 747, 161.79, 321, 738 ], + [ 562, 691, -197.6, 744, 827 ], + [ 495, 59, -19.85, 484, 223 ], + [ 785, 721, 359.66, 787, 725 ], + [ 261, 89, -139.99, 255, 234 ], + [ 137, 746, -86.8, 751, 178 ], + [ 342, 480, -2.77, 363, 495 ], + [ 107, 414, -126.38, 395, 331 ], + [ 375, 306, -267.64, 321, 386 ], + [ 217, 666, 304.01, 672, 552 ], + [ 131, 735, 283.69, 744, 301 ], + [ 873, 388, 211.15, 947, 782 ], + [ 80, 759, 265.54, 762, 137 ], + [ 564, 579, 221.17, 804, 806 ], + [ 563, 7, 303.67, 317, 472 ], + [ 876, 109, 282.32, 291, 878 ], + [ 241, 365, 11.53, 309, 406 ], + [ 749, 446, -321.05, 862, 817 ], + [ 57, 726, -34.7, 458, 629 ], + [ 188, 121, -131.26, 213, 220 ], + [ 109, 219, -162.69, 168, 240 ], + [ 1024, 105, 200.79, 993, 460 ], + [ 812, 165, 191.77, 827, 326 ], + [ 678, 587, 14.3, 801, 736 ], + [ 481, 241, 20.14, 534, 391 ], + [ 401, 130, 122.08, 322, 407 ], + [ 290, 310, -255.92, 371, 355 ], + [ 818, 103, 161.27, 807, 359 ], + [ 874, 145, 121.63, 581, 819 ], + [ 313, 286, 234.41, 414, 419 ], + [ 638, 367, -281.16, 483, 696 ], + [ 918, 367, -183.26, 937, 417 ], + [ 692, 208, -29.51, 703, 521 ], + [ 453, 716, -199.75, 668, 826 ], + [ 985, 309, 25.67, 1021, 705 ], + [ 502, 609, 146.76, 753, 783 ], + [ 163, 58, -283.29, 93, 171 ], + [ 311, 671, -187.24, 393, 704 ], + [ 29, 558, -51.47, 454, 370 ], + [ 714, 24, 216.39, 587, 441 ], + [ 535, 502, -159.67, 674, 655 ], + [ 647, 423, 184.07, 674, 466 ], + [ 899, 174, -341.28, 907, 453 ], + [ 345, 513, 329.85, 555, 617 ], + [ 573, 483, -101.39, 585, 655 ], + [ 97, 91, -251.33, 117, 119 ], + [ 818, 213, -197.64, 844, 449 ], + [ 927, 447, -288.8, 721, 1021 ], + [ 149, 468, 234.16, 465, 393 ], + [ 587, 235, 256.52, 364, 624 ], + [ 383, 636, 7.03, 458, 677 ], + [ 668, 353, -317, 729, 713 ], + [ 418, 258, -154.77, 487, 410 ], + [ 444, 516, 325.47, 656, 676 ], + [ 402, 178, 58.01, 363, 434 ], + [ 769, 250, 143.01, 764, 661 ], + [ 924, 95, 342.54, 909, 368 ], + [ 131, 22, 40.69, 113, 102 ], + [ 232, 133, -241.28, 228, 266 ], + [ 53, 573, -21.74, 260, 551 ], + [ 930, 92, -146.28, 823, 592 ], + [ 428, 593, 183.95, 466, 620 ], + [ 489, 138, -294.32, 327, 502 ], + [ 781, 471, 189.43, 846, 591 ], + [ 732, 4, 355.43, 729, 62 ], + [ 302, 580, -130.66, 635, 606 ], + [ 1018, 505, -42.07, 1092, 1057 ], + [ 993, 603, -22.08, 1146, 932 ], + [ 539, 8, 102.05, 120, 528 ], + [ 708, 48, -81.14, 155, 706 ], + [ 165, 745, 4.73, 225, 755 ], + [ 469, 298, -160.01, 541, 439 ], + [ 218, 664, -31.28, 530, 680 ], + [ 312, 482, -310.38, 569, 549 ], + [ 69, 262, -307.45, 249, 213 ], + [ 63, 489, 236.14, 440, 323 ], + [ 389, 484, -10.97, 472, 549 ], + [ 841, 460, -166.4, 924, 643 ], + [ 298, 96, -158.96, 311, 195 ], + [ 955, 626, -114.48, 964, 1127 ], + [ 327, 749, -136.45, 752, 767 ], + [ 846, 332, 142.16, 872, 779 ], + [ 107, 496, 44.36, 423, 429 ], + [ 574, 729, -224.62, 920, 921 ], + [ 645, 13, 20.69, 608, 239 ], + [ 719, 59, -241.73, 392, 660 ], + [ 543, 351, 211.16, 645, 579 ], + [ 16, 213, -78.13, 210, 59 ], + [ 291, 436, 11.09, 369, 483 ], + [ 577, 383, -122.45, 631, 691 ], + [ 926, 629, -140.49, 1113, 1073 ], + [ 220, 495, 170.69, 297, 522 ], + [ 934, 574, -84.97, 652, 980 ], + [ 485, 236, -121.77, 455, 535 ], + [ 678, 720, -110.28, 909, 884 ], + [ 195, 491, -55.09, 513, 440 ], + [ 363, 130, 273.14, 148, 369 ], + [ 152, 141, -247.66, 187, 193 ], + [ 553, 359, 160.46, 641, 521 ], + [ 56, 23, -200.49, 60, 40 ], + [ 394, 717, -331.63, 687, 818 ], + [ 706, 114, 131.79, 555, 601 ], + [ 437, 46, -50.28, 313, 365 ], + [ 828, 410, 170.92, 882, 534 ], + [ 143, 150, 124.98, 204, 202 ], + [ 498, 296, -135.53, 561, 558 ], + [ 932, 568, -14.46, 1043, 782 ], + [ 808, 681, 117.2, 975, 1028 ], + [ 968, 327, -266.95, 378, 982 ], + [ 684, 369, -245.86, 616, 774 ], + [ 911, 665, -294.01, 977, 1103 ], + [ 952, 426, -187.84, 1001, 550 ], + [ 627, 753, -145.19, 943, 974 ], + [ 44, 606, 238.3, 538, 354 ], + [ 799, 713, -342, 979, 924 ], + [ 464, 319, -65.38, 482, 554 ], + [ 784, 249, -253.58, 460, 821 ], + [ 251, 87, -335.43, 264, 183 ], + [ 930, 45, -29.07, 833, 490 ], + [ 38, 558, 316.6, 409, 431 ], + [ 200, 473, 219.12, 452, 492 ], + [ 294, 418, -102, 469, 373 ], + [ 539, 120, -116.07, 343, 536 ], + [ 845, 153, -268.05, 181, 848 ], + [ 672, 751, -319.72, 998, 1007 ], + [ 697, 331, -21.35, 769, 561 ], + [ 92, 151, 211.96, 157, 175 ], + [ 244, 339, 293.35, 406, 358 ], + [ 279, 267, 335.17, 364, 359 ], + [ 749, 296, 137.87, 754, 721 ], + [ 687, 446, -139.04, 809, 786 ], + [ 487, 666, 252.84, 778, 660 ], + [ 854, 302, -354.74, 878, 379 ], + [ 177, 167, -212.02, 239, 234 ], + [ 308, 670, -228.12, 704, 675 ], + [ 365, 425, 81.96, 472, 420 ], + [ 847, 494, 222.68, 956, 936 ], + [ 636, 676, -180.42, 640, 679 ], + [ 541, 116, 69.88, 295, 547 ], + [ 506, 678, 346.6, 648, 777 ], + [ 190, 425, 43.89, 431, 437 ], + [ 504, 22, -52.24, 324, 411 ], + [ 352, 662, 230.37, 733, 692 ], + [ 359, 27, -333.47, 333, 184 ], + [ 570, 257, 294.55, 469, 625 ], + [ 757, 530, -27.59, 914, 820 ], + [ 362, 397, -285.26, 478, 453 ], + [ 167, 106, 347.98, 184, 138 ], + [ 422, 511, -263.32, 557, 477 ], + [ 9, 122, -348.21, 33, 120 ], + [ 1014, 264, 49.91, 855, 945 ], + [ 565, 642, 70.15, 795, 749 ], + [ 1003, 138, -29.48, 940, 613 ], + [ 818, 407, -5.06, 849, 477 ], + [ 856, 66, 235.38, 539, 740 ], + [ 763, 634, 12.1, 879, 779 ], + [ 212, 137, -336.25, 249, 210 ], + [ 318, 262, -237.7, 390, 407 ], + [ 301, 578, 241.1, 650, 541 ], + [ 438, 584, 355.3, 483, 617 ], + [ 455, 577, -117.24, 720, 667 ], + [ 14, 232, -235.53, 198, 141 ], + [ 626, 216, -106.74, 386, 660 ], + [ 72, 61, 274.4, 65, 76 ], + [ 577, 702, 335.87, 812, 876 ], + [ 399, 506, 209.55, 596, 635 ], + [ 929, 665, 337.4, 1112, 971 ], + [ 962, 135, -137.9, 803, 743 ], + [ 112, 360, -316.76, 328, 338 ], + [ 967, 688, 230.71, 1143, 1183 ], + [ 961, 261, 51.08, 806, 911 ], + [ 282, 271, 268.25, 278, 288 ], + [ 459, 740, -250.03, 852, 683 ], + [ 819, 26, -193.37, 802, 213 ], + [ 697, 458, -255.25, 620, 790 ], + [ 663, 757, 218.88, 990, 1004 ], + [ 449, 685, 121.42, 819, 739 ], + [ 417, 639, 247.11, 750, 632 ], + [ 733, 723, -136.06, 1028, 1028 ], + [ 794, 680, -211.41, 1031, 992 ], + [ 610, 632, 32.85, 855, 861 ], + [ 171, 75, 286.64, 119, 184 ], + [ 40, 419, 260.1, 418, 110 ], + [ 197, 264, 283.76, 301, 254 ], + [ 434, 19, -7.45, 431, 75 ], + [ 583, 516, -18.13, 714, 671 ], + [ 382, 84, -96.2, 124, 387 ], + [ 43, 397, -217.9, 277, 338 ], + [ 906, 178, -260.93, 318, 921 ], + [ 201, 5, -344.2, 194, 59 ], + [ 412, 462, 247.6, 583, 555 ], + [ 971, 590, 317.22, 1112, 1092 ], + [ 866, 620, -10.88, 966, 772 ], + [ 724, 768, 236.57, 1038, 1026 ], + [ 616, 325, 253.97, 481, 681 ], + [ 93, 761, 17.18, 313, 754 ], + [ 580, 280, 95.75, 337, 604 ], + [ 955, 311, -252.94, 577, 1002 ], + [ 133, 223, -86.78, 229, 145 ], + [ 274, 373, -82.53, 404, 319 ], + [ 296, 682, 350.52, 402, 721 ], + [ 136, 641, 183.43, 172, 647 ], + [ 76, 523, 44.45, 420, 426 ], + [ 572, 298, 43.57, 619, 610 ], + [ 397, 759, 347.43, 551, 827 ], + [ 246, 61, 340.77, 251, 139 ], + [ 782, 495, -117.54, 799, 921 ], + [ 406, 443, 173.94, 450, 482 ], + [ 559, 542, -337.71, 723, 713 ], + [ 947, 747, 11.49, 1077, 920 ], + [ 318, 201, -69.91, 297, 367 ], + [ 304, 303, -189.04, 348, 345 ], + [ 53, 359, 300.74, 335, 229 ], + [ 628, 20, -140.49, 496, 413 ], + [ 178, 317, -148.96, 314, 362 ], + [ 161, 501, -92.13, 505, 178 ], + [ 133, 319, -348.12, 196, 339 ], + [ 478, 624, 66.87, 761, 684 ], + [ 213, 274, -145.07, 330, 345 ], + [ 245, 216, -164.86, 291, 271 ], + [ 773, 454, 23.31, 889, 722 ], + [ 162, 682, 307.04, 640, 540 ], + [ 405, 722, -76.47, 795, 562 ], + [ 321, 425, -58.1, 529, 497 ], + [ 633, 558, 238.73, 804, 830 ], + [ 933, 678, -12.03, 1052, 857 ], + [ 290, 10, 286.84, 93, 280 ], + [ 307, 368, 151.05, 446, 469 ], + [ 914, 527, -312.32, 1005, 1030 ], + [ 196, 711, -110.04, 734, 427 ], + [ 473, 522, 315.35, 702, 703 ], + [ 1019, 643, 234.38, 1115, 1201 ], + [ 943, 179, 190.45, 958, 346 ], + [ 322, 419, -131.97, 526, 518 ], + [ 752, 126, -329.31, 710, 491 ], + [ 579, 57, 305.87, 384, 502 ], + [ 50, 274, 314.31, 229, 226 ], + [ 970, 504, -61.53, 904, 1092 ], + [ 673, 422, -287.69, 606, 769 ], + [ 741, 568, -341.9, 880, 770 ], + [ 72, 155, 147.35, 144, 168 ], + [ 9, 150, 30.13, 82, 134 ], + [ 854, 89, 197.41, 840, 339 ], + [ 613, 331, -130.07, 646, 681 ], + [ 937, 126, -12.79, 940, 330 ], + [ 981, 560, 40.29, 1110, 1061 ], + [ 904, 598, 297.04, 942, 1077 ], + [ 839, 557, 49.32, 968, 999 ], + [ 239, 293, -275.98, 315, 268 ], + [ 342, 80, 256.08, 159, 349 ], + [ 368, 134, -319.39, 366, 341 ], + [ 66, 141, -179.05, 66, 141 ], + [ 620, 13, -95.86, 75, 616 ], + [ 389, 472, 213.71, 584, 607 ], + [ 716, 407, 331.54, 822, 699 ], + [ 186, 374, 136.35, 392, 398 ], + [ 755, 284, -23.86, 804, 565 ], + [ 666, 145, -222.27, 590, 553 ], + [ 224, 324, 52.16, 393, 375 ], + [ 343, 580, 127.12, 668, 622 ], + [ 196, 88, 252.56, 141, 211 ], + [ 253, 137, -336.17, 286, 227 ], + [ 511, 602, -324.69, 764, 786 ], + [ 886, 125, 117.44, 519, 843 ], + [ 678, 662, -221.74, 946, 944 ], + [ 638, 33, 128.46, 422, 519 ], + [ 320, 599, -326.1, 599, 675 ], + [ 798, 330, -191.1, 847, 476 ], + [ 994, 498, 111.94, 833, 1107 ], + [ 556, 755, -64.69, 919, 825 ], + [ 225, 133, 17.12, 254, 193 ], + [ 72, 323, 144.73, 245, 304 ], + [ 106, 706, -11.75, 246, 712 ], + [ 156, 602, 105.1, 621, 306 ], + [ 835, 343, 5.2, 862, 417 ], + [ 8, 53, 35.77, 37, 47 ], + [ 702, 292, -122.9, 625, 747 ], + [ 751, 122, 186.23, 758, 201 ], + [ 991, 99, -67.53, 468, 953 ], + [ 638, 516, -80.16, 616, 716 ], + [ 34, 684, 44.43, 503, 511 ], + [ 570, 737, 346.85, 722, 847 ], + [ 264, 48, 288.03, 126, 266 ], + [ 760, 165, 132.14, 631, 673 ], + [ 397, 23, -267.13, 42, 396 ], + [ 916, 28, -218.24, 736, 587 ], + [ 455, 555, 55.17, 715, 690 ], + [ 792, 349, 169.23, 843, 489 ], + [ 545, 324, 261.67, 398, 585 ], + [ 961, 34, 24.65, 887, 431 ], + [ 335, 60, -54.7, 241, 308 ], + [ 910, 487, -83.82, 580, 956 ], + [ 892, 685, -38.88, 1123, 1092 ], + [ 789, 486, 115.43, 777, 920 ], + [ 465, 15, 136.55, 347, 329 ], + [ 597, 702, 162.75, 778, 846 ], + [ 799, 275, -37.53, 800, 704 ], + [ 365, 101, 289.64, 216, 377 ], + [ 94, 510, -126.88, 463, 380 ], + [ 978, 462, -95.12, 546, 1014 ], + [ 501, 713, -39.78, 840, 868 ], + [ 936, 628, -303.76, 1042, 1127 ], + [ 304, 528, 337.71, 480, 604 ], + [ 761, 352, 72.83, 560, 831 ], + [ 413, 701, 86.73, 723, 452 ], + [ 95, 380, 24.59, 244, 385 ], + [ 839, 211, 282.2, 382, 865 ], + [ 274, 485, -343.38, 401, 543 ], + [ 173, 555, 134.13, 518, 509 ], + [ 505, 520, -152.84, 685, 692 ], + [ 882, 128, -204.43, 856, 480 ], + [ 313, 61, -336.21, 311, 182 ], + [ 393, 289, 158.67, 471, 410 ], + [ 361, 92, -231.78, 295, 339 ], + [ 977, 401, -295.42, 781, 1054 ], + [ 588, 472, 60.64, 699, 743 ], + [ 111, 333, 258.13, 347, 175 ], + [ 1, 538, -7.75, 72, 533 ], + [ 24, 668, -207.79, 332, 601 ], + [ 727, 278, 279.69, 395, 763 ], + [ 1022, 171, -320.66, 898, 779 ], + [ 219, 406, -263.54, 427, 262 ], + [ 253, 700, 7.25, 338, 725 ], + [ 991, 203, 92.81, 251, 998 ], + [ 149, 167, -251.44, 205, 193 ], + [ 241, 608, 191.06, 352, 642 ], + [ 839, 691, -216.96, 1085, 1055 ], + [ 694, 691, 287.01, 863, 865 ], + [ 171, 709, 192.56, 319, 728 ], + [ 388, 486, -267.17, 504, 410 ], + [ 273, 484, -107.27, 542, 403 ], + [ 329, 648, 272.46, 660, 356 ], + [ 466, 69, 208.26, 442, 280 ], + [ 989, 44, -322.16, 808, 641 ], + [ 609, 155, -165.97, 627, 296 ], + [ 763, 335, -316.43, 783, 768 ], + [ 523, 196, 331.05, 551, 425 ], + [ 726, 716, 258.65, 843, 851 ], + [ 623, 76, 203, 602, 312 ], + [ 384, 366, -61.76, 502, 511 ], + [ 516, 571, -328.46, 738, 756 ], + [ 876, 49, 278.47, 176, 873 ], + [ 751, 399, 342.82, 834, 602 ], + [ 536, 372, -336.75, 639, 553 ], + [ 589, 60, -191.98, 588, 180 ], + [ 865, 632, 121.69, 992, 1067 ], + [ 927, 256, 304.9, 739, 906 ], + [ 886, 32, 255.78, 247, 865 ], + [ 742, 499, 326.58, 893, 824 ], + [ 519, 19, -50.51, 344, 412 ], + [ 943, 406, -235.77, 866, 1006 ], + [ 988, 294, -189.88, 1023, 458 ], + [ 872, 89, 183.64, 875, 143 ], + [ 847, 491, -187.97, 906, 602 ], + [ 880, 164, -131.9, 708, 763 ], + [ 95, 45, -259.59, 61, 100 ], + [ 780, 740, -19.97, 985, 962 ], + [ 702, 636, 10, 801, 747 ], + [ 554, 424, -242.69, 631, 686 ], + [ 506, 45, -225.48, 386, 391 ], + [ 946, 752, 154.49, 1177, 1085 ], + [ 99, 724, -290.08, 713, 341 ], + [ 341, 610, -206.71, 578, 697 ], + [ 90, 477, 252.85, 481, 225 ], + [ 335, 368, -313.57, 497, 496 ], + [ 659, 440, -271.91, 461, 673 ], + [ 802, 257, -298.62, 610, 827 ], + [ 262, 15, 297.37, 132, 239 ], + [ 788, 430, -25.32, 895, 726 ], + [ 955, 41, 16.33, 928, 307 ], + [ 122, 750, 0.56, 128, 751 ], + [ 854, 56, -41.09, 679, 603 ], + [ 953, 312, -190.15, 993, 473 ], + [ 147, 379, 256.2, 402, 231 ], + [ 1019, 630, -118.81, 1042, 1195 ], + [ 61, 356, 292.86, 350, 194 ], + [ 186, 189, -187.11, 207, 210 ], + [ 274, 385, -173.82, 312, 411 ], + [ 184, 208, 228.5, 276, 274 ], + [ 660, 155, 266.41, 195, 667 ], + [ 172, 247, 249.42, 290, 247 ], + [ 684, 119, 301.76, 460, 644 ], + [ 114, 65, -126.46, 118, 129 ], + [ 356, 175, 228.43, 366, 381 ], + [ 208, 171, 98.43, 199, 229 ], + [ 551, 216, -288.35, 378, 590 ], + [ 456, 356, -111.48, 496, 553 ], + [ 701, 560, 249.07, 772, 853 ], + [ 966, 698, -276.73, 806, 1041 ], + [ 779, 425, -131.54, 833, 864 ], + [ 646, 554, 99.15, 649, 724 ], + [ 489, 299, 209.84, 572, 501 ], + [ 406, 384, 270.44, 386, 408 ], + [ 738, 650, -285.75, 826, 886 ], + [ 551, 38, -125.87, 352, 467 ], + [ 179, 451, 76.5, 480, 279 ], + [ 281, 24, -310.54, 200, 229 ], + [ 634, 550, 214.41, 833, 811 ], + [ 477, 506, 148.69, 670, 678 ], + [ 595, 322, -173, 628, 391 ], + [ 920, 38, 345.67, 899, 264 ], + [ 549, 404, -94.74, 447, 579 ], + [ 335, 708, 264.69, 735, 398 ], + [ 208, 494, 153.81, 404, 533 ], + [ 314, 138, 188.81, 330, 183 ], + [ 647, 272, -109.12, 467, 699 ], + [ 243, 482, -87.11, 492, 266 ], + [ 314, 190, -292.29, 295, 362 ], + [ 107, 547, 181.54, 120, 548 ], + [ 830, 225, -237.15, 639, 818 ], + [ 72, 146, -201.94, 121, 160 ], + [ 52, 557, 176.6, 84, 558 ], + [ 429, 41, 120.19, 250, 390 ], + [ 293, 196, -137.62, 347, 341 ], + [ 1024, 431, -8.38, 1075, 575 ], + [ 781, 147, 246.32, 447, 773 ], + [ 113, 330, -21.87, 226, 348 ], + [ 502, 564, 155.6, 690, 720 ], + [ 598, 644, -52.24, 874, 866 ], + [ 344, 498, 157.9, 505, 589 ], + [ 519, 552, 294.48, 716, 701 ], + [ 744, 590, 96.63, 671, 806 ], + [ 483, 144, 134.21, 439, 445 ], + [ 142, 363, 317.65, 348, 363 ], + [ 458, 363, 1.19, 465, 372 ], + [ 958, 180, 138.96, 840, 764 ], + [ 796, 265, -272.53, 300, 807 ], + [ 253, 641, 256.2, 681, 397 ], + [ 987, 321, 139.53, 958, 883 ], + [ 400, 578, -113.84, 689, 598 ], + [ 113, 199, -316.9, 218, 222 ], + [ 20, 756, 236.02, 637, 438 ], + [ 669, 350, -349.85, 720, 462 ], + [ 348, 456, -9.77, 418, 508 ], + [ 288, 31, 330.03, 263, 170 ], + [ 801, 749, -221.64, 1096, 1091 ], + [ 487, 578, 26.13, 692, 733 ], + [ 611, 768, -93.8, 805, 659 ], + [ 464, 739, 206.31, 742, 866 ], + [ 150, 271, 27.98, 259, 309 ], + [ 500, 355, 214.08, 612, 573 ], + [ 562, 370, 202.66, 660, 556 ], + [ 385, 103, -189.69, 396, 165 ], + [ 400, 364, 237.25, 521, 532 ], + [ 898, 579, 265.83, 641, 936 ], + [ 855, 412, 247.68, 704, 945 ], + [ 28, 8, 69.11, 16, 29 ], + [ 577, 467, 68.26, 647, 708 ], + [ 956, 46, 40.72, 754, 658 ], + [ 737, 158, 294.26, 445, 736 ], + [ 265, 533, -93.23, 545, 293 ], + [ 57, 186, 252.73, 193, 108 ], + [ 858, 475, 348.77, 933, 633 ], + [ 76, 6, -30.9, 67, 44 ], + [ 925, 679, -208.65, 1137, 1038 ], + [ 791, 552, 176.74, 820, 594 ], + [ 632, 573, 184.2, 671, 616 ], + [ 218, 237, -98.78, 266, 250 ], + [ 827, 186, -56.01, 615, 789 ], + [ 373, 738, 92.05, 751, 397 ], + [ 279, 169, -128.47, 304, 322 ], + [ 659, 211, 45.56, 612, 618 ], + [ 506, 86, -229.86, 392, 440 ], + [ 490, 143, -224.56, 449, 444 ], + [ 958, 68, -308.34, 647, 793 ], + [ 150, 661, 208.86, 449, 650 ], + [ 913, 367, 25.37, 981, 723 ], + [ 663, 530, 247.34, 743, 814 ], + [ 10, 381, -299.04, 337, 193 ], + [ 443, 661, -327.45, 729, 795 ], + [ 62, 102, -169.91, 78, 109 ], + [ 391, 596, 27.65, 623, 709 ], + [ 968, 207, 107.88, 494, 984 ], + [ 129, 574, 241.1, 564, 388 ], + [ 321, 524, 304.55, 613, 561 ], + [ 395, 636, 204.06, 618, 741 ], + [ 180, 553, -172.71, 247, 570 ], + [ 728, 281, 323.36, 751, 659 ], + [ 568, 289, -39.48, 621, 584 ], + [ 655, 620, -140.52, 898, 894 ], + [ 701, 666, -45.48, 965, 966 ], + [ 971, 191, -207.62, 949, 618 ], + [ 499, 149, -14.17, 518, 266 ], + [ 140, 245, -196.4, 203, 273 ], + [ 995, 485, 294.81, 856, 1107 ], + [ 916, 13, 170.8, 906, 158 ], + [ 694, 148, -31.69, 667, 490 ], + [ 961, 694, 231.56, 1140, 1182 ], + [ 908, 351, -218.24, 930, 837 ], + [ 340, 74, -357.1, 343, 91 ], + [ 652, 599, 267.04, 630, 681 ], + [ 76, 559, -274.31, 562, 117 ], + [ 293, 164, -135.55, 323, 321 ], + [ 784, 269, 93.44, 316, 797 ], + [ 1017, 247, -171.9, 1040, 387 ], + [ 924, 165, -159.9, 923, 471 ], + [ 655, 686, -21.69, 861, 879 ], + [ 98, 638, -91.92, 640, 117 ], + [ 939, 551, 236.78, 974, 1086 ], + [ 117, 38, 305.36, 97, 117 ], + [ 121, 527, 146.37, 392, 505 ], + [ 1013, 583, -50.09, 1095, 1151 ], + [ 111, 34, 103.63, 59, 114 ], + [ 430, 622, 355.41, 477, 654 ], + [ 51, 616, -133.67, 480, 460 ], + [ 318, 541, 20.97, 490, 618 ], + [ 603, 264, -56.65, 551, 648 ], + [ 60, 337, -179.21, 63, 336 ], + [ 441, 274, 336.64, 512, 426 ], + [ 266, 361, -348.6, 331, 406 ], + [ 189, 452, 144.59, 416, 476 ], + [ 909, 443, -29.3, 1008, 830 ], + [ 325, 526, 2.12, 343, 538 ], + [ 985, 80, -135.32, 755, 748 ], + [ 978, 118, -278.03, 253, 984 ], + [ 276, 356, 228.81, 448, 440 ], + [ 818, 40, -244.03, 394, 752 ], + [ 225, 85, 71.79, 151, 240 ], + [ 483, 416, 327.8, 629, 609 ], + [ 449, 615, -54.23, 760, 723 ], + [ 466, 756, -351.4, 573, 816 ], + [ 159, 669, 63.34, 669, 442 ], + [ 630, 145, -164.34, 644, 309 ], + [ 121, 115, -89.96, 114, 120 ], + [ 440, 80, 19.91, 440, 224 ], + [ 557, 287, 353.35, 585, 349 ], + [ 370, 132, -329.54, 385, 301 ], + [ 535, 301, -250.42, 463, 604 ], + [ 125, 663, 138.77, 531, 580 ], + [ 79, 231, -5.19, 98, 237 ], + [ 514, 580, -16.94, 659, 704 ], + [ 526, 699, -110.11, 835, 732 ], + [ 105, 403, 25.22, 266, 409 ], + [ 383, 493, 132.67, 621, 614 ], + [ 767, 55, 207.66, 704, 404 ], + [ 283, 472, -243.5, 548, 463 ], + [ 274, 749, -119.81, 785, 608 ], + [ 317, 382, 162.07, 419, 459 ], + [ 473, 476, 62.93, 639, 638 ], + [ 158, 535, 279.41, 552, 242 ], + [ 663, 152, 102.91, 296, 679 ], + [ 127, 547, 38.04, 437, 509 ], + [ 482, 625, -304.52, 788, 751 ], + [ 6, 143, 157.38, 60, 133 ], + [ 707, 718, 208.39, 961, 967 ], + [ 490, 162, -69.52, 322, 516 ], + [ 656, 367, -87.51, 394, 671 ], + [ 997, 605, 270.38, 610, 1000 ], + [ 716, 191, -4.08, 727, 241 ], + [ 293, 309, -223.07, 425, 425 ], + [ 179, 38, -80.84, 65, 182 ], + [ 837, 133, -288.78, 395, 835 ], + [ 379, 494, 296.46, 609, 559 ], + [ 458, 498, -289.49, 621, 597 ], + [ 170, 260, -336.57, 258, 306 ], + [ 978, 627, -184.85, 1027, 706 ], + [ 367, 351, 158.99, 468, 458 ], + [ 755, 330, 5.41, 782, 400 ], + [ 628, 99, 184.02, 632, 142 ], + [ 110, 319, -246.44, 335, 227 ], + [ 304, 452, -211.14, 494, 543 ], + [ 702, 312, -87.03, 347, 717 ], + [ 803, 596, -344.23, 934, 792 ], + [ 712, 171, 122.5, 526, 691 ], + [ 813, 298, -354.2, 838, 378 ], + [ 846, 263, 233.36, 714, 834 ], + [ 71, 539, -158.35, 263, 526 ], + [ 809, 337, 150.89, 870, 686 ], + [ 305, 39, 243.13, 171, 289 ], + [ 55, 121, -178.71, 56, 121 ], + [ 216, 349, 244.81, 406, 343 ], + [ 428, 106, -183.44, 433, 130 ], + [ 816, 708, -345.39, 968, 890 ], + [ 839, 474, -185.56, 881, 552 ], + [ 501, 315, 93.42, 343, 518 ], + [ 509, 181, 339.95, 539, 344 ], + [ 329, 234, 124.48, 379, 402 ], + [ 616, 73, -280.47, 183, 618 ], + [ 318, 301, -244.54, 408, 415 ], + [ 152, 342, -131.81, 355, 340 ], + [ 410, 504, 194.66, 523, 590 ], + [ 246, 56, 294.02, 150, 247 ], + [ 497, 668, 237.51, 828, 777 ], + [ 518, 529, -38.43, 733, 735 ], + [ 766, 136, 256.73, 306, 775 ], + [ 387, 517, -293.6, 628, 561 ], + [ 358, 763, -74.25, 830, 551 ], + [ 23, 457, -6.24, 71, 456 ], + [ 137, 695, 275.85, 703, 207 ], + [ 301, 41, -123.98, 201, 271 ], + [ 625, 348, 347.85, 682, 471 ], + [ 390, 532, -319.83, 641, 658 ], + [ 296, 137, 306, 283, 320 ], + [ 122, 525, 90, 525, 122 ], + [ 579, 331, 210, 666, 575 ], + [ 433, 621, -30, 684, 754 ], + [ 527, 121, -30, 516, 368 ], + [ 933, 138, -330, 877, 586 ], + [ 413, 240, 330, 476, 414 ], + [ 293, 466, -360, 293, 466 ], + [ 629, 166, -30, 626, 458 ], + [ 611, 458, -270, 458, 611 ], + [ 668, 680, -120, 922, 917 ], + [ 592, 3, -360, 592, 3 ], + [ 195, 438, -30, 386, 476 ], + [ 1003, 762, -180, 1003, 762 ], + [ 451, 389, -60, 561, 584 ], + [ 917, 320, 60, 735, 954 ], + [ 967, 629, 300, 1027, 1151 ], + [ 920, 80, 0, 920, 80 ], + [ 926, 551, 150, 1076, 938 ], + [ 372, 242, -150, 442, 395 ], + [ 526, 742, -240, 905, 825 ], + [ 862, 418, -210, 955, 791 ], + [ 873, 563, 210, 1037, 923 ], + [ 300, 547, 210, 532, 623 ], + [ 554, 117, -180, 554, 117 ], + [ 969, 36, 150, 857, 514 ], + [ 544, 761, 0, 544, 761 ], + [ 438, 683, 90, 683, 438 ], + [ 953, 609, -120, 1002, 1129 ], + [ 664, 120, 90, 120, 664 ], + [ 986, 514, 90, 514, 986 ], + [ 167, 392, -270, 392, 167 ], + [ 375, 279, 300, 428, 463 ], + [ 142, 503, 60, 506, 373 ], + [ 923, 151, 180, 923, 151 ], + [ 175, 109, 210, 205, 180 ], + [ 876, 517, 210, 1016, 885 ], + [ 368, 78, 120, 251, 356 ], + [ 990, 24, -30, 868, 516 ], + [ 299, 522, 90, 522, 299 ], + [ 954, 493, -150, 1072, 903 ], + [ 549, 618, 210, 783, 808 ], + [ 701, 130, -240, 463, 671 ], + [ 579, 236, 240, 492, 618 ], + [ 162, 160, -360, 162, 160 ], + [ 925, 291, -270, 291, 925 ], + [ 711, 180, 240, 510, 704 ], + [ 488, 455, 330, 649, 638 ], + [ 150, 429, 330, 343, 447 ], + [ 625, 653, 90, 653, 625 ], + [ 235, 600, -180, 235, 600 ], + [ 529, 244, -300, 475, 580 ], + [ 322, 1, -180, 322, 1 ], + [ 487, 435, 30, 638, 620 ], + [ 298, 556, 210, 535, 630 ], + [ 309, 482, -270, 482, 309 ], + [ 466, 246, -90, 246, 466 ], + [ 287, 612, 360, 287, 612 ], + [ 211, 724, 30, 544, 732 ], + [ 167, 425, -90, 425, 167 ], + [ 818, 600, -270, 600, 818 ], + [ 77, 229, -30, 180, 236 ], + [ 160, 470, -30, 372, 487 ], + [ 416, 67, -330, 393, 265 ], + [ 987, 289, 210, 998, 742 ], + [ 747, 521, -90, 521, 747 ], + [ 894, 101, 150, 824, 532 ], + [ 256, 316, 0, 256, 316 ], + [ 225, 474, -30, 430, 522 ], + [ 765, 599, -360, 765, 599 ], + [ 128, 444, 210, 331, 448 ], + [ 291, 502, -300, 580, 503 ], + [ 778, 621, 150, 983, 925 ], + [ 241, 165, 210, 290, 262 ], + [ 284, 137, -270, 137, 284 ], + [ 44, 697, 240, 625, 386 ], + [ 446, 599, 360, 446, 599 ], + [ 152, 23, -90, 23, 152 ], + [ 673, 191, 360, 673, 191 ], + [ 110, 764, 210, 476, 716 ], + [ 113, 92, -120, 135, 142 ], + [ 916, 309, -360, 916, 309 ], + [ 999, 124, 240, 605, 926 ], + [ 683, 305, -360, 683, 305 ], + [ 456, 517, -210, 652, 674 ], + [ 614, 550, -300, 782, 806 ], + [ 441, 116, 300, 319, 439 ], + [ 403, 230, 300, 399, 464 ], + [ 786, 734, -240, 1028, 1046 ], + [ 777, 48, -270, 48, 777 ], + [ 543, 133, -180, 543, 133 ], + [ 899, 52, 150, 804, 493 ], + [ 900, 32, 270, 32, 900 ], + [ 704, 544, -210, 881, 821 ], + [ 728, 118, 300, 464, 689 ], + [ 935, 577, 90, 577, 935 ], + [ 22, 140, 210, 88, 131 ], + [ 931, 441, -330, 1026, 847 ], + [ 846, 263, -360, 846, 263 ], + [ 1015, 77, -300, 574, 917 ], + [ 154, 484, -180, 154, 484 ], + [ 439, 390, -120, 556, 574 ], + [ 112, 297, -90, 297, 112 ], + [ 974, 56, -300, 534, 871 ], + [ 194, 602, -210, 469, 616 ], + [ 103, 535, 360, 103, 535 ], + [ 744, 551, -240, 848, 918 ], + [ 517, 481, 180, 517, 481 ], + [ 595, 348, -300, 598, 689 ], + [ 627, 341, -240, 608, 711 ], + [ 723, 456, 240, 755, 853 ], + [ 996, 17, -150, 870, 512 ], + [ 312, 101, 150, 320, 241 ], + [ 695, 63, 210, 632, 401 ], + [ 779, 208, 60, 569, 778 ], + [ 1018, 285, -180, 1018, 285 ], + [ 1003, 75, 360, 1003, 75 ], + [ 149, 462, 90, 462, 149 ], + [ 907, 140, -330, 855, 574 ], + [ 991, 610, 0, 991, 610 ], + [ 153, 384, -300, 409, 324 ], + [ 984, 363, -300, 805, 1033 ], + [ 287, 597, -180, 287, 597 ], + [ 1008, 350, -120, 806, 1046 ], + [ 780, 165, 60, 532, 757 ], + [ 331, 161, 360, 331, 161 ], + [ 594, 144, -330, 586, 421 ], + [ 568, 256, 120, 505, 618 ], + [ 160, 168, 270, 168, 160 ], + [ 214, 143, 90, 143, 214 ], + [ 722, 257, -270, 257, 722 ], + [ 813, 227, 90, 227, 813 ], + [ 926, 441, -90, 441, 926 ], + [ 149, 429, 240, 445, 343 ], + [ 480, 246, -330, 538, 452 ], + [ 64, 493, -180, 64, 493 ], + [ 26, 171, -30, 107, 161 ], + [ 87, 597, 210, 373, 559 ], + [ 764, 716, 360, 764, 716 ], + [ 602, 108, -240, 394, 574 ], + [ 229, 649, 0, 229, 649 ], + [ 367, 490, 240, 606, 561 ], + [ 989, 416, -90, 416, 989 ], + [ 357, 528, 330, 572, 635 ], + [ 190, 119, -180, 190, 119 ], + [ 243, 167, 300, 265, 293 ], + [ 510, 186, -270, 186, 510 ], + [ 968, 251, 300, 699, 963 ], + [ 743, 55, 240, 418, 670 ], + [ 741, 506, 360, 741, 506 ], + [ 928, 678, 240, 1050, 1141 ], + [ 434, 185, -30, 467, 377 ], + [ 241, 158, 360, 241, 158 ], + [ 636, 425, 150, 762, 684 ], + [ 578, 568, 240, 780, 783 ], + [ 847, 750, -330, 1108, 1073 ], + [ 750, 364, -150, 830, 689 ], + [ 880, 322, 90, 322, 880 ], + [ 780, 111, 150, 730, 484 ], + [ 610, 666, -120, 881, 860 ], + [ 584, 594, -210, 802, 804 ], + [ 398, 151, -30, 419, 330 ], + [ 864, 331, 120, 718, 912 ], + [ 861, 39, -210, 764, 463 ], + [ 536, 472, 360, 536, 472 ], + [ 370, 340, 330, 489, 479 ], + [ 425, 134, -60, 327, 435 ], + [ 728, 25, 240, 385, 642 ], + [ 341, 610, 360, 341, 610 ], + [ 27, 299, 270, 299, 27 ], + [ 388, 737, 90, 737, 388 ], + [ 628, 267, 240, 544, 676 ], + [ 90, 147, -180, 90, 147 ], + [ 785, 24, -150, 690, 412 ], + [ 1023, 426, 300, 879, 1098 ], + [ 994, 703, -120, 1105, 1211 ], + [ 939, 253, 30, 939, 688 ], + [ 344, 31, 60, 198, 312 ], + [ 179, 287, -120, 337, 298 ], + [ 160, 307, 180, 160, 307 ], + [ 6, 597, 150, 303, 518 ], + [ 643, 300, -330, 706, 581 ], + [ 456, 367, -210, 577, 544 ], + [ 710, 188, -180, 710, 188 ], + [ 1016, 187, 240, 669, 972 ], + [ 927, 164, 180, 927, 164 ], + [ 267, 99, -330, 280, 219 ], + [ 808, 357, -300, 712, 877 ], + [ 358, 491, -30, 555, 604 ], + [ 721, 611, 270, 611, 721 ], + [ 458, 615, 120, 761, 702 ], + [ 153, 328, -330, 296, 360 ], + [ 204, 670, 30, 511, 681 ], + [ 449, 434, 210, 604, 599 ], + [ 725, 428, -360, 725, 428 ], + [ 545, 355, -210, 648, 578 ], + [ 277, 554, -120, 617, 515 ], + [ 345, 392, 240, 510, 493 ], + [ 455, 660, -30, 723, 799 ], + [ 946, 432, -180, 946, 432 ], + [ 822, 7, -330, 714, 416 ], + [ 469, 122, -30, 466, 340 ], + [ 516, 274, -120, 494, 582 ], + [ 27, 611, 300, 541, 328 ], + [ 812, 386, -30, 895, 740 ], + [ 822, 27, 330, 724, 434 ], + [ 24, 234, 120, 214, 136 ], + [ 383, 492, -270, 492, 383 ], + [ 194, 65, -360, 194, 65 ], + [ 658, 7, 60, 334, 572 ], + [ 912, 623, 60, 995, 1100 ], + [ 961, 206, 180, 961, 206 ], + [ 424, 593, -300, 725, 663 ], + [ 778, 613, 300, 918, 979 ], + [ 687, 454, -60, 735, 821 ], + [ 142, 302, -60, 331, 273 ], + [ 69, 320, 180, 69, 320 ], + [ 734, 607, -90, 607, 734 ], + [ 898, 752, -30, 1152, 1100 ], + [ 916, 477, -30, 1031, 871 ], + [ 487, 393, -150, 617, 582 ], + [ 66, 346, 30, 230, 332 ], + [ 832, 663, -120, 989, 1051 ], + [ 938, 478, -300, 882, 1051 ], + [ 834, 225, 150, 834, 610 ], + [ 221, 331, -300, 397, 356 ], + [ 666, 382, 0, 666, 382 ], + [ 398, 165, -150, 426, 341 ], + [ 786, 70, -150, 714, 453 ], + [ 236, 757, 300, 772, 582 ], + [ 718, 353, -60, 663, 797 ], + [ 1006, 403, 270, 403, 1006 ], + [ 545, 258, 180, 545, 258 ], + [ 874, 193, -120, 603, 852 ], + [ 996, 51, 120, 541, 886 ], + [ 699, 216, -330, 713, 536 ], + [ 805, 715, 60, 1021, 1054 ], + [ 312, 245, 210, 392, 367 ], + [ 628, 66, 270, 66, 628 ], + [ 922, 242, 270, 242, 922 ], + [ 827, 12, -120, 422, 721 ], + [ 231, 766, 270, 766, 231 ], + [ 95, 407, -240, 399, 284 ], + [ 842, 294, -60, 674, 876 ], + [ 178, 272, 0, 178, 272 ], + [ 838, 28, 210, 738, 442 ], + [ 914, 631, 150, 1106, 1001 ], + [ 476, 97, -360, 476, 97 ], + [ 257, 567, -300, 619, 505 ], + [ 131, 490, -60, 488, 358 ], + [ 1013, 342, -60, 801, 1048 ], + [ 317, 515, -60, 603, 531 ], + [ 157, 585, 360, 157, 585 ], + [ 341, 448, -120, 557, 518 ], + [ 781, 581, 150, 966, 892 ], + [ 265, 441, 180, 265, 441 ], + [ 35, 359, 30, 209, 328 ], + [ 757, 716, 150, 1013, 997 ], + [ 782, 141, -60, 511, 747 ], + [ 893, 238, -330, 892, 652 ], + [ 302, 230, 0, 302, 230 ], + [ 752, 467, -240, 779, 883 ], + [ 174, 585, 330, 442, 594 ], + [ 138, 72, 0, 138, 72 ], + [ 492, 658, -180, 492, 658 ], + [ 185, 88, 270, 88, 185 ], + [ 939, 113, -150, 869, 566 ], + [ 487, 681, -180, 487, 681 ], + [ 163, 499, -60, 512, 390 ], + [ 594, 400, 180, 594, 400 ], + [ 655, 626, -360, 655, 626 ], + [ 148, 422, 120, 438, 338 ], + [ 325, 552, -60, 639, 557 ], + [ 259, 655, 0, 259, 655 ], + [ 789, 255, -120, 614, 810 ], + [ 46, 394, -30, 235, 364 ], + [ 915, 330, 90, 330, 915 ], + [ 214, 695, 0, 214, 695 ], + [ 466, 48, -270, 48, 466 ], + [ 906, 494, 120, 880, 1030 ], + [ 638, 217, 0, 638, 217 ], + [ 676, 327, -360, 676, 327 ], + [ 312, 571, 180, 312, 571 ], + [ 727, 765, 90, 765, 727 ], + [ 214, 588, 330, 478, 616 ], + [ 576, 622, -30, 808, 827 ], + [ 31, 540, 270, 540, 31 ], + [ 661, 701, 210, 922, 936 ], + [ 149, 282, 270, 282, 149 ], + [ 302, 193, 0, 302, 193 ], + [ 987, 671, 210, 1189, 1073 ], + [ 459, 410, -240, 584, 601 ], + [ 492, 644, 330, 747, 804 ], + [ 236, 421, 330, 414, 483 ], + [ 853, 581, 150, 1028, 928 ], + [ 785, 245, 0, 785, 245 ], + [ 217, 268, -210, 321, 339 ], + [ 90, 763, -300, 705, 458 ], + [ 888, 107, 0, 888, 107 ], + [ 726, 333, 180, 726, 333 ], + [ 198, 305, 90, 305, 198 ], + [ 983, 649, -270, 649, 983 ], + [ 155, 250, 360, 155, 250 ], + [ 89, 427, 30, 290, 414 ], + [ 17, 298, -150, 162, 265 ], + [ 729, 118, 90, 118, 729 ], + [ 223, 280, 330, 332, 353 ], + [ 436, 347, 330, 550, 519 ], + [ 558, 244, -300, 489, 605 ], + [ 32, 20, 0, 32, 20 ], + [ 787, 169, 300, 538, 765 ], + [ 387, 137, 180, 387, 137 ], + [ 534, 253, -330, 588, 485 ], + [ 514, 320, 90, 320, 514 ], + [ 31, 332, -360, 31, 332 ], + [ 321, 111, -240, 256, 331 ], + [ 936, 277, 30, 948, 707 ], + [ 884, 604, 360, 884, 604 ], + [ 825, 254, -150, 840, 631 ], + [ 907, 278, -120, 693, 923 ], + [ 408, 100, 30, 403, 290 ], + [ 315, 390, 180, 315, 390 ], + [ 62, 22, -330, 64, 49 ], + [ 141, 407, -30, 325, 422 ], + [ 789, 429, 300, 765, 897 ], + [ 808, 670, -330, 1034, 983 ], + [ 984, 603, -60, 1012, 1153 ], + [ 509, 498, -240, 685, 688 ], + [ 496, 349, 30, 603, 549 ], + [ 808, 260, 300, 627, 829 ], + [ 122, 566, -360, 122, 566 ], + [ 684, 33, 30, 608, 370 ], + [ 405, 79, 30, 389, 270 ], + [ 463, 185, -330, 492, 391 ], + [ 947, 8, 90, 8, 947 ], + [ 850, 614, 0, 850, 614 ], + [ 899, 584, -150, 1069, 954 ], + [ 294, 197, 330, 352, 318 ], + [ 493, 568, -90, 568, 493 ], + [ 7, 406, 180, 7, 406 ], + [ 914, 497, -30, 1039, 887 ], + [ 930, 242, 0, 930, 242 ], + [ 440, 546, 90, 546, 440 ], + [ 1002, 125, 270, 125, 1002 ], + [ 58, 67, 270, 67, 58 ], + [ 710, 703, 120, 963, 964 ], + [ 205, 609, -60, 628, 481 ], + [ 505, 61, 120, 305, 466 ], + [ 445, 431, -60, 594, 600 ], + [ 848, 435, 330, 951, 801 ], + [ 574, 352, 60, 591, 673 ], + [ 29, 281, 300, 256, 165 ], + [ 552, 611, 90, 611, 552 ], + [ 155, 594, 210, 430, 590 ], + [ 4, 637, 270, 637, 4 ], + [ 887, 401, 210, 968, 789 ], + [ 539, 555, 60, 750, 743 ], + [ 968, 165, 120, 626, 919 ], + [ 635, 498, -210, 798, 747 ], + [ 36, 366, 210, 213, 334 ], + [ 1001, 28, -210, 880, 523 ], + [ 580, 49, 60, 331, 526 ], + [ 481, 463, -90, 463, 481 ], + [ 85, 579, -240, 543, 361 ], + [ 878, 581, 360, 878, 581 ], + [ 735, 478, 0, 735, 478 ], + [ 506, 113, -210, 494, 349 ], + [ 64, 71, -30, 90, 93 ], + [ 728, 547, -300, 837, 903 ], + [ 941, 574, 60, 967, 1101 ], + [ 710, 556, 90, 556, 710 ], + [ 948, 223, 120, 666, 930 ], + [ 536, 583, -180, 536, 583 ], + [ 938, 646, -360, 938, 646 ], + [ 83, 536, 210, 338, 504 ], + [ 76, 319, -60, 312, 224 ], + [ 603, 698, 30, 871, 905 ], + [ 846, 746, 120, 1068, 1104 ], + [ 284, 523, -120, 594, 506 ], + [ 385, 461, -300, 591, 563 ], + [ 974, 225, 240, 681, 955 ], + [ 575, 167, -300, 432, 580 ], + [ 241, 569, 270, 569, 241 ], + [ 524, 504, 150, 705, 696 ], + [ 554, 566, -60, 765, 762 ], + [ 334, 622, 240, 705, 599 ], + [ 750, 306, 180, 750, 306 ], + [ 294, 172, 150, 340, 294 ], + [ 955, 385, -90, 385, 955 ], + [ 297, 673, 360, 297, 673 ], + [ 403, 635, -180, 403, 635 ], + [ 226, 298, 0, 226, 298 ], + [ 345, 478, -210, 537, 585 ], + [ 216, 103, 300, 195, 238 ], + [ 948, 518, 90, 518, 948 ], + [ 346, 768, -60, 836, 683 ], + [ 169, 562, 240, 570, 426 ], + [ 921, 14, -330, 804, 472 ], + [ 618, 730, 30, 900, 940 ], + [ 1013, 184, -60, 664, 969 ], + [ 998, 486, -180, 998, 486 ], + [ 257, 655, 120, 695, 548 ], + [ 764, 146, -180, 764, 146 ], + [ 1004, 308, -60, 767, 1023 ], + [ 892, 250, -330, 897, 662 ], + [ 510, 510, -330, 696, 696 ], + [ 956, 636, 240, 1028, 1144 ], + [ 787, 558, 240, 875, 959 ], + [ 846, 253, 210, 858, 641 ], + [ 395, 245, -360, 395, 245 ], + [ 840, 225, 120, 614, 838 ], + [ 83, 417, 0, 83, 417 ], + [ 746, 222, 150, 757, 563 ], + [ 1022, 206, -300, 688, 988 ], + [ 412, 107, -60, 297, 409 ], + [ 491, 489, -330, 669, 668 ], + [ 569, 438, 300, 662, 711 ], + [ 372, 260, 90, 260, 372 ], + [ 194, 126, 330, 230, 206 ], + [ 988, 422, -180, 988, 422 ], + [ 1003, 283, 60, 746, 1009 ], + [ 641, 345, -300, 619, 727 ], + [ 129, 136, -90, 136, 129 ], + [ 835, 134, 120, 533, 789 ], + [ 926, 443, 240, 846, 1022 ], + [ 326, 42, -30, 302, 199 ], + [ 405, 469, -360, 405, 469 ], + [ 307, 747, -120, 799, 638 ], + [ 952, 124, 0, 952, 124 ], + [ 911, 96, -330, 836, 538 ], + [ 171, 80, -60, 153, 188 ], + [ 754, 561, 270, 561, 754 ], + [ 868, 657, -330, 1079, 1002 ], + [ 254, 515, -210, 476, 571 ], + [ 937, 441, 240, 849, 1031 ], + [ 740, 685, 270, 685, 740 ], + [ 169, 220, -30, 255, 275 ], + [ 183, 449, -30, 382, 480 ], + [ 541, 395, 90, 395, 541 ], + [ 44, 310, 180, 44, 310 ], + [ 108, 438, 240, 432, 311 ], + [ 549, 235, 30, 592, 478 ], + [ 425, 118, -60, 313, 427 ], + [ 481, 308, -300, 507, 570 ], + [ 715, 243, 120, 567, 739 ], + [ 515, 29, 30, 460, 282 ], + [ 370, 156, 240, 319, 397 ], + [ 849, 292, -60, 676, 881 ], + [ 275, 698, 330, 586, 741 ], + [ 891, 731, -120, 1077, 1136 ], + [ 610, 43, 300, 340, 549 ], + [ 434, 455, -210, 602, 609 ], + [ 1006, 6, -90, 6, 1006 ], + [ 405, 366, 210, 532, 518 ], + [ 491, 134, -330, 492, 361 ], + [ 139, 520, -300, 519, 380 ], + [ 699, 29, -150, 619, 373 ], + [ 528, 665, 150, 789, 838 ], + [ 955, 103, 90, 103, 955 ], + [ 884, 3, 30, 766, 444 ], + [ 203, 460, 90, 460, 203 ], + [ 104, 17, -210, 98, 65 ], + [ 319, 3, -210, 277, 161 ], + [ 691, 307, 150, 751, 610 ], + [ 448, 674, 270, 674, 448 ], + [ 529, 10, 30, 463, 273 ], + [ 647, 535, 60, 786, 827 ], + [ 973, 162, -30, 922, 626 ], + [ 663, 110, 60, 426, 629 ], + [ 259, 5, 60, 133, 226 ], + [ 829, 157, -240, 550, 794 ], + [ 432, 235, -240, 419, 490 ], + [ 641, 473, -210, 791, 729 ], + [ 857, 223, 60, 621, 853 ], + [ 537, 559, -30, 744, 752 ], + [ 396, 187, 0, 396, 187 ], + [ 947, 672, -240, 1055, 1155 ], + [ 512, 633, -90, 633, 512 ], + [ 963, 362, 30, 1014, 795 ], + [ 527, 556, 30, 734, 745 ], + [ 95, 410, 180, 95, 410 ], + [ 274, 733, -330, 603, 771 ], + [ 476, 446, 120, 623, 634 ], + [ 681, 321, 330, 749, 618 ], + [ 271, 723, 150, 595, 760 ], + [ 759, 252, 330, 782, 597 ], + [ 236, 194, 240, 285, 300 ], + [ 426, 578, 90, 578, 426 ], + [ 831, 532, -300, 876, 985 ], + [ 358, 159, 240, 316, 389 ], + [ 915, 229, -120, 654, 906 ], + [ 657, 435, -150, 785, 704 ], + [ 711, 23, 300, 374, 626 ], + [ 371, 534, -150, 587, 646 ], + [ 327, 736, -180, 327, 736 ], + [ 4, 537, 60, 466, 271 ], + [ 953, 714, 240, 1093, 1181 ], + [ 749, 265, 60, 603, 780 ], + [ 365, 119, -180, 365, 119 ], + [ 464, 388, -30, 594, 568 ], + [ 280, 306, 210, 394, 404 ], + [ 641, 30, -120, 345, 569 ], + [ 956, 563, -90, 563, 956 ], + [ 840, 72, -330, 763, 481 ], + [ 128, 317, -360, 128, 317 ], + [ 379, 320, 150, 488, 465 ], + [ 966, 266, 120, 712, 968 ], + [ 786, 60, 360, 786, 60 ], + [ 352, 333, -270, 333, 352 ], + [ 626, 681, 120, 902, 881 ], + [ 87, 122, -330, 136, 149 ], + [ 878, 603, -300, 960, 1061 ], + [ 199, 551, 240, 575, 447 ], + [ 575, 413, 300, 644, 703 ], + [ 638, 509, -120, 759, 806 ], + [ 658, 25, 180, 658, 25 ], + [ 369, 749, -300, 833, 693 ], + [ 7, 570, -240, 497, 290 ], + [ 1022, 66, 60, 567, 918 ], + [ 147, 535, 0, 147, 535 ], + [ 812, 600, 120, 925, 1002 ], + [ 861, 490, -60, 853, 990 ], + [ 949, 640, -270, 640, 949 ], + [ 763, 303, 120, 643, 810 ], + [ 673, 13, -120, 346, 588 ], + [ 1006, 506, -90, 506, 1006 ], + [ 740, 14, -120, 381, 646 ], + [ 829, 19, -300, 430, 726 ], + [ 996, 17, -240, 512, 869 ], + [ 583, 127, 270, 127, 583 ], + [ 53, 736, 120, 663, 412 ], + [ 709, 613, -150, 920, 884 ], + [ 121, 556, -270, 556, 121 ], + [ 266, 360, 0, 266, 360 ], + [ 962, 96, 30, 881, 563 ], + [ 287, 82, -240, 214, 288 ], + [ 668, 637, -210, 896, 884 ], + [ 1024, 490, -210, 1131, 934 ], + [ 91, 468, -180, 91, 468 ], + [ 269, 137, -60, 252, 300 ], + [ 137, 177, -90, 177, 137 ], + [ 804, 708, -240, 1014, 1049 ], + [ 76, 30, 300, 62, 80 ], + [ 200, 229, -90, 229, 200 ], + [ 728, 182, 0, 728, 182 ], + [ 248, 397, 60, 467, 412 ], + [ 442, 130, -60, 332, 447 ], + [ 654, 130, -330, 631, 439 ], + [ 892, 197, 120, 616, 869 ], + [ 128, 399, 270, 399, 128 ], + [ 557, 501, -330, 732, 712 ], + [ 942, 335, -360, 942, 335 ], + [ 95, 392, -360, 95, 392 ], + [ 1014, 542, -120, 975, 1148 ], + [ 364, 320, 60, 458, 475 ], + [ 866, 505, -300, 869, 1001 ], + [ 453, 68, -180, 453, 68 ], + [ 888, 558, -60, 925, 1048 ], + [ 952, 459, 150, 1053, 872 ], + [ 616, 554, -210, 810, 786 ], + [ 462, 203, 270, 203, 462 ], + [ 543, 141, -150, 540, 392 ], + [ 544, 212, 240, 455, 576 ], + [ 585, 204, 0, 585, 204 ], + [ 931, 477, 330, 1044, 878 ], + [ 495, 358, 60, 557, 607 ], + [ 588, 697, 120, 897, 856 ], + [ 854, 594, -90, 594, 854 ], + [ 268, 540, 360, 268, 540 ], + [ 968, 234, 150, 955, 685 ], + [ 152, 580, 360, 152, 580 ], + [ 3, 219, -270, 219, 3 ], + [ 301, 627, -60, 692, 573 ], + [ 96, 297, 150, 231, 303 ], + [ 614, 227, -150, 644, 503 ], + [ 533, 598, 210, 759, 783 ], + [ 101, 271, 150, 222, 284 ], + [ 782, 143, -30, 748, 515 ], + [ 160, 344, -240, 377, 309 ], + [ 148, 301, -30, 278, 335 ], + [ 874, 415, -210, 963, 794 ], + [ 847, 417, 0, 847, 417 ], + [ 1007, 642, -120, 1058, 1192 ], + [ 426, 714, 150, 725, 829 ], + [ 117, 406, -360, 117, 406 ], + [ 443, 37, 210, 401, 252 ], + [ 222, 113, -270, 113, 222 ], + [ 928, 699, -120, 1068, 1152 ], + [ 378, 50, 210, 351, 231 ], + [ 548, 161, -270, 161, 548 ], + [ 4, 48, 150, 27, 42 ], + [ 233, 682, -180, 233, 682 ], + [ 754, 670, -330, 987, 956 ], + [ 61, 221, -360, 61, 221 ], + [ 789, 309, 30, 837, 662 ], + [ 795, 641, 60, 952, 1008 ], + [ 573, 39, 180, 573, 39 ], + [ 712, 734, 90, 734, 712 ], + [ 18, 250, 150, 140, 224 ], + [ 805, 660, 300, 973, 1027 ], + [ 968, 663, 360, 968, 663 ], + [ 661, 69, 270, 69, 661 ], + [ 908, 634, 180, 908, 634 ], + [ 986, 680, -360, 986, 680 ], + [ 992, 503, -60, 930, 1110 ], + [ 514, 331, -180, 514, 331 ], + [ 85, 760, -150, 452, 699 ], + [ 830, 526, -180, 830, 526 ], + [ 458, 539, 60, 695, 665 ], + [ 152, 374, -30, 317, 400 ], + [ 59, 332, -150, 216, 316 ], + [ 29, 59, -90, 59, 29 ], + [ 909, 740, -210, 1157, 1094 ], + [ 633, 711, -300, 932, 903 ], + [ 651, 686, 180, 651, 686 ], + [ 56, 303, -240, 289, 198 ], + [ 314, 366, -240, 473, 453 ], + [ 624, 220, 270, 220, 624 ], + [ 888, 563, 60, 931, 1050 ], + [ 459, 676, -330, 735, 814 ], + [ 911, 720, 0, 911, 720 ], + [ 197, 742, 30, 541, 741 ], + [ 599, 655, 30, 845, 866 ], + [ 823, 361, -30, 892, 724 ], + [ 929, 81, -120, 533, 844 ], + [ 681, 122, 180, 681, 122 ], + [ 874, 358, -210, 935, 745 ], + [ 31, 57, 0, 31, 57 ], + [ 914, 722, -210, 1152, 1080 ], + [ 468, 298, -300, 491, 554 ], + [ 520, 213, -330, 556, 443 ], + [ 1014, 360, -360, 1014, 360 ], + [ 563, 42, 270, 42, 563 ], + [ 73, 658, -120, 605, 391 ], + [ 530, 587, -60, 771, 751 ], + [ 877, 329, 60, 723, 923 ], + [ 604, 216, -330, 631, 488 ], + [ 818, 239, -270, 239, 818 ], + [ 302, 156, -330, 339, 285 ], + [ 534, 507, -30, 715, 706 ], + [ 644, 128, 180, 644, 128 ], + [ 690, 121, -210, 657, 448 ], + [ 699, 542, -180, 699, 542 ], + [ 557, 597, -300, 795, 780 ], + [ 896, 226, 270, 226, 896 ], + [ 318, 116, -150, 332, 258 ], + [ 926, 568, 60, 954, 1085 ], + [ 987, 414, 300, 851, 1061 ], + [ 98, 639, -300, 601, 403 ], + [ 173, 272, 180, 173, 272 ], + [ 644, 755, 330, 934, 976 ], + [ 375, 504, -300, 623, 576 ], + [ 585, 259, 210, 635, 515 ], + [ 851, 677, 60, 1011, 1074 ], + [ 970, 580, 270, 580, 970 ], + [ 846, 51, -360, 846, 51 ], + [ 136, 745, 180, 136, 745 ], + [ 737, 716, -150, 995, 987 ], + [ 632, 22, -300, 334, 558 ], + [ 734, 151, -30, 710, 498 ], + [ 488, 633, -30, 738, 792 ], + [ 412, 146, -300, 331, 429 ], + [ 497, 585, -30, 722, 755 ], + [ 280, 455, -120, 533, 469 ], + [ 857, 413, -330, 948, 786 ], + [ 712, 279, -270, 279, 712 ], + [ 719, 381, -300, 689, 812 ], + [ 460, 165, -330, 480, 372 ], + [ 751, 639, -330, 969, 928 ], + [ 870, 421, -180, 870, 421 ], + [ 327, 18, -300, 179, 292 ], + [ 790, 327, -150, 847, 677 ], + [ 900, 699, -330, 1128, 1054 ], + [ 330, 141, 90, 141, 330 ], + [ 663, 16, -240, 345, 581 ], + [ 744, 549, 30, 918, 846 ], + [ 892, 320, -360, 892, 320 ], + [ 1009, 664, -180, 1009, 664 ], + [ 33, 459, -330, 257, 414 ], + [ 108, 344, 90, 344, 108 ], + [ 385, 589, 120, 702, 626 ], + [ 481, 413, -330, 622, 598 ], + [ 771, 320, 360, 771, 320 ], + [ 817, 567, -240, 899, 989 ], + [ 444, 296, -240, 477, 531 ], + [ 622, 86, 300, 383, 581 ], + [ 148, 755, -150, 505, 727 ], + [ 181, 743, -240, 733, 526 ], + [ 434, 767, 210, 758, 880 ], + [ 514, 80, -210, 485, 324 ], + [ 131, 368, 360, 131, 368 ], + [ 796, 728, 30, 1053, 1027 ], + [ 872, 539, 330, 1024, 903 ], + [ 856, 383, 150, 932, 758 ], + [ 3, 716, -330, 360, 621 ], + [ 194, 414, -240, 455, 374 ], + [ 93, 525, -240, 501, 341 ], + [ 417, 660, -270, 660, 417 ], + [ 602, 218, -270, 218, 602 ], + [ 376, 603, -210, 626, 708 ], + [ 607, 701, 150, 875, 909 ], + [ 599, 529, 120, 757, 781 ], + [ 117, 415, -120, 416, 308 ], + [ 61, 289, 270, 289, 61 ], + [ 760, 291, 210, 803, 631 ], + [ 830, 436, -120, 792, 935 ], + [ 1013, 505, -360, 1013, 505 ], + [ 153, 50, 270, 50, 153 ], + [ 283, 501, -270, 501, 283 ], + [ 412, 15, 330, 363, 219 ], + [ 646, 469, 270, 469, 646 ], + [ 347, 150, 30, 375, 303 ], + [ 1019, 237, -330, 1000, 714 ], + [ 360, 283, -30, 452, 425 ], + [ 211, 85, 240, 178, 224 ], + [ 1016, 707, -90, 707, 1016 ], + [ 323, 698, -330, 628, 765 ], + [ 330, 44, -300, 202, 307 ], + [ 192, 256, 210, 293, 317 ], + [ 343, 564, 210, 578, 658 ], + [ 274, 281, -90, 281, 274 ], + [ 115, 541, 60, 526, 369 ], + [ 661, 537, 300, 794, 840 ], + [ 736, 38, -180, 736, 38 ], + [ 171, 196, -90, 196, 171 ], + [ 1018, 190, -150, 975, 673 ], + [ 397, 432, -210, 559, 571 ], + [ 450, 679, -180, 450, 679 ], + [ 568, 54, 60, 330, 518 ], + [ 330, 311, -120, 433, 440 ], + [ 111, 517, 150, 354, 502 ], + [ 517, 602, -90, 602, 517 ], + [ 99, 330, -300, 335, 250 ], + [ 1021, 113, -270, 113, 1021 ], + [ 271, 241, -90, 241, 271 ], + [ 657, 236, 120, 532, 685 ], + [ 961, 534, -210, 1099, 941 ], + [ 147, 103, -300, 162, 178 ], + [ 415, 529, -270, 529, 415 ], + [ 1023, 8, 60, 518, 889 ], + [ 335, 91, -210, 335, 245 ], + [ 20, 479, -360, 20, 479 ], + [ 426, 553, -30, 644, 692 ], + [ 523, 550, 60, 737, 727 ], + [ 671, 752, 300, 985, 957 ], + [ 55, 477, -210, 285, 439 ], + [ 715, 429, 300, 728, 833 ], + [ 927, 539, -330, 1071, 930 ], + [ 1018, 82, 180, 1018, 82 ], + [ 158, 81, 180, 158, 81 ], + [ 736, 332, 270, 332, 736 ], + [ 978, 347, 0, 978, 347 ], + [ 999, 666, -210, 1198, 1075 ], + [ 435, 290, 300, 467, 521 ], + [ 21, 25, 300, 31, 30 ], + [ 951, 66, 0, 951, 66 ], + [ 148, 602, -330, 429, 594 ], + [ 55, 528, 210, 310, 483 ], + [ 99, 524, 300, 502, 347 ], + [ 847, 642, 360, 847, 642 ], + [ 581, 425, -60, 657, 715 ], + [ 436, 390, 270, 390, 436 ], + [ 929, 371, 180, 929, 371 ], + [ 111, 697, -300, 659, 444 ], + [ 24, 712, -270, 712, 24 ], + [ 941, 657, -210, 1142, 1038 ], + [ 444, 768, -360, 444, 768 ], + [ 519, 41, 150, 469, 294 ], + [ 288, 115, -90, 115, 288 ], + [ 166, 749, -210, 517, 730 ], + [ 152, 417, 210, 339, 436 ], + [ 579, 744, -180, 579, 744 ], + [ 444, 673, 180, 444, 673 ], + [ 173, 755, 120, 740, 525 ], + [ 288, 5, 60, 147, 251 ], + [ 436, 710, -30, 731, 833 ], + [ 670, 275, -30, 717, 573 ], + [ 683, 664, 0, 683, 664 ], + [ 375, 111, 120, 283, 378 ], + [ 765, 236, 90, 236, 765 ], + [ 986, 349, -240, 794, 1026 ], + [ 749, 15, -270, 15, 749 ], + [ 8, 348, -360, 8, 348 ], + [ 786, 477, 360, 786, 477 ], + [ 443, 693, 360, 443, 693 ], + [ 26, 251, 330, 147, 230 ], + [ 483, 753, -180, 483, 753 ], + [ 940, 497, -270, 497, 940 ], + [ 426, 10, -150, 372, 221 ], + [ 64, 583, 60, 536, 346 ], + [ 701, 555, -330, 884, 831 ], + [ 870, 348, -330, 927, 735 ], + [ 1005, 354, 0, 1005, 354 ], + [ 1013, 175, -270, 175, 1013 ], + [ 1004, 507, -330, 1122, 940 ], + [ 1001, 526, -90, 526, 1001 ], + [ 921, 120, -120, 563, 856 ], + [ 183, 56, 360, 183, 56 ], + [ 329, 376, -360, 329, 376 ], + [ 623, 423, 210, 750, 676 ], + [ 207, 181, 0, 207, 181 ], + [ 256, 65, 330, 253, 184 ], + [ 324, 50, -60, 203, 305 ], + [ 855, 42, 90, 42, 855 ], + [ 998, 27, -150, 877, 521 ], + [ 16, 9, 360, 16, 9 ], + [ 392, 700, -270, 700, 392 ], + [ 749, 69, -240, 434, 681 ], + [ 743, 316, 150, 801, 644 ], + [ 749, 15, -210, 655, 386 ], + [ 509, 170, -30, 524, 401 ], + [ 1007, 362, 30, 1053, 817 ], + [ 945, 604, 90, 604, 945 ], + [ 331, 477, 120, 578, 523 ], + [ 973, 458, 150, 1071, 882 ], + [ 243, 469, 180, 243, 469 ], + [ 214, 763, 90, 763, 214 ], + [ 344, 557, 180, 344, 557 ], + [ 549, 345, -270, 345, 549 ], + [ 229, 139, -270, 139, 229 ], + [ 474, 520, -90, 520, 474 ], + [ 959, 506, 270, 506, 959 ], + [ 506, 447, 90, 447, 506 ], + [ 119, 694, -180, 119, 694 ], + [ 811, 656, 270, 656, 811 ], + [ 514, 70, -30, 479, 318 ], + [ 229, 230, -30, 312, 313 ], + [ 877, 487, -240, 860, 1001 ], + [ 592, 131, 90, 131, 592 ], + [ 727, 302, 240, 624, 779 ], + [ 882, 657, -30, 1091, 1010 ], + [ 240, 608, -270, 608, 240 ], + [ 78, 219, 150, 176, 227 ], + [ 722, 308, 90, 308, 722 ], + [ 977, 147, -30, 919, 615 ], + [ 818, 533, 60, 870, 974 ], + [ 261, 704, 270, 704, 261 ], + [ 721, 593, 0, 721, 593 ], + [ 898, 268, 120, 680, 910 ], + [ 510, 46, -330, 464, 294 ], + [ 307, 707, 300, 764, 618 ], + [ 796, 118, 150, 748, 498 ], + [ 935, 177, 360, 935, 177 ], + [ 646, 718, -90, 718, 646 ], + [ 258, 682, 60, 719, 564 ], + [ 744, 528, -180, 744, 528 ], + [ 298, 723, -240, 774, 618 ], + [ 158, 495, 360, 158, 495 ], + [ 668, 400, -150, 777, 679 ], + [ 327, 14, -90, 14, 327 ], + [ 389, 244, -150, 457, 404 ], + [ 302, 73, -30, 297, 214 ], + [ 1, 6, 120, 5, 2 ], + [ 1016, 490, 90, 490, 1016 ], + [ 392, 683, 0, 392, 683 ], + [ 989, 472, -240, 903, 1091 ], + [ 246, 695, -270, 695, 246 ], + [ 442, 46, 210, 404, 260 ], + [ 404, 547, -150, 622, 675 ], + [ 723, 24, -150, 637, 381 ], + [ 78, 315, 90, 315, 78 ], + [ 375, 540, 150, 594, 654 ], + [ 829, 541, 180, 829, 541 ], + [ 494, 534, -120, 708, 693 ], + [ 92, 60, -180, 92, 60 ], + [ 598, 33, 270, 33, 598 ], + [ 783, 218, 210, 786, 579 ], + [ 907, 549, 240, 927, 1059 ], + [ 667, 83, 30, 618, 405 ], + [ 960, 625, 60, 1020, 1143 ], + [ 247, 683, 360, 247, 683 ], + [ 883, 196, 120, 611, 861 ], + [ 590, 50, -90, 50, 590 ], + [ 305, 420, -330, 474, 516 ], + [ 672, 489, -270, 489, 672 ], + [ 924, 168, -240, 606, 883 ], + [ 782, 755, -30, 1054, 1045 ], + [ 542, 668, -240, 849, 802 ], + [ 363, 400, -150, 513, 526 ], + [ 912, 351, -300, 759, 964 ], + [ 467, 536, -360, 467, 536 ], + [ 442, 430, -180, 442, 430 ], + [ 112, 104, -120, 145, 147 ], + [ 503, 332, 270, 332, 503 ], + [ 581, 68, 0, 581, 68 ], + [ 739, 761, 180, 739, 761 ], + [ 941, 579, 150, 1103, 970 ], + [ 409, 217, 60, 392, 462 ], + [ 607, 489, -300, 726, 769 ], + [ 916, 404, 30, 995, 807 ], + [ 11, 754, -180, 11, 754 ], + [ 15, 317, 240, 281, 170 ], + [ 280, 401, 330, 442, 487 ], + [ 611, 10, -90, 10, 611 ], + [ 478, 446, -30, 635, 625 ], + [ 995, 232, -30, 976, 698 ], + [ 713, 169, -210, 701, 501 ], + [ 413, 475, 0, 413, 475 ], + [ 1002, 162, -270, 162, 1002 ], + [ 75, 81, 120, 107, 103 ], + [ 640, 89, 90, 89, 640 ], + [ 896, 100, -330, 825, 534 ], + [ 699, 310, 60, 617, 760 ], + [ 641, 768, 60, 985, 939 ], + [ 1, 358, -270, 358, 1 ], + [ 475, 336, -30, 578, 528 ], + [ 938, 103, 120, 557, 862 ], + [ 125, 412, -150, 313, 418 ], + [ 623, 395, 0, 623, 395 ], + [ 749, 450, 90, 450, 749 ], + [ 390, 162, 180, 390, 162 ], + [ 11, 66, 270, 66, 11 ], + [ 55, 590, -180, 55, 590 ], + [ 633, 302, -180, 633, 302 ], + [ 199, 303, 180, 199, 303 ], + [ 359, 659, -240, 750, 638 ], + [ 827, 595, -150, 1013, 927 ], + [ 496, 688, 240, 843, 772 ], + [ 799, 387, -120, 733, 884 ], + [ 293, 180, 330, 342, 302 ], + [ 920, 472, -270, 472, 920 ], + [ 640, 480, -210, 794, 734 ], + [ 493, 521, 360, 493, 521 ], + [ 759, 227, -90, 227, 759 ], + [ 1023, 376, 210, 1072, 836 ], + [ 741, 645, 90, 645, 741 ], + [ 906, 497, -60, 881, 1032 ], + [ 183, 101, -150, 208, 177 ], + [ 9, 700, 240, 609, 356 ], + [ 360, 152, -330, 387, 311 ], + [ 287, 73, 120, 206, 283 ], + [ 429, 553, -150, 647, 692 ], + [ 659, 154, 330, 646, 462 ], + [ 651, 723, -180, 651, 723 ], + [ 21, 722, 180, 21, 722 ], + [ 819, 509, 60, 850, 963 ], + [ 447, 420, -210, 597, 586 ], + [ 867, 557, -90, 557, 867 ], + [ 155, 563, -180, 155, 563 ], + [ 982, 64, -30, 881, 546 ], + [ 1, 279, 60, 242, 139 ], + [ 674, 600, -150, 882, 856 ], + [ 962, 326, 0, 962, 326 ], + [ 920, 46, -60, 498, 819 ], + [ 157, 61, -90, 61, 157 ], + [ 918, 675, -360, 918, 675 ], + [ 492, 242, 60, 455, 547 ], + [ 728, 124, -150, 691, 470 ], + [ 74, 240, -360, 74, 240 ], + [ 353, 208, -300, 356, 409 ], + [ 833, 209, -30, 825, 597 ], + [ 386, 714, -180, 386, 714 ], + [ 676, 667, 120, 915, 917 ], + [ 146, 588, 180, 146, 588 ], + [ 580, 705, 240, 900, 854 ], + [ 950, 626, 150, 1135, 1015 ], + [ 975, 226, -360, 975, 226 ], + [ 534, 4, -240, 269, 463 ], + [ 835, 59, 0, 835, 59 ], + [ 861, 323, 180, 861, 323 ], + [ 972, 179, -360, 972, 179 ], + [ 430, 469, 330, 606, 621 ], + [ 688, 207, 240, 522, 698 ], + [ 356, 316, 60, 451, 466 ], + [ 85, 751, 0, 85, 751 ], + [ 920, 695, -150, 1143, 1061 ], + [ 606, 658, 90, 658, 606 ], + [ 614, 290, 90, 290, 614 ], + [ 786, 148, 150, 754, 519 ], + [ 254, 26, -300, 149, 232 ], + [ 1022, 755, -120, 1164, 1262 ], + [ 20, 309, 330, 171, 278 ], + [ 994, 57, -210, 888, 544 ], + [ 807, 324, 90, 324, 807 ], + [ 381, 387, 90, 387, 381 ], + [ 894, 309, 0, 894, 309 ], + [ 193, 763, -90, 763, 193 ], + [ 817, 455, 180, 817, 455 ], + [ 421, 276, 330, 501, 449 ], + [ 102, 466, 360, 102, 466 ], + [ 213, 464, 330, 415, 508 ], + [ 562, 479, -120, 695, 725 ], + [ 512, 456, -60, 649, 671 ], + [ 737, 294, 270, 294, 737 ], + [ 359, 579, -120, 679, 599 ], + [ 982, 481, 180, 982, 481 ], + [ 483, 626, 180, 483, 626 ], + [ 861, 470, -120, 836, 979 ], + [ 601, 17, 150, 528, 314 ], + [ 538, 93, -150, 511, 349 ], + [ 509, 252, 300, 471, 566 ], + [ 462, 674, 30, 737, 814 ], + [ 827, 289, -270, 289, 827 ], + [ 242, 73, 0, 242, 73 ], + [ 89, 343, -180, 89, 343 ], + [ 451, 311, 300, 493, 545 ], + [ 200, 673, 150, 509, 681 ], + [ 965, 550, -150, 1109, 957 ], + [ 288, 232, -120, 344, 364 ], + [ 1003, 635, -30, 1185, 1051 ], + [ 290, 249, 210, 375, 360 ], + [ 196, 595, 120, 612, 465 ], + [ 740, 447, -360, 740, 447 ], + [ 868, 628, -270, 628, 868 ], + [ 363, 694, 60, 782, 661 ], + [ 629, 264, 360, 629, 264 ], + [ 512, 413, 270, 413, 512 ], + [ 204, 369, 60, 421, 360 ], + [ 522, 584, 270, 584, 522 ], + [ 862, 568, 150, 1030, 921 ], + [ 302, 17, -360, 302, 17 ], + [ 78, 164, 210, 148, 180 ], + [ 751, 718, -90, 718, 751 ], + [ 785, 601, -240, 912, 978 ], + [ 916, 105, -300, 548, 845 ], + [ 480, 577, -60, 738, 703 ], + [ 457, 192, 360, 457, 192 ], + [ 307, 345, -30, 437, 452 ], + [ 888, 736, -210, 1137, 1079 ], + [ 755, 615, 330, 960, 910 ], + [ 443, 73, 360, 443, 73 ], + [ 462, 131, -240, 343, 464 ], + [ 250, 694, -270, 694, 250 ], + [ 632, 514, 300, 759, 804 ], + [ 827, 433, -360, 827, 433 ], + [ 922, 25, 210, 810, 482 ], + [ 345, 367, -360, 345, 367 ], + [ 317, 597, 0, 317, 597 ], + [ 780, 495, -30, 922, 819 ], + [ 998, 293, -180, 998, 293 ], + [ 948, 625, -120, 1014, 1132 ], + [ 921, 203, 0, 921, 203 ], + [ 87, 390, -60, 380, 270 ], + [ 211, 96, -300, 188, 230 ], + [ 111, 716, 120, 675, 453 ], + [ 152, 639, 120, 628, 449 ], + [ 961, 130, -270, 130, 961 ], + [ 1001, 367, 330, 1049, 818 ], + [ 477, 183, 60, 396, 504 ], + [ 933, 163, 330, 889, 607 ], + [ 160, 106, 210, 190, 171 ], + [ 508, 28, -360, 508, 28 ], + [ 6, 93, 30, 51, 83 ], + [ 428, 251, 120, 430, 494 ], + [ 511, 334, 60, 544, 609 ], + [ 147, 448, -60, 460, 351 ], + [ 840, 400, 60, 765, 927 ], + [ 997, 383, -330, 1054, 830 ], + [ 926, 740, -150, 1170, 1103 ], + [ 490, 671, 30, 759, 825 ], + [ 442, 23, 120, 240, 392 ], + [ 203, 403, 150, 376, 449 ], + [ 234, 407, 240, 468, 405 ], + [ 785, 728, -270, 728, 785 ], + [ 439, 343, 60, 516, 551 ], + [ 23, 453, -270, 453, 23 ], + [ 459, 314, 120, 501, 553 ], + [ 42, 294, -240, 275, 182 ], + [ 115, 220, -270, 220, 115 ], + [ 431, 587, -360, 431, 587 ], + [ 965, 150, -330, 910, 612 ], + [ 615, 303, 60, 569, 683 ], + [ 303, 478, -300, 565, 501 ], + [ 114, 298, -330, 247, 314 ], + [ 260, 627, 0, 260, 627 ], + [ 851, 644, -330, 1058, 983 ], + [ 1014, 219, 0, 1014, 219 ], + [ 686, 251, 270, 251, 686 ], + [ 868, 336, -270, 336, 868 ], + [ 1011, 659, 300, 1075, 1204 ], + [ 1024, 615, -300, 1044, 1193 ], + [ 43, 307, 0, 43, 307 ], + [ 623, 534, -270, 534, 623 ], + [ 722, 619, 0, 722, 619 ], + [ 767, 46, -120, 422, 686 ], + [ 243, 684, -240, 713, 551 ], + [ 323, 676, -30, 616, 746 ], + [ 821, 423, 210, 922, 775 ], + [ 687, 305, -210, 746, 606 ], + [ 845, 295, -330, 878, 677 ], + [ 762, 295, 270, 295, 762 ], + [ 878, 327, -180, 878, 327 ], + [ 373, 26, 360, 373, 26 ], + [ 505, 567, 210, 720, 742 ], + [ 10, 374, 240, 328, 194 ], + [ 350, 556, 180, 350, 556 ], + [ 524, 30, 90, 30, 524 ], + [ 989, 645, -270, 645, 989 ], + [ 765, 392, 30, 858, 721 ], + [ 988, 257, 330, 983, 717 ], + [ 41, 60, -150, 64, 71 ], + [ 938, 718, 180, 938, 718 ], + [ 196, 230, 210, 283, 296 ], + [ 165, 608, 0, 165, 608 ], + [ 959, 615, 360, 959, 615 ], + [ 790, 109, 180, 790, 109 ], + [ 508, 502, 210, 689, 688 ], + [ 104, 475, 60, 462, 327 ], + [ 262, 281, -300, 373, 366 ], + [ 827, 253, -30, 842, 632 ], + [ 760, 284, 30, 800, 625 ], + [ 62, 219, -30, 162, 221 ], + [ 822, 365, 180, 822, 365 ], + [ 593, 488, 30, 757, 719 ], + [ 95, 439, -120, 426, 301 ], + [ 95, 263, 0, 95, 263 ], + [ 835, 644, -270, 644, 835 ], + [ 590, 722, 180, 590, 722 ], + [ 180, 150, -270, 150, 180 ], + [ 292, 2, -30, 252, 148 ], + [ 746, 571, 240, 867, 931 ], + [ 256, 617, -300, 661, 529 ], + [ 723, 466, 60, 765, 859 ], + [ 472, 142, -210, 479, 357 ], + [ 7, 213, 210, 112, 186 ], + [ 362, 284, -270, 284, 362 ], + [ 862, 141, 360, 862, 141 ], + [ 1006, 583, 300, 1006, 1162 ], + [ 760, 718, -300, 1001, 1017 ], + [ 887, 169, -300, 589, 852 ], + [ 326, 728, 270, 728, 326 ], + [ 144, 152, 120, 203, 199 ], + [ 252, 694, -180, 252, 694 ], + [ 846, 279, -60, 663, 871 ], + [ 43, 284, 150, 179, 266 ], + [ 848, 555, -300, 904, 1011 ], + [ 980, 433, -180, 980, 433 ], + [ 946, 419, -360, 946, 419 ], + [ 849, 221, 330, 845, 615 ], + [ 952, 117, -240, 576, 881 ], + [ 32, 361, -270, 361, 32 ], + [ 897, 469, -120, 853, 1010 ], + [ 536, 657, -210, 792, 835 ], + [ 795, 524, 60, 851, 950 ], + [ 219, 556, 330, 466, 591 ], + [ 950, 424, 120, 841, 1033 ], + [ 11, 390, -180, 11, 390 ], + [ 283, 331, 210, 410, 427 ], + [ 308, 199, -330, 365, 325 ], + [ 267, 145, -210, 303, 258 ], + [ 450, 168, 150, 473, 368 ], + [ 578, 73, -150, 536, 351 ], + [ 909, 474, -240, 864, 1023 ], + [ 115, 303, 270, 303, 115 ], + [ 717, 467, 60, 762, 853 ], + [ 673, 411, -300, 692, 787 ], + [ 344, 419, 90, 419, 344 ], + [ 592, 630, -330, 827, 841 ], + [ 374, 93, -150, 369, 267 ], + [ 450, 293, 0, 450, 293 ], + [ 681, 630, 150, 904, 885 ], + [ 369, 295, 240, 438, 466 ], + [ 681, 208, -60, 519, 693 ], + [ 846, 295, 240, 677, 879 ], + [ 684, 64, -60, 395, 624 ], + [ 238, 569, 360, 238, 569 ], + [ 357, 58, 0, 357, 58 ], + [ 994, 502, -120, 931, 1110 ], + [ 25, 14, -30, 27, 24 ], + [ 345, 351, 240, 475, 473 ], + [ 857, 93, 120, 509, 787 ], + [ 569, 370, -360, 569, 370 ], + [ 378, 112, -150, 382, 285 ], + [ 818, 747, 150, 1081, 1054 ], + [ 637, 491, 210, 796, 742 ], + [ 54, 669, 0, 54, 669 ], + [ 17, 169, 60, 154, 98 ], + [ 539, 146, -210, 539, 394 ], + [ 876, 164, -180, 876, 164 ], + [ 312, 422, 150, 481, 519 ], + [ 139, 297, 240, 325, 268 ], + [ 805, 723, -150, 1058, 1027 ], + [ 839, 238, -30, 844, 625 ], + [ 127, 83, -60, 134, 150 ], + [ 832, 561, -330, 1000, 901 ], + [ 597, 601, 330, 817, 818 ], + [ 96, 613, -240, 578, 388 ], + [ 696, 249, -120, 563, 726 ], + [ 941, 137, 60, 589, 882 ], + [ 180, 371, -300, 410, 340 ], + [ 911, 475, 0, 911, 475 ], + [ 743, 310, -30, 797, 639 ], + [ 42, 171, 180, 42, 171 ], + [ 515, 266, 270, 266, 515 ], + [ 959, 122, 60, 585, 891 ], + [ 9, 569, -90, 569, 9 ], + [ 975, 640, -240, 1041, 1163 ], + [ 117, 394, 0, 117, 394 ], + [ 477, 331, 120, 525, 577 ], + [ 31, 465, -270, 465, 31 ], + [ 148, 381, 210, 318, 403 ], + [ 660, 169, -240, 475, 654 ], + [ 87, 201, -90, 201, 87 ], + [ 855, 587, 180, 855, 587 ], + [ 710, 538, 300, 819, 883 ], + [ 288, 544, 90, 544, 288 ], + [ 665, 507, -30, 828, 771 ], + [ 836, 594, 360, 836, 594 ], + [ 314, 183, -60, 313, 362 ], + [ 997, 206, -360, 997, 206 ], + [ 126, 316, 0, 126, 316 ], + [ 912, 43, 180, 912, 43 ], + [ 62, 108, 360, 62, 108 ], + [ 464, 749, 180, 464, 749 ], + [ 197, 513, -30, 426, 542 ], + [ 91, 728, -240, 675, 441 ], + [ 743, 459, 240, 768, 872 ], + [ 201, 318, 60, 375, 333 ], + [ 183, 553, 240, 569, 434 ], + [ 605, 532, -300, 763, 789 ], + [ 623, 627, 0, 623, 627 ], + [ 123, 542, 60, 530, 377 ], + [ 873, 589, 150, 1050, 945 ], + [ 866, 169, 150, 833, 577 ], + [ 616, 316, -120, 581, 690 ], + [ 59, 384, -150, 242, 361 ], + [ 227, 173, 300, 262, 282 ], + [ 45, 323, -150, 199, 301 ], + [ 655, 460, -360, 655, 460 ], + [ 484, 145, 150, 491, 366 ], + [ 587, 611, 30, 813, 822 ], + [ 87, 704, -210, 427, 652 ], + [ 668, 590, -360, 668, 590 ], + [ 368, 471, -210, 553, 590 ], + [ 41, 165, 90, 165, 41 ], + [ 398, 209, -270, 209, 398 ], + [ 702, 379, -120, 678, 796 ], + [ 593, 412, 210, 718, 652 ], + [ 907, 135, -90, 135, 907 ], + [ 934, 498, 60, 897, 1057 ], + [ 646, 170, -120, 469, 643 ], + [ 168, 235, -180, 168, 235 ], + [ 413, 736, -330, 725, 843 ], + [ 419, 243, 120, 419, 482 ], + [ 648, 274, 270, 274, 648 ], + [ 274, 572, -270, 572, 274 ], + [ 411, 330, 90, 330, 411 ], + [ 778, 6, -240, 393, 675 ], + [ 551, 686, -120, 868, 819 ], + [ 936, 618, 360, 936, 618 ], + [ 490, 334, -210, 591, 532 ], + [ 858, 460, 30, 973, 826 ], + [ 115, 1, -30, 99, 58 ], + [ 159, 315, 210, 294, 351 ], + [ 830, 33, -270, 33, 830 ], + [ 696, 238, 300, 552, 721 ], + [ 841, 546, -120, 892, 1000 ], + [ 466, 362, -120, 546, 583 ], + [ 641, 7, -210, 558, 325 ], + [ 995, 708, -360, 995, 708 ], + [ 989, 307, -30, 1009, 760 ], + [ 185, 182, -330, 251, 250 ], + [ 717, 268, -330, 754, 590 ], + [ 183, 387, -30, 351, 426 ], + [ 995, 241, 0, 995, 241 ], + [ 124, 763, 240, 722, 488 ], + [ 15, 625, 30, 324, 548 ], + [ 345, 207, -360, 345, 207 ], + [ 683, 687, -360, 683, 687 ], + [ 910, 665, 330, 1120, 1031 ], + [ 938, 638, -120, 1021, 1130 ], + [ 385, 8, 60, 199, 337 ], + [ 445, 546, 300, 694, 658 ], + [ 506, 682, -60, 842, 779 ], + [ 986, 660, 180, 986, 660 ], + [ 485, 753, 180, 485, 753 ], + [ 302, 764, 30, 643, 812 ], + [ 846, 254, 240, 642, 858 ], + [ 847, 765, 150, 1115, 1085 ], + [ 857, 680, 60, 1017, 1082 ], + [ 786, 615, -90, 615, 786 ], + [ 100, 135, -60, 165, 153 ], + [ 681, 469, -300, 746, 823 ], + [ 595, 80, -360, 595, 80 ], + [ 1023, 51, 270, 51, 1023 ], + [ 749, 415, 270, 415, 749 ], + [ 467, 641, 240, 787, 724 ], + [ 1017, 507, -240, 947, 1132 ], + [ 819, 374, -240, 733, 895 ], + [ 511, 248, -330, 566, 470 ], + [ 66, 69, 270, 69, 66 ], + [ 469, 144, -360, 469, 144 ], + [ 883, 655, 90, 655, 883 ], + [ 1000, 332, 180, 1000, 332 ], + [ 1003, 331, 210, 1033, 787 ], + [ 861, 125, -120, 537, 807 ], + [ 697, 474, -240, 758, 839 ], + [ 528, 469, 240, 669, 691 ], + [ 696, 315, -150, 759, 620 ], + [ 860, 698, 120, 1033, 1092 ], + [ 906, 747, 180, 906, 747 ], + [ 747, 330, 300, 658, 811 ], + [ 768, 224, 210, 776, 577 ], + [ 389, 206, -210, 439, 371 ], + [ 116, 190, 90, 190, 116 ], + [ 935, 67, 210, 842, 524 ], + [ 259, 590, -60, 639, 519 ], + [ 437, 217, -360, 437, 217 ], + [ 265, 739, -60, 771, 598 ], + [ 592, 616, -120, 828, 819 ], + [ 225, 527, -150, 457, 567 ], + [ 974, 319, 180, 974, 319 ], + [ 891, 126, -360, 891, 126 ], + [ 656, 417, -150, 776, 688 ], + [ 864, 504, 30, 1000, 867 ], + [ 489, 437, 90, 437, 489 ], + [ 279, 630, -90, 630, 279 ], + [ 701, 190, 150, 702, 514 ], + [ 723, 388, 120, 697, 819 ], + [ 140, 64, -30, 152, 125 ], + [ 486, 233, -270, 233, 486 ], + [ 803, 195, 30, 792, 570 ], + [ 536, 95, 150, 511, 348 ], + [ 567, 587, -180, 567, 587 ], + [ 873, 466, 300, 839, 989 ], + [ 408, 65, -30, 385, 260 ], + [ 31, 274, -150, 162, 251 ], + [ 440, 31, 30, 396, 246 ], + [ 114, 573, -330, 384, 552 ], + [ 772, 678, -270, 678, 772 ], + [ 203, 274, -60, 337, 312 ], + [ 336, 108, 150, 344, 260 ], + [ 894, 510, 240, 888, 1028 ], + [ 594, 167, 60, 441, 597 ], + [ 850, 54, -240, 471, 762 ], + [ 753, 359, -180, 753, 359 ], + [ 214, 382, 90, 382, 214 ], + [ 514, 712, 120, 873, 800 ], + [ 43, 29, -60, 45, 51 ], + [ 89, 608, -150, 380, 570 ], + [ 219, 760, 90, 760, 219 ], + [ 659, 244, 210, 691, 539 ], + [ 334, 146, -30, 361, 293 ], + [ 134, 591, 120, 578, 410 ], + [ 717, 460, 180, 717, 460 ], + [ 896, 257, -240, 670, 902 ], + [ 137, 417, 150, 326, 428 ], + [ 767, 34, 90, 34, 767 ], + [ 414, 66, 120, 263, 390 ], + [ 825, 132, -30, 779, 526 ], + [ 91, 296, -60, 300, 226 ], + [ 745, 22, 180, 745, 22 ], + [ 520, 272, 330, 585, 496 ], + [ 974, 372, 150, 1029, 807 ], + [ 626, 141, -150, 612, 434 ], + [ 350, 45, 120, 213, 324 ], + [ 552, 147, -240, 402, 550 ], + [ 239, 722, -210, 567, 743 ], + [ 873, 265, -150, 888, 664 ], + [ 521, 115, 0, 521, 115 ], + [ 979, 183, 270, 183, 979 ], + [ 401, 741, 300, 841, 717 ], + [ 147, 363, -180, 147, 363 ], + [ 86, 325, 180, 86, 325 ], + [ 813, 26, 300, 428, 717 ], + [ 105, 288, 360, 105, 288 ], + [ 763, 702, -240, 989, 1010 ], + [ 952, 112, -300, 572, 880 ], + [ 163, 767, -30, 524, 745 ], + [ 485, 389, 90, 389, 485 ], + [ 983, 355, 240, 797, 1028 ], + [ 852, 655, 240, 992, 1064 ], + [ 749, 765, -180, 749, 765 ], + [ 1007, 61, 150, 902, 555 ], + [ 737, 671, 180, 737, 671 ], + [ 654, 749, -300, 975, 940 ], + [ 631, 553, -360, 631, 553 ], + [ 803, 498, -240, 832, 943 ], + [ 914, 620, -240, 993, 1100 ], + [ 353, 215, 120, 362, 411 ], + [ 968, 185, -270, 185, 968 ], + [ 803, 55, 360, 803, 55 ], + [ 601, 616, 360, 601, 616 ], + [ 882, 604, -330, 1065, 963 ], + [ 571, 388, 330, 687, 621 ], + [ 295, 111, 300, 242, 310 ], + [ 402, 583, 90, 583, 402 ], + [ 420, 417, -180, 420, 417 ], + [ 588, 334, -330, 676, 582 ], + [ 753, 598, -120, 893, 950 ], + [ 412, 556, 60, 687, 634 ], + [ 545, 390, 120, 610, 665 ], + [ 526, 72, -30, 490, 325 ], + [ 502, 733, -180, 502, 733 ], + [ 560, 393, 210, 680, 619 ], + [ 487, 613, 300, 773, 727 ], + [ 388, 146, 120, 319, 408 ], + [ 1, 461, -210, 230, 398 ], + [ 278, 133, 90, 133, 278 ], + [ 357, 698, -60, 781, 658 ], + [ 723, 238, -270, 238, 723 ], + [ 310, 646, 120, 713, 590 ], + [ 994, 272, 180, 994, 272 ], + [ 451, 649, 150, 714, 786 ], + [ 713, 300, -210, 767, 615 ], + [ 467, 665, -360, 467, 665 ], + [ 369, 187, -240, 346, 411 ], + [ 24, 188, 210, 113, 174 ], + [ 234, 396, 360, 234, 396 ], + [ 889, 663, 270, 663, 889 ], + [ 293, 437, -240, 524, 470 ], + [ 438, 669, -360, 438, 669 ], + [ 78, 646, -90, 646, 78 ], + [ 836, 216, -210, 831, 603 ], + [ 524, 750, 90, 750, 524 ], + [ 733, 332, -30, 799, 654 ], + [ 736, 608, 180, 736, 608 ], + [ 933, 626, 360, 933, 626 ], + [ 721, 33, -30, 640, 389 ], + [ 686, 699, -120, 947, 943 ], + [ 556, 151, 300, 407, 556 ], + [ 743, 479, 210, 882, 785 ], + [ 767, 537, 90, 537, 767 ], + [ 130, 396, 300, 406, 310 ], + [ 357, 20, 270, 20, 357 ], + [ 984, 570, -150, 1136, 985 ], + [ 449, 353, -300, 530, 564 ], + [ 164, 285, 300, 327, 284 ], + [ 127, 57, 330, 137, 112 ], + [ 618, 571, 270, 571, 618 ], + [ 968, 220, -330, 948, 674 ], + [ 875, 9, 150, 761, 444 ], + [ 649, 584, -90, 584, 649 ], + [ 258, 681, 300, 717, 563 ], + [ 155, 181, 330, 224, 234 ], + [ 27, 151, 120, 144, 97 ], + [ 340, 487, -270, 487, 340 ], + [ 441, 610, 0, 441, 610 ], + [ 350, 705, 60, 785, 655 ], + [ 303, 400, -150, 461, 496 ], + [ 164, 357, 60, 390, 320 ], + [ 225, 245, 90, 245, 225 ], + [ 924, 731, -90, 731, 924 ], + [ 291, 156, -180, 291, 156 ], + [ 184, 272, 0, 184, 272 ], + [ 103, 292, 150, 235, 303 ], + [ 782, 547, -150, 950, 864 ], + [ 906, 109, -300, 546, 838 ], + [ 387, 371, -360, 387, 371 ], + [ 966, 598, 30, 1135, 1000 ], + [ 259, 721, -360, 259, 721 ], + [ 868, 121, -120, 538, 811 ], + [ 464, 46, -180, 464, 46 ], + [ 838, 264, 0, 838, 264 ], + [ 72, 402, 240, 383, 262 ], + [ 570, 479, -210, 732, 698 ], + [ 244, 297, 330, 359, 379 ], + [ 567, 208, -330, 595, 463 ], + [ 679, 497, 30, 836, 769 ], + [ 706, 454, -120, 745, 837 ], + [ 242, 648, -180, 242, 648 ], + [ 253, 531, -60, 585, 484 ], + [ 589, 111, 0, 589, 111 ], + [ 876, 739, 240, 1077, 1127 ], + [ 346, 25, -120, 194, 311 ], + [ 178, 452, 360, 178, 452 ], + [ 399, 634, -120, 747, 661 ], + [ 324, 292, 90, 292, 324 ], + [ 394, 33, -180, 394, 33 ], + [ 959, 562, 270, 562, 959 ], + [ 272, 743, 150, 606, 777 ], + [ 547, 164, -90, 164, 547 ], + [ 982, 606, 30, 1153, 1015 ], + [ 455, 494, 0, 455, 494 ], + [ 279, 753, 30, 617, 791 ], + [ 638, 118, -270, 118, 638 ], + [ 610, 417, 330, 736, 666 ], + [ 977, 660, -120, 1059, 1175 ], + [ 597, 187, -90, 187, 597 ], + [ 880, 138, -300, 559, 831 ], + [ 743, 342, -330, 814, 667 ], + [ 445, 752, -150, 760, 872 ], + [ 854, 720, -330, 1099, 1050 ], + [ 387, 292, 150, 481, 445 ], + [ 905, 501, 150, 1033, 885 ], + [ 431, 211, 270, 211, 431 ], + [ 558, 668, -90, 668, 558 ], + [ 507, 633, -180, 507, 633 ], + [ 843, 312, 0, 843, 312 ], + [ 199, 203, 120, 275, 272 ], + [ 303, 760, -300, 809, 642 ], + [ 374, 325, -300, 467, 485 ], + [ 662, 198, -360, 662, 198 ], + [ 734, 488, -60, 788, 879 ], + [ 440, 398, -330, 580, 564 ], + [ 126, 721, -120, 686, 469 ], + [ 993, 371, -210, 1044, 816 ], + [ 367, 750, -360, 367, 750 ], + [ 621, 615, -60, 842, 844 ], + [ 100, 764, 150, 468, 710 ], + [ 787, 223, 150, 792, 585 ], + [ 906, 503, -300, 888, 1035 ], + [ 1001, 231, -90, 231, 1001 ], + [ 8, 13, -360, 8, 13 ], + [ 428, 343, 0, 428, 343 ], + [ 491, 436, -30, 642, 623 ], + [ 835, 412, 330, 928, 774 ], + [ 20, 687, 300, 603, 360 ], + [ 49, 385, 150, 234, 356 ], + [ 493, 460, 150, 656, 643 ], + [ 183, 282, 360, 183, 282 ], + [ 886, 193, 120, 609, 862 ], + [ 999, 175, 330, 952, 651 ], + [ 350, 181, 360, 350, 181 ], + [ 352, 502, -60, 609, 555 ], + [ 883, 102, 360, 883, 102 ], + [ 327, 728, 0, 327, 728 ], + [ 264, 743, -60, 773, 599 ], + [ 177, 11, 300, 97, 158 ], + [ 909, 380, 0, 909, 380 ], + [ 610, 517, 270, 517, 610 ], + [ 608, 413, -270, 413, 608 ], + [ 251, 394, -120, 465, 413 ], + [ 202, 656, 60, 668, 502 ], + [ 170, 152, -180, 170, 152 ], + [ 594, 46, -90, 46, 594 ], + [ 575, 290, -120, 537, 641 ], + [ 82, 488, -150, 314, 463 ], + [ 504, 620, -150, 745, 788 ], + [ 415, 533, 210, 625, 668 ], + [ 939, 222, -30, 923, 661 ], + [ 813, 678, -360, 813, 678 ], + [ 944, 98, 0, 944, 98 ], + [ 284, 249, -90, 249, 284 ], + [ 888, 377, 60, 769, 957 ], + [ 113, 54, 90, 54, 113 ], + [ 498, 485, -360, 498, 485 ], + [ 828, 546, 90, 546, 828 ], + [ 99, 156, -30, 162, 184 ], + [ 409, 467, -270, 467, 409 ], + [ 200, 403, -60, 447, 374 ], + [ 679, 244, -150, 709, 549 ], + [ 685, 184, -60, 500, 685 ], + [ 191, 396, 210, 362, 437 ], + [ 572, 294, -180, 572, 294 ], + [ 130, 378, -120, 391, 300 ], + [ 773, 751, 330, 1044, 1036 ], + [ 778, 604, 120, 911, 974 ], + [ 409, 678, 270, 678, 409 ], + [ 867, 216, -30, 857, 620 ], + [ 1010, 366, 360, 1010, 366 ], + [ 425, 107, -150, 421, 304 ], + [ 743, 620, 30, 953, 908 ], + [ 167, 763, -330, 525, 744 ], + [ 946, 424, -30, 1030, 840 ], + [ 179, 521, 330, 415, 540 ], + [ 507, 332, 300, 540, 605 ], + [ 457, 147, 150, 468, 354 ], + [ 110, 453, 60, 446, 321 ], + [ 994, 335, 270, 335, 994 ], + [ 453, 324, -120, 506, 553 ], + [ 448, 642, -30, 707, 780 ], + [ 768, 431, -150, 880, 756 ], + [ 307, 556, -150, 542, 634 ], + [ 374, 497, 240, 616, 571 ], + [ 831, 631, 0, 831, 631 ], + [ 768, 243, 330, 786, 594 ], + [ 683, 585, -270, 585, 683 ], + [ 377, 667, 150, 659, 765 ], + [ 976, 645, -270, 645, 976 ], + [ 838, 216, 60, 605, 833 ], + [ 163, 552, -60, 558, 417 ], + [ 654, 359, 360, 654, 359 ], + [ 947, 589, 150, 1114, 982 ], + [ 210, 101, -120, 191, 231 ], + [ 37, 724, -240, 645, 393 ], + [ 557, 532, -30, 747, 739 ], + [ 495, 276, -180, 495, 276 ], + [ 628, 558, -300, 796, 822 ], + [ 341, 522, 330, 555, 622 ], + [ 500, 382, -210, 624, 579 ], + [ 66, 503, 330, 308, 469 ], + [ 488, 225, -60, 437, 534 ], + [ 460, 168, -240, 374, 481 ], + [ 148, 325, -150, 290, 354 ], + [ 499, 352, -180, 499, 352 ], + [ 667, 2, 330, 577, 335 ], + [ 133, 373, -150, 301, 388 ], + [ 379, 76, -360, 379, 76 ], + [ 490, 332, 150, 590, 531 ], + [ 435, 706, -210, 729, 827 ], + [ 668, 755, 270, 755, 668 ], + [ 612, 353, -270, 353, 612 ], + [ 10, 697, -90, 697, 10 ], + [ 193, 40, 210, 186, 130 ], + [ 509, 414, -180, 509, 414 ], + [ 766, 146, -180, 766, 146 ], + [ 707, 245, 180, 707, 245 ], + [ 1016, 529, 240, 965, 1143 ], + [ 20, 128, -180, 20, 128 ], + [ 743, 454, -240, 764, 869 ], + [ 950, 187, -240, 636, 914 ], + [ 804, 646, 90, 646, 804 ], + [ 957, 653, 30, 1154, 1044 ], + [ 309, 30, 60, 180, 282 ], + [ 94, 411, -270, 411, 94 ], + [ 347, 217, -150, 408, 360 ], + [ 603, 747, 90, 747, 603 ], + [ 331, 741, -150, 656, 806 ], + [ 160, 755, 0, 160, 755 ], + [ 431, 544, -330, 645, 686 ], + [ 618, 488, -150, 778, 731 ], + [ 788, 323, -270, 323, 788 ], + [ 404, 272, -360, 404, 272 ], + [ 948, 504, -330, 1072, 909 ], + [ 522, 574, 60, 757, 739 ], + [ 648, 66, 270, 66, 648 ], + [ 967, 518, 240, 931, 1095 ], + [ 589, 766, 210, 892, 956 ], + [ 915, 118, 210, 850, 558 ], + [ 387, 441, 0, 387, 441 ], + [ 464, 136, -270, 136, 464 ], + [ 790, 727, 30, 1047, 1024 ], + [ 952, 262, 300, 701, 955 ], + [ 960, 204, 30, 933, 656 ], + [ 1008, 596, -150, 1169, 1019 ], + [ 579, 313, -90, 313, 579 ], + [ 438, 270, -330, 514, 452 ], + [ 270, 711, -330, 588, 750 ], + [ 64, 629, -210, 369, 575 ], + [ 865, 151, 210, 824, 562 ], + [ 343, 499, 330, 546, 603 ], + [ 471, 323, 300, 514, 568 ], + [ 5, 268, -180, 5, 268 ], + [ 926, 220, 0, 926, 220 ], + [ 470, 208, -150, 510, 414 ], + [ 778, 641, 150, 993, 942 ], + [ 150, 202, -330, 230, 249 ], + [ 212, 404, 360, 212, 404 ], + [ 270, 451, 210, 458, 525 ], + [ 485, 332, 360, 485, 332 ], + [ 262, 588, 90, 588, 262 ], + [ 220, 173, -330, 276, 259 ], + [ 124, 176, -60, 212, 195 ], + [ 375, 102, 150, 375, 274 ], + [ 898, 454, 330, 1003, 842 ], + [ 166, 269, 210, 277, 315 ], + [ 876, 381, -330, 948, 767 ], + [ 921, 540, 60, 928, 1067 ], + [ 891, 742, -240, 1088, 1141 ], + [ 674, 337, 60, 628, 751 ], + [ 659, 533, -90, 533, 659 ], + [ 284, 698, 60, 745, 594 ], + [ 339, 22, 240, 187, 303 ], + [ 750, 303, 330, 800, 637 ], + [ 431, 208, 30, 477, 395 ], + [ 388, 332, 300, 480, 502 ], + [ 176, 221, -90, 221, 176 ], + [ 231, 144, -300, 240, 272 ], + [ 824, 43, -330, 734, 448 ], + [ 995, 548, 0, 995, 548 ], + [ 582, 274, -240, 527, 640 ], + [ 953, 486, -300, 897, 1068 ], + [ 529, 740, -300, 905, 828 ], + [ 340, 535, 0, 340, 535 ], + [ 285, 90, 210, 290, 219 ], + [ 835, 380, -210, 913, 745 ], + [ 719, 512, 0, 719, 512 ], + [ 86, 685, 150, 416, 634 ], + [ 172, 535, 180, 172, 535 ], + [ 222, 512, -30, 447, 554 ], + [ 721, 180, 240, 515, 713 ], + [ 906, 127, -30, 847, 563 ], + [ 997, 523, -60, 950, 1124 ], + [ 94, 10, -300, 55, 86 ], + [ 617, 223, -210, 645, 500 ], + [ 393, 81, 150, 380, 265 ], + [ 612, 620, -90, 620, 612 ], + [ 100, 684, -180, 100, 684 ], + [ 804, 44, 360, 804, 44 ], + [ 540, 211, 90, 211, 540 ], + [ 1007, 751, 270, 751, 1007 ], + [ 815, 663, -360, 815, 663 ], + [ 247, 642, 150, 534, 678 ], + [ 665, 712, 180, 665, 712 ], + [ 771, 407, -360, 771, 407 ], + [ 995, 701, -300, 1104, 1211 ], + [ 702, 392, 330, 802, 690 ], + [ 67, 467, 240, 436, 291 ], + [ 343, 301, 270, 301, 343 ], + [ 308, 705, -270, 705, 308 ], + [ 955, 692, -270, 692, 955 ], + [ 817, 535, 360, 817, 535 ], + [ 847, 720, 240, 1046, 1092 ], + [ 551, 450, 180, 551, 450 ], + [ 267, 259, -180, 267, 259 ], + [ 279, 237, -240, 344, 358 ], + [ 400, 764, 150, 728, 860 ], + [ 322, 45, -150, 300, 199 ], + [ 131, 302, 150, 264, 326 ], + [ 1023, 533, 90, 533, 1023 ], + [ 147, 480, 0, 147, 480 ], + [ 271, 324, -210, 396, 415 ], + [ 255, 192, -270, 192, 255 ], + [ 71, 604, 180, 71, 604 ], + [ 835, 36, -330, 741, 448 ], + [ 93, 245, -90, 245, 93 ], + [ 286, 545, -90, 545, 286 ], + [ 1000, 17, -60, 513, 874 ], + [ 288, 115, 240, 243, 306 ], + [ 1000, 114, 0, 1000, 114 ], + [ 604, 223, -270, 223, 604 ], + [ 106, 427, 60, 422, 304 ], + [ 322, 618, 240, 695, 586 ], + [ 472, 670, 60, 815, 743 ], + [ 197, 528, 120, 555, 433 ], + [ 267, 597, 360, 267, 597 ], + [ 653, 43, 120, 363, 585 ], + [ 9, 25, 150, 19, 25 ], + [ 448, 240, 270, 240, 448 ], + [ 265, 222, 360, 265, 222 ], + [ 792, 674, -180, 792, 674 ], + [ 934, 753, 240, 1118, 1184 ], + [ 484, 226, 90, 226, 484 ], + [ 289, 580, 0, 289, 580 ], + [ 900, 727, -240, 1079, 1141 ], + [ 533, 159, 360, 533, 159 ], + [ 493, 649, -360, 493, 649 ], + [ 155, 655, -330, 461, 644 ], + [ 863, 223, -90, 223, 863 ], + [ 700, 421, 120, 714, 815 ], + [ 688, 247, 30, 718, 557 ], + [ 973, 179, 0, 973, 179 ], + [ 803, 542, 240, 869, 965 ], + [ 431, 758, 60, 871, 752 ], + [ 952, 665, 0, 952, 665 ], + [ 128, 297, 150, 258, 319 ], + [ 100, 666, 60, 626, 419 ], + [ 129, 14, -60, 75, 118 ], + [ 190, 661, 150, 494, 665 ], + [ 570, 417, -180, 570, 417 ], + [ 221, 165, 60, 253, 273 ], + [ 769, 126, 240, 492, 727 ], + [ 274, 728, 0, 274, 728 ], + [ 80, 282, 0, 80, 282 ], + [ 658, 228, -90, 228, 658 ], + [ 157, 324, 180, 157, 324 ], + [ 892, 399, 90, 399, 892 ], + [ 442, 495, 90, 495, 442 ], + [ 866, 637, 0, 866, 637 ], + [ 561, 296, -360, 561, 296 ], + [ 818, 461, -180, 818, 461 ], + [ 379, 270, -60, 422, 463 ], + [ 178, 475, -120, 499, 391 ], + [ 699, 535, 180, 699, 535 ], + [ 182, 260, -360, 182, 260 ], + [ 551, 378, -30, 665, 602 ], + [ 291, 278, 360, 291, 278 ], + [ 919, 609, 90, 609, 919 ], + [ 541, 490, -60, 693, 713 ], + [ 19, 143, 240, 132, 87 ], + [ 53, 756, -60, 680, 423 ], + [ 305, 272, 210, 399, 387 ], + [ 725, 405, -90, 405, 725 ], + [ 66, 161, -300, 171, 137 ], + [ 807, 297, -60, 659, 846 ], + [ 827, 710, 300, 1027, 1071 ], + [ 255, 160, -180, 255, 160 ], + [ 214, 81, -330, 225, 176 ], + [ 833, 487, 150, 964, 837 ], + [ 253, 501, 300, 559, 469 ], + [ 66, 540, 270, 540, 66 ], + [ 494, 1, -210, 427, 246 ], + [ 257, 544, 180, 257, 544 ], + [ 652, 593, 360, 652, 593 ], + [ 737, 430, -120, 739, 852 ], + [ 143, 282, -180, 143, 282 ], + [ 53, 472, -30, 280, 435 ], + [ 333, 633, 60, 714, 604 ], + [ 401, 489, -210, 591, 622 ], + [ 98, 678, 270, 678, 98 ], + [ 1009, 727, 60, 1134, 1236 ], + [ 879, 329, 60, 724, 925 ], + [ 87, 522, -180, 87, 522 ], + [ 858, 243, -330, 864, 638 ], + [ 819, 27, 90, 27, 819 ], + [ 151, 134, 360, 151, 134 ], + [ 429, 174, 60, 365, 458 ], + [ 921, 423, -60, 825, 1008 ], + [ 496, 724, -330, 791, 874 ], + [ 719, 29, 330, 636, 384 ], + [ 585, 17, 270, 17, 585 ], + [ 147, 676, -150, 464, 657 ], + [ 733, 740, 360, 733, 740 ], + [ 1020, 615, -150, 1190, 1042 ], + [ 48, 461, -120, 422, 271 ], + [ 672, 574, 0, 672, 574 ], + [ 647, 128, 180, 647, 128 ], + [ 227, 50, -150, 220, 155 ], + [ 651, 421, -210, 773, 689 ], + [ 701, 192, -180, 701, 192 ], + [ 631, 631, -150, 861, 860 ], + [ 506, 741, -240, 894, 807 ], + [ 820, 522, -270, 522, 820 ], + [ 805, 519, 330, 956, 851 ], + [ 89, 554, 30, 354, 524 ], + [ 432, 290, -180, 432, 290 ], + [ 953, 7, -60, 481, 828 ], + [ 680, 177, 0, 680, 177 ], + [ 972, 665, -330, 1173, 1061 ], + [ 179, 422, -150, 365, 453 ], + [ 448, 127, -270, 127, 448 ], + [ 738, 507, -300, 807, 892 ], + [ 905, 354, 210, 959, 758 ], + [ 10, 189, -30, 102, 169 ], + [ 959, 255, -240, 700, 956 ], + [ 474, 578, 30, 699, 737 ], + [ 755, 524, 30, 915, 831 ], + [ 181, 266, 210, 288, 319 ], + [ 698, 227, 120, 545, 716 ], + [ 751, 361, -240, 688, 829 ], + [ 37, 696, -120, 620, 379 ], + [ 163, 482, -360, 163, 482 ], + [ 252, 392, -30, 413, 465 ], + [ 13, 399, 150, 210, 351 ], + [ 465, 350, -330, 577, 535 ], + [ 631, 706, 240, 925, 898 ], + [ 159, 503, 0, 159, 503 ], + [ 719, 639, 240, 911, 941 ], + [ 491, 394, -270, 394, 491 ], + [ 123, 189, 60, 225, 200 ], + [ 318, 217, 0, 318, 217 ], + [ 682, 338, -270, 338, 682 ], + [ 697, 527, 90, 527, 697 ], + [ 159, 536, -270, 536, 159 ], + [ 896, 116, 90, 116, 896 ], + [ 949, 589, 30, 1115, 984 ], + [ 748, 708, -240, 986, 1000 ], + [ 222, 479, -360, 222, 479 ], + [ 50, 78, 30, 82, 92 ], + [ 180, 170, 210, 239, 236 ], + [ 790, 409, -330, 888, 748 ], + [ 280, 152, 0, 280, 152 ], + [ 411, 675, -240, 790, 691 ], + [ 41, 26, 270, 26, 41 ], + [ 202, 698, -360, 202, 698 ], + [ 829, 641, 210, 1037, 968 ], + [ 348, 433, 150, 517, 547 ], + [ 538, 599, -120, 787, 764 ], + [ 555, 636, -60, 827, 798 ], + [ 73, 2, 120, 38, 63 ], + [ 867, 594, -240, 947, 1046 ], + [ 925, 36, -90, 36, 925 ], + [ 409, 344, -180, 409, 344 ], + [ 595, 374, -150, 701, 620 ], + [ 470, 525, -210, 669, 688 ], + [ 499, 378, -330, 621, 576 ], + [ 289, 638, 90, 638, 289 ], + [ 1021, 273, -90, 273, 1021 ], + [ 343, 689, -210, 641, 767 ], + [ 793, 743, -210, 1057, 1038 ], + [ 445, 625, 360, 445, 625 ], + [ 641, 550, -360, 641, 550 ], + [ 16, 89, 0, 16, 89 ], + [ 289, 534, 360, 289, 534 ], + [ 596, 119, 120, 400, 574 ], + [ 808, 493, -270, 493, 808 ], + [ 850, 709, 360, 850, 709 ], + [ 445, 707, -120, 833, 738 ], + [ 838, 195, -210, 822, 586 ], + [ 30, 22, -240, 33, 35 ], + [ 249, 355, -360, 249, 355 ], + [ 235, 68, 150, 237, 175 ], + [ 269, 79, 240, 201, 271 ], + [ 761, 295, 30, 806, 635 ], + [ 726, 742, 150, 999, 1004 ], + [ 367, 580, -120, 684, 606 ], + [ 512, 449, -180, 512, 449 ], + [ 483, 15, -240, 254, 424 ], + [ 817, 643, -60, 964, 1028 ], + [ 22, 665, -30, 351, 587 ], + [ 217, 84, 300, 180, 229 ], + [ 221, 260, 360, 221, 260 ], + [ 958, 461, -300, 877, 1059 ], + [ 788, 263, 0, 788, 263 ], + [ 307, 40, -30, 284, 188 ], + [ 1007, 315, -210, 1029, 775 ], + [ 316, 698, 210, 621, 761 ], + [ 569, 292, 240, 536, 637 ], + [ 739, 137, 120, 488, 706 ], + [ 146, 153, 0, 146, 153 ], + [ 605, 315, 90, 315, 605 ], + [ 512, 480, 330, 682, 672 ], + [ 509, 430, 30, 655, 626 ], + [ 610, 253, 300, 522, 654 ], + [ 80, 483, 330, 310, 458 ], + [ 539, 470, -120, 675, 700 ], + [ 747, 707, -180, 747, 707 ], + [ 528, 365, 120, 579, 638 ], + [ 329, 151, 240, 294, 359 ], + [ 987, 260, -360, 987, 260 ], + [ 483, 713, -60, 857, 774 ], + [ 90, 329, -330, 241, 329 ], + [ 43, 710, 90, 710, 43 ], + [ 610, 400, 330, 727, 651 ], + [ 504, 460, 60, 649, 666 ], + [ 450, 96, -240, 307, 436 ], + [ 815, 655, -300, 974, 1032 ], + [ 63, 283, 60, 276, 195 ], + [ 724, 530, 210, 891, 820 ], + [ 542, 501, -300, 704, 719 ], + [ 563, 95, -360, 563, 95 ], + [ 1004, 162, -30, 949, 642 ], + [ 36, 194, -90, 194, 36 ], + [ 665, 652, 300, 896, 901 ], + [ 607, 261, 0, 607, 261 ], + [ 169, 598, 90, 598, 169 ], + [ 357, 441, 0, 357, 441 ], + [ 462, 488, 270, 488, 462 ], + [ 29, 263, 30, 156, 242 ], + [ 820, 669, -240, 988, 1043 ], + [ 934, 323, -150, 969, 746 ], + [ 974, 308, 30, 997, 753 ], + [ 429, 334, 210, 537, 502 ], + [ 830, 65, 90, 65, 830 ], + [ 739, 520, 360, 739, 520 ], + [ 237, 646, 180, 237, 646 ], + [ 885, 145, -120, 567, 838 ], + [ 359, 491, 330, 555, 604 ], + [ 210, 512, -150, 436, 547 ], + [ 767, 359, -240, 694, 842 ], + [ 967, 291, 60, 735, 982 ], + [ 270, 253, 360, 270, 253 ], + [ 813, 574, -60, 902, 991 ], + [ 589, 428, 0, 589, 428 ], + [ 210, 214, -240, 289, 287 ], + [ 179, 391, -360, 179, 391 ], + [ 944, 287, -210, 960, 719 ], + [ 249, 556, 270, 556, 249 ], + [ 204, 713, 0, 204, 713 ], + [ 509, 520, 360, 509, 520 ], + [ 682, 477, -90, 477, 682 ], + [ 262, 679, 150, 565, 717 ], + [ 251, 520, -60, 574, 477 ], + [ 686, 194, 360, 686, 194 ], + [ 182, 351, -180, 182, 351 ], + [ 371, 485, 210, 563, 604 ], + [ 396, 425, -270, 425, 396 ], + [ 614, 611, 270, 611, 614 ], + [ 461, 763, 240, 890, 780 ], + [ 991, 377, 0, 991, 377 ], + [ 974, 120, -300, 590, 903 ], + [ 320, 316, 360, 320, 316 ], + [ 3, 504, 120, 437, 253 ], + [ 451, 251, 300, 441, 515 ], + [ 444, 387, -90, 387, 444 ], + [ 723, 664, -360, 723, 664 ], + [ 498, 193, 30, 527, 415 ], + [ 656, 653, -120, 893, 894 ], + [ 529, 230, -120, 462, 572 ], + [ 329, 205, 240, 341, 386 ], + [ 821, 168, 360, 821, 168 ], + [ 925, 407, -90, 407, 925 ], + [ 909, 409, -330, 991, 808 ], + [ 562, 747, -90, 747, 562 ], + [ 473, 312, 300, 505, 565 ], + [ 852, 85, 180, 852, 85 ], + [ 852, 459, 360, 852, 459 ], + [ 394, 182, 270, 182, 394 ], + [ 750, 579, -270, 579, 750 ], + [ 74, 57, -60, 84, 92 ], + [ 924, 672, 90, 672, 924 ], + [ 910, 597, -270, 597, 910 ], + [ 292, 511, 150, 507, 587 ], + [ 346, 164, 120, 314, 380 ], + [ 734, 518, -330, 894, 815 ], + [ 613, 389, -240, 643, 723 ], + [ 353, 260, 210, 434, 400 ], + [ 349, 555, -360, 349, 555 ], + [ 191, 349, 210, 339, 396 ], + [ 340, 404, 240, 519, 495 ], + [ 762, 329, 150, 823, 664 ], + [ 383, 243, 0, 383, 243 ], + [ 546, 462, -270, 462, 546 ], + [ 229, 721, -90, 721, 229 ], + [ 908, 503, -330, 1037, 889 ], + [ 977, 194, 30, 943, 656 ], + [ 136, 458, 300, 463, 346 ], + [ 930, 720, 90, 720, 930 ], + [ 943, 95, -330, 863, 553 ], + [ 702, 349, 60, 652, 781 ], + [ 536, 152, -360, 536, 152 ], + [ 243, 439, -120, 500, 429 ], + [ 745, 80, 330, 684, 441 ], + [ 903, 251, 270, 251, 903 ], + [ 256, 164, 30, 303, 269 ], + [ 299, 129, 210, 322, 260 ], + [ 859, 230, -90, 230, 859 ], + [ 742, 160, -150, 721, 509 ], + [ 341, 98, -60, 254, 344 ], + [ 404, 624, 300, 740, 661 ], + [ 1006, 258, -300, 725, 1000 ], + [ 1021, 440, 120, 891, 1103 ], + [ 336, 230, 180, 336, 230 ], + [ 266, 139, -300, 252, 299 ], + [ 132, 331, -150, 279, 352 ], + [ 669, 554, -30, 855, 814 ], + [ 439, 430, 210, 594, 590 ], + [ 820, 219, 360, 820, 219 ], + [ 112, 474, -300, 465, 333 ], + [ 553, 8, 300, 282, 482 ], + ]; + } + +} diff --git a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php index f211a00..0e43609 100644 --- a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php @@ -138,7 +138,7 @@ public function testFormat($string, array $args, $expected, $message, $expected_ $result = SafeMarkup::format($string, $args); $this->assertEquals($expected, $result, $message); - $this->assertEquals($expected_is_safe, SafeMarkup::isSafe($result), 'SafeMarkup::format correctly sets the result as safe or not safe.'); + $this->assertEquals($expected_is_safe, $result instanceof MarkupInterface, 'SafeMarkup::format correctly sets the result as safe or not safe.'); foreach ($args as $arg) { $this->assertSame($arg instanceof SafeMarkupTestMarkup, SafeMarkup::isSafe($arg)); diff --git a/core/tests/Drupal/Tests/Component/Utility/TimerTest.php b/core/tests/Drupal/Tests/Component/Utility/TimerTest.php index cfb05dd..79e863f 100644 --- a/core/tests/Drupal/Tests/Component/Utility/TimerTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/TimerTest.php @@ -39,7 +39,7 @@ public function testTimer() { // Although we sleep for 5 milliseconds, we should test that at least 4 ms // have past because usleep() is not reliable on Windows. See - // http://php.net/manual/en/function.usleep.php for more information. The + // http://php.net/manual/function.usleep.php for more information. The // purpose of the test to validate that the Timer class can measure elapsed // time not the granularity of usleep() on a particular OS. $this->assertGreaterThanOrEqual(4, $value, 'Timer failed to measure at least 4 milliseconds of sleeping while running.'); diff --git a/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php b/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php index e2c8505..59135a9 100644 --- a/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php +++ b/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php @@ -246,7 +246,7 @@ public static function providerTestFilterQueryParameters() { */ public function testParse($url, $expected) { $parsed = UrlHelper::parse($url); - $this->assertEquals($expected, $parsed, 'The url was not properly parsed.'); + $this->assertEquals($expected, $parsed, 'The URL was not properly parsed.'); } /** diff --git a/core/tests/Drupal/Tests/ComposerIntegrationTest.php b/core/tests/Drupal/Tests/ComposerIntegrationTest.php index 4557e94..976606c 100644 --- a/core/tests/Drupal/Tests/ComposerIntegrationTest.php +++ b/core/tests/Drupal/Tests/ComposerIntegrationTest.php @@ -49,6 +49,7 @@ protected function getPaths() { return [ $this->root, $this->root . '/core', + $this->root . '/core/lib/Drupal/Component/Bridge', $this->root . '/core/lib/Drupal/Component/Gettext', $this->root . '/core/lib/Drupal/Component/Plugin', $this->root . '/core/lib/Drupal/Component/ProxyBuilder', diff --git a/core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php b/core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php index 209aa64..0a35eec 100644 --- a/core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/CssCollectionGrouperUnitTest.php @@ -5,7 +5,6 @@ * Contains \Drupal\Tests\Core\Asset\CssCollectionGrouperUnitTest. */ - namespace Drupal\Tests\Core\Asset; use Drupal\Core\Asset\CssCollectionGrouper; @@ -46,15 +45,15 @@ function testGrouper() { 'browsers' => array('IE' => TRUE, '!IE' => TRUE), 'basename' => 'system.base.css', ), - 'system.theme.css' => array( + 'js.module.css' => array( 'group' => -100, 'type' => 'file', 'weight' => 0.013, 'media' => 'all', 'preprocess' => TRUE, - 'data' => 'core/modules/system/system.theme.css', + 'data' => 'core/modules/system/js.module.css', 'browsers' => array('IE' => TRUE, '!IE' => TRUE), - 'basename' => 'system.theme.css', + 'basename' => 'js.module.css', ), 'jquery.ui.core.css' => array( 'group' => -100, @@ -120,7 +119,7 @@ function testGrouper() { $this->assertSame($group['preprocess'], TRUE); $this->assertSame(count($group['items']), 3); $this->assertContains($css_assets['system.base.css'], $group['items']); - $this->assertContains($css_assets['system.theme.css'], $group['items']); + $this->assertContains($css_assets['js.module.css'], $group['items']); // Check group 2. $group = $groups[1]; diff --git a/core/tests/Drupal/Tests/Core/Asset/CssCollectionRendererUnitTest.php b/core/tests/Drupal/Tests/Core/Asset/CssCollectionRendererUnitTest.php index 48a92ee..29011e5 100644 --- a/core/tests/Drupal/Tests/Core/Asset/CssCollectionRendererUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/CssCollectionRendererUnitTest.php @@ -5,7 +5,6 @@ * Contains \Drupal\Tests\Core\Asset\CssCollectionRendererUnitTest. */ - namespace { /** diff --git a/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php b/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php index 5922bf1..d4cad06 100644 --- a/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/CssOptimizerUnitTest.php @@ -5,7 +5,6 @@ * Contains \Drupal\Tests\Core\Asset\CssOptimizerUnitTest. */ - namespace { /** diff --git a/core/tests/Drupal/Tests/Core/Cache/CacheableMetadataTest.php b/core/tests/Drupal/Tests/Core/Cache/CacheableMetadataTest.php index b73bec2..c6829ff 100644 --- a/core/tests/Drupal/Tests/Core/Cache/CacheableMetadataTest.php +++ b/core/tests/Drupal/Tests/Core/Cache/CacheableMetadataTest.php @@ -11,7 +11,6 @@ use Drupal\Core\Cache\CacheableMetadata; use Drupal\Tests\Core\Render\TestCacheableDependency; use Drupal\Tests\UnitTestCase; -use Drupal\Core\Render\Element; use Symfony\Component\DependencyInjection\ContainerBuilder; /** diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php index 508f39b..cd589f6 100644 --- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php @@ -854,7 +854,8 @@ public function testDeleteRevision() { * @covers ::doDelete */ public function testDelete() { - // Dependencies are tested in \Drupal\config\Tests\ConfigDependencyTest. + // Dependencies are tested in + // \Drupal\Tests\config\Kernel\ConfigDependencyTest. $this->configManager->expects($this->any()) ->method('getConfigEntitiesToChangeOnDependencyRemoval') ->willReturn(['update' => [], 'delete' => [], 'unchanged' => []]); diff --git a/core/tests/Drupal/Tests/Core/Controller/TitleResolverTest.php b/core/tests/Drupal/Tests/Core/Controller/TitleResolverTest.php index 1d8c416..e2fca4b 100644 --- a/core/tests/Drupal/Tests/Core/Controller/TitleResolverTest.php +++ b/core/tests/Drupal/Tests/Core/Controller/TitleResolverTest.php @@ -12,6 +12,7 @@ use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Route; +use Drupal\Core\StringTranslation\TranslatableMarkup; /** * @coversDefaultClass \Drupal\Core\Controller\TitleResolver @@ -55,13 +56,7 @@ protected function setUp() { public function testStaticTitle() { $request = new Request(); $route = new Route('/test-route', array('_title' => 'static title')); - - $this->translationManager->expects($this->once()) - ->method('translate') - ->with('static title', array(), array()) - ->will($this->returnValue('translated title')); - - $this->assertEquals('translated title', $this->titleResolver->getTitle($request, $route)); + $this->assertEquals(new TranslatableMarkup('static title', array(), array(), $this->translationManager), $this->titleResolver->getTitle($request, $route)); } /** @@ -72,13 +67,7 @@ public function testStaticTitle() { public function testStaticTitleWithContext() { $request = new Request(); $route = new Route('/test-route', array('_title' => 'static title', '_title_context' => 'context')); - - $this->translationManager->expects($this->once()) - ->method('translate') - ->with('static title', array(), array('context' => 'context')) - ->will($this->returnValue('translated title with context')); - - $this->assertEquals('translated title with context', $this->titleResolver->getTitle($request, $route)); + $this->assertEquals(new TranslatableMarkup('static title', array(), array('context' => 'context'), $this->translationManager), $this->titleResolver->getTitle($request, $route)); } /** @@ -94,20 +83,14 @@ public function testStaticTitleWithParameter($title, $expected_title) { $request->attributes->set('_raw_variables', $raw_variables); $route = new Route('/test-route', array('_title' => $title)); - - $this->translationManager->expects($this->once()) - ->method('translate') - ->with($title, $this->logicalOr($this->arrayHasKey('@test'), $this->arrayHasKey('%test'), $this->arrayHasKey('!test')), array()) - ->will($this->returnValue('static title value')); - $this->assertEquals($expected_title, $this->titleResolver->getTitle($request, $route)); } public function providerTestStaticTitleWithParameter() { + $translation_manager = $this->getMock('\Drupal\Core\StringTranslation\TranslationInterface'); return array( - array('static title @test', 'static title value'), - array('static title !test', 'static title value'), - array('static title %test', 'static title value'), + array('static title @test', new TranslatableMarkup('static title @test', ['@test' => 'value', '%test' => 'value', '@test2' => 'value2', '%test2' => 'value2'], array(), $translation_manager)), + array('static title %test', new TranslatableMarkup('static title %test', ['@test' => 'value', '%test' => 'value', '@test2' => 'value2', '%test2' => 'value2'], array(), $translation_manager)), ); } diff --git a/core/tests/Drupal/Tests/Core/Datetime/DateTest.php b/core/tests/Drupal/Tests/Core/Datetime/DateTest.php index 10e9c70..4198231 100644 --- a/core/tests/Drupal/Tests/Core/Datetime/DateTest.php +++ b/core/tests/Drupal/Tests/Core/Datetime/DateTest.php @@ -8,9 +8,11 @@ namespace Drupal\Tests\Core\Datetime; use Drupal\Core\Datetime\DateFormatter; +use Drupal\Core\Datetime\FormattedDateDiff; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Tests\UnitTestCase; use Symfony\Component\HttpFoundation\Request; +use Drupal\Core\StringTranslation\TranslatableMarkup; /** * @coversDefaultClass \Drupal\Core\Datetime\DateFormatter @@ -96,11 +98,10 @@ protected function setUp() { public function testFormatInterval($interval, $granularity, $expected, $langcode = NULL) { // Mocks a simple formatPlural implementation. $this->stringTranslation->expects($this->any()) - ->method('formatPlural') - ->with($this->anything(), $this->anything(), $this->anything(), array(), array('langcode' => $langcode)) - ->will($this->returnCallback(function($count, $one, $multiple) { - return $count == 1 ? $one : str_replace('@count', $count, $multiple); - })); + ->method('translateString') + ->willReturnCallback(function (TranslatableMarkup $arg) { + return $arg->getUntranslatedString(); + }); // Check if the granularity is specified. if ($granularity) { @@ -110,7 +111,7 @@ public function testFormatInterval($interval, $granularity, $expected, $langcode $result = $this->dateFormatter->formatInterval($interval); } - $this->assertEquals($expected, $result); + $this->assertEquals(new TranslatableMarkup($expected, [], ['langcode' => $langcode], $this->stringTranslation), $result); } /** @@ -152,14 +153,8 @@ public function providerTestFormatInterval() { * Tests the formatInterval method for 0 second. */ public function testFormatIntervalZeroSecond() { - $this->stringTranslation->expects($this->once()) - ->method('translate') - ->with('0 sec', array(), array('langcode' => 'xxx-lolspeak')) - ->will($this->returnValue('0 sec')); - $result = $this->dateFormatter->formatInterval(0, 1, 'xxx-lolspeak'); - - $this->assertEquals('0 sec', $result); + $this->assertEquals(new TranslatableMarkup('0 sec', array(), array('langcode' => 'xxx-lolspeak'), $this->stringTranslation), $result); } /** @@ -194,11 +189,17 @@ public function testFormatTimeDiffUntil() { // Mocks the formatDiff function of the dateformatter object. $this->dateFormatterStub - ->expects($this->any()) + ->expects($this->at(0)) ->method('formatDiff') ->with($timestamp, $request_time, $options) ->will($this->returnValue($expected)); + $this->dateFormatterStub + ->expects($this->at(1)) + ->method('formatDiff') + ->with($timestamp, $request_time, $options + ['return_as_object' => TRUE]) + ->will($this->returnValue(new FormattedDateDiff('1 second', 1))); + $request = Request::createFromGlobals(); $request->server->set('REQUEST_TIME', $request_time); // Mocks a the request stack getting the current request. @@ -207,6 +208,9 @@ public function testFormatTimeDiffUntil() { ->willReturn($request); $this->assertEquals($expected, $this->dateFormatterStub->formatTimeDiffSince($timestamp, $options)); + $options['return_as_object'] = TRUE; + $expected_object = new FormattedDateDiff('1 second', 1); + $this->assertEquals($expected_object, $this->dateFormatterStub->formatTimeDiffSince($timestamp, $options)); } /** @@ -222,11 +226,17 @@ public function testFormatTimeDiffSince() { // Mocks the formatDiff function of the dateformatter object. $this->dateFormatterStub - ->expects($this->any()) + ->expects($this->at(0)) ->method('formatDiff') ->with($request_time, $timestamp, $options) ->will($this->returnValue($expected)); + $this->dateFormatterStub + ->expects($this->at(1)) + ->method('formatDiff') + ->with($request_time, $timestamp, $options + ['return_as_object' => TRUE]) + ->will($this->returnValue(new FormattedDateDiff('1 second', 1))); + $request = Request::createFromGlobals(); $request->server->set('REQUEST_TIME', $request_time); // Mocks a the request stack getting the current request. @@ -235,6 +245,9 @@ public function testFormatTimeDiffSince() { ->willReturn($request); $this->assertEquals($expected, $this->dateFormatterStub->formatTimeDiffUntil($timestamp, $options)); + $options['return_as_object'] = TRUE; + $expected_object = new FormattedDateDiff('1 second', 1); + $this->assertEquals($expected_object, $this->dateFormatterStub->formatTimeDiffUntil($timestamp, $options)); } /** @@ -244,25 +257,25 @@ public function testFormatTimeDiffSince() { * * @covers ::formatDiff */ - public function testformatDiff($expected, $timestamp1, $timestamp2, $options = array()) { - - // Mocks a simple formatPlural implementation. + public function testformatDiff($expected, $max_age, $timestamp1, $timestamp2, $options = array()) { + // Mocks a simple translateString implementation. $this->stringTranslation->expects($this->any()) - ->method('formatPlural') - ->with($this->anything(), $this->anything(), $this->anything(), array(), array('langcode' => isset($options['langcode']) ? $options['langcode'] : NULL)) - ->will($this->returnCallback(function($count, $one, $multiple) { - return $count == 1 ? $one : str_replace('@count', $count, $multiple); - })); + ->method('translateString') + ->willReturnCallback(function (TranslatableMarkup $arg) { + return $arg->getUntranslatedString(); + }); - // Mocks a simple translate implementation. - $this->stringTranslation->expects($this->any()) - ->method('translate') - ->with($this->anything()) - ->will($this->returnCallback(function($string, $args, $options) { - return $string; - })); + if (isset($options['langcode'])) { + $expected_markup = new TranslatableMarkup($expected, [], ['langcode' => $options['langcode']], $this->stringTranslation); + } + else { + $expected_markup = new TranslatableMarkup($expected, [], [], $this->stringTranslation); + } + $this->assertEquals($expected_markup, $this->dateFormatter->formatDiff($timestamp1, $timestamp2, $options)); - $this->assertEquals($expected, $this->dateFormatter->formatDiff($timestamp1, $timestamp2, $options)); + $options['return_as_object'] = TRUE; + $expected_object = new FormattedDateDiff($expected, $max_age); + $this->assertEquals($expected_object, $this->dateFormatter->formatDiff($timestamp1, $timestamp2, $options)); } /** @@ -282,103 +295,103 @@ public function providerTestFormatDiff() { $data = array( // Checks for equal timestamps. - array('0 seconds', $request_time, $request_time), + array('0 seconds', 0, $request_time, $request_time), // Checks for seconds only. - array('1 second', $this->createTimestamp('2013-12-11 10:09:07'), $request_time), - array('1 second', $this->createTimestamp('2013-12-11 10:09:07'), $request_time), - array('1 second', $this->createTimestamp('2013-12-11 10:09:07'), $request_time, $granularity_3 + $langcode_en), - array('1 second', $this->createTimestamp('2013-12-11 10:09:07'), $request_time, $granularity_4 + $langcode_lolspeak), - array('2 seconds', $this->createTimestamp('2013-12-11 10:09:06'), $request_time), - array('59 seconds', $this->createTimestamp('2013-12-11 10:08:09'), $request_time), - array('59 seconds', $this->createTimestamp('2013-12-11 10:08:09'), $request_time), + array('1 second', 1, $this->createTimestamp('2013-12-11 10:09:07'), $request_time), + array('1 second', 1, $this->createTimestamp('2013-12-11 10:09:07'), $request_time), + array('1 second', 1, $this->createTimestamp('2013-12-11 10:09:07'), $request_time, $granularity_3 + $langcode_en), + array('1 second', 1, $this->createTimestamp('2013-12-11 10:09:07'), $request_time, $granularity_4 + $langcode_lolspeak), + array('2 seconds', 1, $this->createTimestamp('2013-12-11 10:09:06'), $request_time), + array('59 seconds', 1, $this->createTimestamp('2013-12-11 10:08:09'), $request_time), + array('59 seconds', 1, $this->createTimestamp('2013-12-11 10:08:09'), $request_time), // Checks for minutes and possibly seconds. - array('1 minute', $this->createTimestamp('2013-12-11 10:08:08'), $request_time), - array('1 minute', $this->createTimestamp('2013-12-11 10:08:08'), $request_time), - array('1 minute 1 second', $this->createTimestamp('2013-12-11 10:08:07'), $request_time), - array('1 minute 59 seconds', $this->createTimestamp('2013-12-11 10:07:09'), $request_time), - array('2 minutes', $this->createTimestamp('2013-12-11 10:07:08'), $request_time), - array('2 minutes 1 second', $this->createTimestamp('2013-12-11 10:07:07'), $request_time), - array('2 minutes 2 seconds', $this->createTimestamp('2013-12-11 10:07:06'), $request_time), - array('2 minutes 2 seconds', $this->createTimestamp('2013-12-11 10:07:06'), $request_time, $granularity_3), - array('2 minutes 2 seconds', $this->createTimestamp('2013-12-11 10:07:06'), $request_time, $granularity_4), - array('30 minutes', $this->createTimestamp('2013-12-11 09:39:08'), $request_time), - array('59 minutes 59 seconds', $this->createTimestamp('2013-12-11 09:09:09'), $request_time), - array('59 minutes 59 seconds', $this->createTimestamp('2013-12-11 09:09:09'), $request_time), + array('1 minute', 60, $this->createTimestamp('2013-12-11 10:08:08'), $request_time), + array('1 minute', 60, $this->createTimestamp('2013-12-11 10:08:08'), $request_time), + array('1 minute 1 second', 1, $this->createTimestamp('2013-12-11 10:08:07'), $request_time), + array('1 minute 59 seconds', 1, $this->createTimestamp('2013-12-11 10:07:09'), $request_time), + array('2 minutes', 60, $this->createTimestamp('2013-12-11 10:07:08'), $request_time), + array('2 minutes 1 second', 1, $this->createTimestamp('2013-12-11 10:07:07'), $request_time), + array('2 minutes 2 seconds', 1, $this->createTimestamp('2013-12-11 10:07:06'), $request_time), + array('2 minutes 2 seconds', 1, $this->createTimestamp('2013-12-11 10:07:06'), $request_time, $granularity_3), + array('2 minutes 2 seconds', 1, $this->createTimestamp('2013-12-11 10:07:06'), $request_time, $granularity_4), + array('30 minutes', 60, $this->createTimestamp('2013-12-11 09:39:08'), $request_time), + array('59 minutes 59 seconds', 1, $this->createTimestamp('2013-12-11 09:09:09'), $request_time), + array('59 minutes 59 seconds', 1, $this->createTimestamp('2013-12-11 09:09:09'), $request_time), // Checks for hours and possibly minutes or seconds. - array('1 hour', $this->createTimestamp('2013-12-11 09:09:08'), $request_time), - array('1 hour', $this->createTimestamp('2013-12-11 09:09:08'), $request_time), - array('1 hour', $this->createTimestamp('2013-12-11 09:09:07'), $request_time), - array('1 hour', $this->createTimestamp('2013-12-11 09:09:06'), $request_time), - array('1 hour 1 minute', $this->createTimestamp('2013-12-11 09:08:08'), $request_time), - array('1 hour 1 minute 1 second', $this->createTimestamp('2013-12-11 09:08:07'), $request_time, $granularity_3), - array('1 hour 1 minute 2 seconds', $this->createTimestamp('2013-12-11 09:08:06'), $request_time, $granularity_4), - array('1 hour 30 minutes', $this->createTimestamp('2013-12-11 08:39:08'), $request_time), - array('2 hours', $this->createTimestamp('2013-12-11 08:09:08'), $request_time), - array('23 hours 59 minutes', $this->createTimestamp('2013-12-10 10:10:08'), $request_time), + array('1 hour', 3600, $this->createTimestamp('2013-12-11 09:09:08'), $request_time), + array('1 hour', 3600, $this->createTimestamp('2013-12-11 09:09:08'), $request_time), + array('1 hour', 3600, $this->createTimestamp('2013-12-11 09:09:07'), $request_time), + array('1 hour', 3600, $this->createTimestamp('2013-12-11 09:09:06'), $request_time), + array('1 hour 1 minute', 60, $this->createTimestamp('2013-12-11 09:08:08'), $request_time), + array('1 hour 1 minute 1 second', 1, $this->createTimestamp('2013-12-11 09:08:07'), $request_time, $granularity_3), + array('1 hour 1 minute 2 seconds', 1, $this->createTimestamp('2013-12-11 09:08:06'), $request_time, $granularity_4), + array('1 hour 30 minutes', 60, $this->createTimestamp('2013-12-11 08:39:08'), $request_time), + array('2 hours', 3600, $this->createTimestamp('2013-12-11 08:09:08'), $request_time), + array('23 hours 59 minutes', 60, $this->createTimestamp('2013-12-10 10:10:08'), $request_time), // Checks for days and possibly hours, minutes or seconds. - array('1 day', $this->createTimestamp('2013-12-10 10:09:08'), $request_time), - array('1 day', $this->createTimestamp('2013-12-10 10:09:07'), $request_time), - array('1 day 1 hour', $this->createTimestamp('2013-12-10 09:09:08'), $request_time), - array('1 day 1 hour 1 minute', $this->createTimestamp('2013-12-10 09:08:07'), $request_time, $granularity_3 + $langcode_en), - array('1 day 1 hour 1 minute 1 second', $this->createTimestamp('2013-12-10 09:08:07'), $request_time, $granularity_4 + $langcode_lolspeak), - array('1 day 2 hours 2 minutes 2 seconds', $this->createTimestamp('2013-12-10 08:07:06'), $request_time, $granularity_4), - array('2 days', $this->createTimestamp('2013-12-09 10:09:08'), $request_time), - array('2 days', $this->createTimestamp('2013-12-09 10:07:08'), $request_time), - array('2 days 2 hours', $this->createTimestamp('2013-12-09 08:09:08'), $request_time), - array('2 days 2 hours 2 minutes', $this->createTimestamp('2013-12-09 08:07:06'), $request_time, $granularity_3 + $langcode_en), - array('2 days 2 hours 2 minutes 2 seconds', $this->createTimestamp('2013-12-09 08:07:06'), $request_time, $granularity_4 + $langcode_lolspeak), + array('1 day', 86400, $this->createTimestamp('2013-12-10 10:09:08'), $request_time), + array('1 day', 86400, $this->createTimestamp('2013-12-10 10:09:07'), $request_time), + array('1 day 1 hour', 3600, $this->createTimestamp('2013-12-10 09:09:08'), $request_time), + array('1 day 1 hour 1 minute', 60, $this->createTimestamp('2013-12-10 09:08:07'), $request_time, $granularity_3 + $langcode_en), + array('1 day 1 hour 1 minute 1 second', 1, $this->createTimestamp('2013-12-10 09:08:07'), $request_time, $granularity_4 + $langcode_lolspeak), + array('1 day 2 hours 2 minutes 2 seconds', 1, $this->createTimestamp('2013-12-10 08:07:06'), $request_time, $granularity_4), + array('2 days', 86400, $this->createTimestamp('2013-12-09 10:09:08'), $request_time), + array('2 days', 86400, $this->createTimestamp('2013-12-09 10:07:08'), $request_time), + array('2 days 2 hours', 3600, $this->createTimestamp('2013-12-09 08:09:08'), $request_time), + array('2 days 2 hours 2 minutes', 60, $this->createTimestamp('2013-12-09 08:07:06'), $request_time, $granularity_3 + $langcode_en), + array('2 days 2 hours 2 minutes 2 seconds', 1, $this->createTimestamp('2013-12-09 08:07:06'), $request_time, $granularity_4 + $langcode_lolspeak), // Checks for weeks and possibly days, hours, minutes or seconds. - array('1 week', $this->createTimestamp('2013-12-04 10:09:08'), $request_time), - array('1 week 1 day', $this->createTimestamp('2013-12-03 10:09:08'), $request_time), - array('2 weeks', $this->createTimestamp('2013-11-27 10:09:08'), $request_time), - array('2 weeks 2 days', $this->createTimestamp('2013-11-25 08:07:08'), $request_time), - array('2 weeks 2 days 2 hours 2 minutes', $this->createTimestamp('2013-11-25 08:07:08'), $request_time, $granularity_4), - array('4 weeks', $this->createTimestamp('2013-11-13 10:09:08'), $request_time), - array('4 weeks 1 day', $this->createTimestamp('2013-11-12 10:09:08'), $request_time), + array('1 week', 7 * 86400, $this->createTimestamp('2013-12-04 10:09:08'), $request_time), + array('1 week 1 day', 86400, $this->createTimestamp('2013-12-03 10:09:08'), $request_time), + array('2 weeks', 7 * 86400, $this->createTimestamp('2013-11-27 10:09:08'), $request_time), + array('2 weeks 2 days', 86400, $this->createTimestamp('2013-11-25 08:07:08'), $request_time), + array('2 weeks 2 days 2 hours 2 minutes', 60, $this->createTimestamp('2013-11-25 08:07:08'), $request_time, $granularity_4), + array('4 weeks', 7 * 86400, $this->createTimestamp('2013-11-13 10:09:08'), $request_time), + array('4 weeks 1 day', 86400, $this->createTimestamp('2013-11-12 10:09:08'), $request_time), // Checks for months and possibly days, hours, minutes or seconds. - array('1 month', $this->createTimestamp('2013-11-11 10:09:08'), $request_time), - array('1 month', $this->createTimestamp('2013-11-11 10:09:07'), $request_time), - array('1 month', $this->createTimestamp('2013-11-11 09:09:08'), $request_time), - array('1 month', $this->createTimestamp('2013-11-11 09:08:07'), $request_time, $granularity_3), - array('1 month', $this->createTimestamp('2013-11-11 09:08:07'), $request_time, $granularity_4), - array('1 month 4 weeks', $this->createTimestamp('2013-10-13 10:09:08'), $request_time), - array('1 month 4 weeks 1 day', $this->createTimestamp('2013-10-13 10:09:08'), $request_time, $granularity_3), - array('1 month 4 weeks', $this->createTimestamp('2013-10-12 10:09:08'), $request_time), - array('1 month 4 weeks 2 days', $this->createTimestamp('2013-10-12 10:09:08'), $request_time, $granularity_3), - array('2 months', $this->createTimestamp('2013-10-11 10:09:08'), $request_time), - array('2 months', $this->createTimestamp('2013-10-10 10:09:08'), $request_time), - array('2 months', $this->createTimestamp('2013-10-09 08:07:06'), $request_time), - array('2 months', $this->createTimestamp('2013-10-09 08:07:06'), $request_time, $granularity_3), - array('2 months', $this->createTimestamp('2013-10-09 08:07:06'), $request_time, $granularity_4), - array('6 months', $this->createTimestamp('2013-06-09 10:09:08'), $request_time), - array('11 months', $this->createTimestamp('2013-01-11 07:09:08'), $request_time), - array('11 months 4 weeks', $this->createTimestamp('2012-12-12 10:09:08'), $request_time), - array('11 months 4 weeks 2 days', $this->createTimestamp('2012-12-12 10:09:08'), $request_time, $granularity_3), + array('1 month', 30 * 86400, $this->createTimestamp('2013-11-11 10:09:08'), $request_time), + array('1 month', 30 * 86400, $this->createTimestamp('2013-11-11 10:09:07'), $request_time), + array('1 month', 30 * 86400, $this->createTimestamp('2013-11-11 09:09:08'), $request_time), + array('1 month', 30 * 86400, $this->createTimestamp('2013-11-11 09:08:07'), $request_time, $granularity_3), + array('1 month', 30 * 86400, $this->createTimestamp('2013-11-11 09:08:07'), $request_time, $granularity_4), + array('1 month 4 weeks', 7 * 86400, $this->createTimestamp('2013-10-13 10:09:08'), $request_time), + array('1 month 4 weeks 1 day', 86400, $this->createTimestamp('2013-10-13 10:09:08'), $request_time, $granularity_3), + array('1 month 4 weeks', 7 * 86400, $this->createTimestamp('2013-10-12 10:09:08'), $request_time), + array('1 month 4 weeks 2 days', 86400, $this->createTimestamp('2013-10-12 10:09:08'), $request_time, $granularity_3), + array('2 months', 30 * 86400, $this->createTimestamp('2013-10-11 10:09:08'), $request_time), + array('2 months', 30 * 86400, $this->createTimestamp('2013-10-10 10:09:08'), $request_time), + array('2 months', 30 * 86400, $this->createTimestamp('2013-10-09 08:07:06'), $request_time), + array('2 months', 30 * 86400, $this->createTimestamp('2013-10-09 08:07:06'), $request_time, $granularity_3), + array('2 months', 30 * 86400, $this->createTimestamp('2013-10-09 08:07:06'), $request_time, $granularity_4), + array('6 months', 30 * 86400, $this->createTimestamp('2013-06-09 10:09:08'), $request_time), + array('11 months', 30 * 86400, $this->createTimestamp('2013-01-11 07:09:08'), $request_time), + array('11 months 4 weeks', 7 * 86400, $this->createTimestamp('2012-12-12 10:09:08'), $request_time), + array('11 months 4 weeks 2 days', 86400, $this->createTimestamp('2012-12-12 10:09:08'), $request_time, $granularity_3), // Checks for years and possibly months, days, hours, minutes or seconds. - array('1 year', $this->createTimestamp('2012-12-11 10:09:08'), $request_time), - array('1 year', $this->createTimestamp('2012-12-11 10:08:08'), $request_time), - array('1 year', $this->createTimestamp('2012-12-10 10:09:08'), $request_time), - array('2 years', $this->createTimestamp('2011-12-11 10:09:08'), $request_time), - array('2 years', $this->createTimestamp('2011-12-11 10:07:08'), $request_time), - array('2 years', $this->createTimestamp('2011-12-09 10:09:08'), $request_time), - array('2 years 2 months', $this->createTimestamp('2011-10-09 08:07:06'), $request_time, $granularity_3), - array('2 years 2 months', $this->createTimestamp('2011-10-09 08:07:06'), $request_time, $granularity_4), - array('10 years', $this->createTimestamp('2003-12-11 10:09:08'), $request_time), - array('100 years', $this->createTimestamp('1913-12-11 10:09:08'), $request_time), + array('1 year', 365 * 86400, $this->createTimestamp('2012-12-11 10:09:08'), $request_time), + array('1 year', 365 * 86400, $this->createTimestamp('2012-12-11 10:08:08'), $request_time), + array('1 year', 365 * 86400, $this->createTimestamp('2012-12-10 10:09:08'), $request_time), + array('2 years', 365 * 86400, $this->createTimestamp('2011-12-11 10:09:08'), $request_time), + array('2 years', 365 * 86400, $this->createTimestamp('2011-12-11 10:07:08'), $request_time), + array('2 years', 365 * 86400, $this->createTimestamp('2011-12-09 10:09:08'), $request_time), + array('2 years 2 months', 30 * 86400, $this->createTimestamp('2011-10-09 08:07:06'), $request_time, $granularity_3), + array('2 years 2 months', 30 * 86400, $this->createTimestamp('2011-10-09 08:07:06'), $request_time, $granularity_4), + array('10 years', 365 * 86400, $this->createTimestamp('2003-12-11 10:09:08'), $request_time), + array('100 years', 365 * 86400, $this->createTimestamp('1913-12-11 10:09:08'), $request_time), // Checks the non-strict option vs. strict (default). - array('1 second', $this->createTimestamp('2013-12-11 10:09:08'), $this->createTimestamp('2013-12-11 10:09:07'), $non_strict), - array('0 seconds', $this->createTimestamp('2013-12-11 10:09:08'), $this->createTimestamp('2013-12-11 10:09:07')), + array('1 second', 1, $this->createTimestamp('2013-12-11 10:09:08'), $this->createTimestamp('2013-12-11 10:09:07'), $non_strict), + array('0 seconds', 0, $this->createTimestamp('2013-12-11 10:09:08'), $this->createTimestamp('2013-12-11 10:09:07')), // Checks granularity limit. - array('2 years 3 months 1 week', $this->createTimestamp('2011-08-30 11:15:57'), $request_time, $granularity_3), + array('2 years 3 months 1 week', 7 * 86400, $this->createTimestamp('2011-08-30 11:15:57'), $request_time, $granularity_3), ); return $data; diff --git a/core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/BackendCompilerPassTest.php b/core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/BackendCompilerPassTest.php index 2efb13f..ee13b93 100644 --- a/core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/BackendCompilerPassTest.php +++ b/core/tests/Drupal/Tests/Core/DependencyInjection/Compiler/BackendCompilerPassTest.php @@ -72,7 +72,8 @@ public function providerTestProcess() { // Configure a manual alias for the service, so ensure that it is not // overridden by the default backend. - $container = clone $container; + $container = $this->getMysqlContainer($service); + $container->setParameter('default_backend', 'mysql'); $container->setDefinition('mariadb.service', new Definition($prefix . 'MariaDb')); $container->setAlias('service', new Alias('mariadb.service')); $data[] = array($prefix . 'MariaDb', $container); diff --git a/core/tests/Drupal/Tests/Core/Enhancer/EntityRevisionRouteEnhancerTest.php b/core/tests/Drupal/Tests/Core/Enhancer/EntityRevisionRouteEnhancerTest.php new file mode 100644 index 0000000..fc3bf6f --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Enhancer/EntityRevisionRouteEnhancerTest.php @@ -0,0 +1,85 @@ +routeEnhancer = new EntityRevisionRouteEnhancer(); + } + + /** + * @covers ::applies + * @dataProvider providerTestApplies + */ + public function testApplies(Route $route, $expected) { + $this->assertEquals($expected, $this->routeEnhancer->applies($route)); + } + + public function providerTestApplies() { + $data = []; + $data['no-parameter'] = [new Route('/test-path'), FALSE]; + $data['none-revision-parameters'] = [new Route('/test-path/{entity_test}', [], [], ['parameters' => ['entity_test' => ['type' => 'entity:entity_test']]]), FALSE]; + $data['with-revision-parameter'] = [new Route('/test-path/{entity_test_revision}', [], [], ['parameters' => ['entity_test_revision' => ['type' => 'entity_revision:entity_test']]]), TRUE]; + + return $data; + } + + /** + * @covers ::enhance + */ + public function testEnhanceWithoutEntityRevision() { + $route = new Route('/test-path/{entity_test}', [], [], ['parameters' => ['entity_test' => ['type' => 'entity:entity_test']]]); + $request = Request::create('/test-path/123'); + $entity = $this->prophesize(EntityInterface::class); + + $defaults = []; + $defaults['entity_test'] = $entity->reveal(); + $defaults[RouteObjectInterface::ROUTE_OBJECT] = $route; + $this->assertEquals($defaults, $this->routeEnhancer->enhance($defaults, $request)); + } + + /** + * @covers ::enhance + */ + public function testEnhanceWithEntityRevision() { + $route = new Route('/test-path/{entity_test_revision}', [], [], ['parameters' => ['entity_test_revision' => ['type' => 'entity_revision:entity_test']]]); + $request = Request::create('/test-path/123'); + $entity = $this->prophesize(EntityInterface::class); + + $defaults = []; + $defaults['entity_test_revision'] = $entity->reveal(); + $defaults[RouteObjectInterface::ROUTE_OBJECT] = $route; + + $expected = $defaults; + $expected['_entity_revision'] = $defaults['entity_test_revision']; + $this->assertEquals($expected, $this->routeEnhancer->enhance($defaults, $request)); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityFormTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityFormTest.php index 810bdd1..30bd12c 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityFormTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityFormTest.php @@ -8,8 +8,14 @@ namespace Drupal\Tests\Core\Entity; use Drupal\Core\Entity\EntityForm; +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityStorageInterface; +use Drupal\Core\Entity\EntityType; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\FormState; +use Drupal\Core\Routing\RouteMatch; use Drupal\Tests\UnitTestCase; +use Symfony\Component\Routing\Route; /** * @coversDefaultClass \Drupal\Core\Entity\EntityForm @@ -25,12 +31,20 @@ class EntityFormTest extends UnitTestCase { protected $entityForm; /** + * A fake entity type used in the test. + * + * @var \Drupal\Core\Entity\EntityTypeInterface + */ + protected $entityType; + + /** * {@inheritdoc} */ protected function setUp() { parent::setUp(); $this->entityForm = new EntityForm(); + $this->entityType = new EntityType(['id' => 'entity_test']); } /** @@ -41,17 +55,13 @@ protected function setUp() { * @dataProvider providerTestFormIds */ public function testFormId($expected, $definition) { - $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface'); - $entity_type->expects($this->any()) - ->method('hasKey') - ->with('bundle') - ->will($this->returnValue($definition['bundle'])); + $this->entityType->set('entity_keys', ['bundle' => $definition['bundle']]); $entity = $this->getMockForAbstractClass('Drupal\Core\Entity\Entity', array(array(), $definition['entity_type']), '', TRUE, TRUE, TRUE, array('getEntityType', 'bundle')); $entity->expects($this->any()) ->method('getEntityType') - ->will($this->returnValue($entity_type)); + ->will($this->returnValue($this->entityType)); $entity->expects($this->any()) ->method('bundle') ->will($this->returnValue($definition['bundle'])); @@ -123,4 +133,115 @@ public function testCopyFormValuesToEntity() { $this->assertNull($result->get('key_controlled_by_plugin_collection')); } + /** + * Tests EntityForm::getEntityFromRouteMatch() for edit and delete forms. + * + * @covers ::getEntityFromRouteMatch + */ + public function testGetEntityFromRouteMatchEditDelete() { + $entity = $this->prophesize(EntityInterface::class)->reveal(); + $id = $this->entityType->id(); + $route_match = new RouteMatch( + 'test_route', + new Route('/entity-test/manage/{' . $id . '}/edit'), + [$id => $entity], + [$id => 1] + ); + $actual = $this->entityForm->getEntityFromRouteMatch($route_match, $id); + $this->assertEquals($entity, $actual); + } + + /** + * Tests EntityForm::getEntityFromRouteMatch() for add forms without a bundle. + * + * @covers ::getEntityFromRouteMatch + */ + public function testGetEntityFromRouteMatchAdd() { + $entity = $this->prophesize(EntityInterface::class)->reveal(); + $this->setUpStorage()->create([])->willReturn($entity); + $route_match = new RouteMatch('test_route', new Route('/entity-test/add')); + $actual = $this->entityForm->getEntityFromRouteMatch($route_match, $this->entityType->id()); + $this->assertEquals($entity, $actual); + } + + /** + * Tests EntityForm::getEntityFromRouteMatch() with a static bundle. + * + * @covers ::getEntityFromRouteMatch + */ + public function testGetEntityFromRouteMatchAddStatic() { + $entity = $this->prophesize(EntityInterface::class)->reveal(); + $bundle_key = 'bundle'; + $bundle = 'test_bundle'; + $this->entityType->set('entity_keys', ['bundle' => $bundle_key]); + $storage = $this->setUpStorage(); + + // Test without a bundle parameter in the route. + $storage->create([])->willReturn($entity); + $route_match = new RouteMatch('test_route', new Route('/entity-test/add')); + $actual = $this->entityForm->getEntityFromRouteMatch($route_match, $this->entityType->id()); + $this->assertEquals($entity, $actual); + + // Test with a static bundle parameter. + $storage->create([$bundle_key => 'test_bundle'])->willReturn($entity); + $route_match = new RouteMatch( + 'test_route', + new Route('/entity-test/add/{' . $bundle_key . '}'), + [$bundle_key => $bundle], + [$bundle_key => $bundle] + ); + $actual = $this->entityForm->getEntityFromRouteMatch($route_match, $this->entityType->id()); + $this->assertEquals($entity, $actual); + } + + /** + * Tests EntityForm::getEntityFromRouteMatch() with a config entity bundle. + * + * @covers ::getEntityFromRouteMatch + */ + public function testGetEntityFromRouteMatchAddEntity() { + $entity = $this->prophesize(EntityInterface::class)->reveal(); + $bundle_entity_type_id = 'entity_test_bundle'; + $bundle = 'test_entity_bundle'; + $this->entityType->set('bundle_entity_type', $bundle_entity_type_id); + $storage = $this->setUpStorage(); + + // Test without a bundle parameter in the route. + $storage->create([])->willReturn($entity); + $route_match = new RouteMatch('test_route', new Route('/entity-test/add')); + $actual = $this->entityForm->getEntityFromRouteMatch($route_match, $this->entityType->id()); + $this->assertEquals($entity, $actual); + + // Test with an entity bundle parameter. + $storage->create(['bundle' => $bundle])->willReturn($entity); + $bundle_entity = $this->prophesize(EntityInterface::class); + $bundle_entity->id()->willReturn('test_entity_bundle'); + $route_match = new RouteMatch( + 'test_route', + new Route('/entity-test/add/{entity_test_bundle}'), + [$bundle_entity_type_id => $bundle_entity->reveal()], + [$bundle_entity_type_id => $bundle] + ); + $actual = $this->entityForm->getEntityFromRouteMatch($route_match, $this->entityType->id()); + $this->assertEquals($entity, $actual); + } + + /** + * Sets up the storage accessed via the entity type manager in the form. + * + * @return \Prophecy\Prophecy\ObjectProphecy + * The storage prophecy. + */ + protected function setUpStorage() { + $storage = $this->prophesize(EntityStorageInterface::class); + + $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class); + $entity_type_manager->getDefinition($this->entityType->id())->willReturn($this->entityType); + $entity_type_manager->getStorage($this->entityType->id())->willReturn($storage->reveal()); + + $this->entityForm->setEntityTypeManager($entity_type_manager->reveal()); + + return $storage; + } + } diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php index ec66c7a..9657827 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php @@ -14,7 +14,6 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\EntityTypeRepositoryInterface; use Drupal\Tests\UnitTestCase; -use Prophecy\Argument; /** * @coversDefaultClass \Drupal\Core\Entity\EntityManager diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php index 6fa1f9c..41dccee 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeTest.php @@ -10,7 +10,6 @@ use Drupal\Core\Entity\EntityType; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; -use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Tests\UnitTestCase; /** @@ -311,15 +310,7 @@ public function testGetGroupLabel() { $this->assertSame($default_label, $entity_type->getGroupLabel()); $default_label = new TranslatableMarkup('Other', array(), array('context' => 'Entity type group')); - $entity_type = $this->setUpEntityType([]); - - $string_translation = $this->getMock(TranslationInterface::class); - $string_translation->expects($this->atLeastOnce()) - ->method('translate') - ->with('Other', array(), array('context' => 'Entity type group')) - ->willReturn($default_label); - $entity_type->setStringTranslation($string_translation); - + $entity_type = $this->setUpEntityType(array('group_label' => $default_label)); $this->assertSame($default_label, $entity_type->getGroupLabel()); } diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php index 3f1e2e0..4df1fcc 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php @@ -10,9 +10,7 @@ use Drupal\Core\Access\AccessResult; use Drupal\Core\Cache\Cache; use Drupal\Core\DependencyInjection\ContainerBuilder; -use Drupal\Core\Entity\Entity; use Drupal\Core\Language\Language; -use Drupal\entity_test\Entity\EntityTestMul; use Drupal\Tests\UnitTestCase; /** diff --git a/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php b/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php index cdfd0f2..54393d4 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php @@ -7,13 +7,16 @@ namespace Drupal\Tests\Core\Entity\Routing; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Config\Entity\ConfigEntityTypeInterface; +use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Tests\UnitTestCase; use Prophecy\Argument; +use Prophecy\Prophecy\ObjectProphecy; use Symfony\Component\Routing\Route; /** @@ -23,78 +26,199 @@ class DefaultHtmlRouteProviderTest extends UnitTestCase { /** - * @covers ::getEntityTypeIdKeyType + * The entity type manager prophecy used in the test. + * + * @var \Prophecy\Prophecy\ProphecyInterface|\Drupal\Core\Entity\EntityTypeManagerInterface */ - public function testGetEntityTypeIdKeyType() { - $entity_manager = $this->prophesize(EntityManagerInterface::class); - $route_provider = new TestDefaultHtmlRouteProvider($entity_manager->reveal()); + protected $entityTypeManager; - $entity_type = $this->prophesize(EntityTypeInterface::class); - $entity_type->isSubclassOf(FieldableEntityInterface::class)->willReturn(TRUE); - $entity_type_id = 'the_entity_type_id'; - $entity_type->id()->willReturn($entity_type_id); - $entity_type->getKey('id')->willReturn('id'); + /** + * The entity field manager prophecy used in the test. + * + * @var \Prophecy\Prophecy\ProphecyInterface|\Drupal\Core\Entity\EntityFieldManagerInterface + */ + protected $entityFieldManager; - $field_storage_definition = $this->prophesize(FieldStorageDefinitionInterface::class); - $field_storage_definition->getType()->willReturn('integer'); - $entity_manager->getFieldStorageDefinitions($entity_type_id)->willReturn(['id' => $field_storage_definition]); + /** + * The HTML route provider used in the test. + * + * @var \Drupal\Tests\Core\Entity\Routing\TestDefaultHtmlRouteProvider + */ + protected $routeProvider; - $type = $route_provider->getEntityTypeIdKeyType($entity_type->reveal()); - $this->assertSame('integer', $type); + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class); + $this->entityFieldManager = $this->prophesize(EntityFieldManagerInterface::class); + + $this->routeProvider = new TestDefaultHtmlRouteProvider($this->entityTypeManager->reveal(), $this->entityFieldManager->reveal()); } /** - * @covers ::getEntityTypeIdKeyType + * @covers ::getAddPageRoute + * @dataProvider providerTestGetAddPageRoute */ - public function testGetEntityTypeIdKeyTypeNotFieldable() { - $entity_manager = $this->prophesize(EntityManagerInterface::class); - $route_provider = new TestDefaultHtmlRouteProvider($entity_manager->reveal()); + public function testGetAddPageRoute(Route $expected = NULL, EntityTypeInterface $entity_type) { + $route = $this->routeProvider->getAddPageRoute($entity_type); + $this->assertEquals($expected, $route); + } - $entity_type = $this->prophesize(EntityTypeInterface::class); - $entity_type->isSubclassOf(FieldableEntityInterface::class)->willReturn(FALSE); - $entity_manager->getFieldStorageDefinitions(Argument::any())->shouldNotBeCalled(); + public function providerTestGetAddPageRoute() { + $data = []; - $type = $route_provider->getEntityTypeIdKeyType($entity_type->reveal()); - $this->assertNull($type); + $entity_type1 = $this->getEntityType(); + $entity_type1->hasLinkTemplate('add-page')->willReturn(FALSE); + $data['no_add_page_link_template'] = [NULL, $entity_type1->reveal()]; + + $entity_type2 = $this->getEntityType(); + $entity_type2->hasLinkTemplate('add-page')->willReturn(TRUE); + $entity_type2->getKey('bundle')->willReturn(NULL); + $data['no_bundle'] = [NULL, $entity_type2->reveal()]; + + $entity_type3 = $this->getEntityType(); + $entity_type3->hasLinkTemplate('add-page')->willReturn(TRUE); + $entity_type3->getLinkTemplate('add-page')->willReturn('/the/add/page/link/template'); + $entity_type3->id()->willReturn('the_entity_type_id'); + $entity_type3->getKey('bundle')->willReturn('type'); + $route = new Route('/the/add/page/link/template'); + $route->setDefaults([ + '_controller' => 'Drupal\Core\Entity\Controller\EntityController::addPage', + '_title_callback' => 'Drupal\Core\Entity\Controller\EntityController::addTitle', + 'entity_type_id' => 'the_entity_type_id', + ]); + $route->setRequirement('_entity_create_access', 'the_entity_type_id'); + $data['add_page'] = [clone $route, $entity_type3->reveal()]; + + return $data; + } + + /** + * @covers ::getAddFormRoute + * @dataProvider providerTestGetAddFormRoute + */ + public function testGetAddFormRoute(Route $expected = NULL, EntityTypeInterface $entity_type, EntityTypeInterface $bundle_entity_type = NULL, FieldStorageDefinitionInterface $field_storage_definition = NULL) { + if ($bundle_entity_type) { + $this->entityTypeManager->getDefinition('the_bundle_entity_type_id')->willReturn($bundle_entity_type); + + if ($field_storage_definition) { + $this->entityFieldManager->getFieldStorageDefinitions('the_bundle_entity_type_id') + ->willReturn(['id' => $field_storage_definition]); + } + } + + $route = $this->routeProvider->getAddFormRoute($entity_type); + $this->assertEquals($expected, $route); + } + + public function providerTestGetAddFormRoute() { + $data = []; + + $entity_type1 = $this->getEntityType(); + $entity_type1->hasLinkTemplate('add-form')->willReturn(FALSE); + $data['no_add_form_link_template'] = [NULL, $entity_type1->reveal()]; + + $entity_type2 = $this->getEntityType(); + $entity_type2->hasLinkTemplate('add-form')->willReturn(TRUE); + $entity_type2->id()->willReturn('the_entity_type_id'); + $entity_type2->getLinkTemplate('add-form')->willReturn('/the/add/form/link/template'); + $entity_type2->getFormClass('add')->willReturn(NULL); + $entity_type2->getKey('bundle')->willReturn(NULL); + $route = (new Route('/the/add/form/link/template')) + ->setDefaults([ + '_entity_form' => 'the_entity_type_id.default', + 'entity_type_id' => 'the_entity_type_id', + '_title_callback' => 'Drupal\Core\Entity\Controller\EntityController::addTitle', + ]) + ->setRequirement('_entity_create_access', 'the_entity_type_id') + ; + $data['no_add_form_no_bundle'] = [clone $route, $entity_type2->reveal()]; + + $entity_type3 = $this->getEntityType($entity_type2); + $entity_type3->getFormClass('add')->willReturn('Drupal\Core\Entity\EntityForm'); + $route->setDefault('_entity_form', 'the_entity_type_id.add'); + $data['add_form_no_bundle'] = [clone $route, $entity_type3->reveal()]; + + $entity_type4 = $this->getEntityType($entity_type3); + $entity_type4->getKey('bundle')->willReturn('the_bundle_key'); + $entity_type4->getBundleEntityType()->willReturn(NULL); + $route + ->setDefault('_title_callback', 'Drupal\Core\Entity\Controller\EntityController::addBundleTitle') + ->setDefault('bundle_parameter', 'the_bundle_key') + ->setRequirement('_entity_create_access', 'the_entity_type_id:the_bundle_key'); + $data['add_form_bundle_static'] = [clone $route, $entity_type4->reveal()]; + + $entity_type5 = $this->getEntityType($entity_type4); + $entity_type5->getBundleEntityType()->willReturn('the_bundle_entity_type_id'); + $bundle_entity_type = $this->getEntityType(); + $bundle_entity_type->isSubclassOf(FieldableEntityInterface::class)->willReturn(FALSE); + $route + ->setDefault('bundle_parameter', 'the_bundle_entity_type_id') + ->setRequirement('_entity_create_access', 'the_entity_type_id:the_bundle_entity_type_id') + ->setOption('parameters', ['the_bundle_entity_type_id' => [ + 'type' => 'entity:the_bundle_entity_type_id', + ]]); + $data['add_form_bundle_entity_id_key_type_null'] = [clone $route, $entity_type5->reveal(), $bundle_entity_type->reveal()]; + + $entity_type6 = $this->getEntityType($entity_type5); + $bundle_entity_type = $this->getEntityType(); + $bundle_entity_type->isSubclassOf(FieldableEntityInterface::class)->willReturn(TRUE); + $field_storage_definition = $this->prophesize(FieldStorageDefinitionInterface::class); + $field_storage_definition->getType()->willReturn('integer'); + $route->setRequirement('the_entity_type_id', '\d+'); + $data['add_form_bundle_entity_id_key_type_integer'] = [clone $route, $entity_type6->reveal(), $bundle_entity_type->reveal(), $field_storage_definition->reveal()]; + + $entity_type7 = $this->getEntityType($entity_type6); + $bundle_entity_type = $this->prophesize(ConfigEntityTypeInterface::class); + $bundle_entity_type->isSubclassOf(FieldableEntityInterface::class)->willReturn(FALSE); + $field_storage_definition = $this->prophesize(FieldStorageDefinitionInterface::class); + $route + // Unset the 'the_entity_type_id' requirement. + ->setRequirements(['_entity_create_access' => $route->getRequirement('_entity_create_access')]) + ->setOption('parameters', ['the_bundle_entity_type_id' => [ + 'type' => 'entity:the_bundle_entity_type_id', + 'with_config_overrides' => TRUE, + ]]); + $data['add_form_bundle_entity_id_key_type_integer'] = [clone $route, $entity_type7->reveal(), $bundle_entity_type->reveal(), $field_storage_definition->reveal()]; + + return $data; } /** * @covers ::getCanonicalRoute * @dataProvider providerTestGetCanonicalRoute */ - public function testGetCanonicalRoute($entity_type_prophecy, $expected, $field_storage_definition = NULL) { - $entity_manager = $this->prophesize(EntityManagerInterface::class); - $route_provider = new TestDefaultHtmlRouteProvider($entity_manager->reveal()); - $entity_type = $entity_type_prophecy->reveal(); - + public function testGetCanonicalRoute(Route $expected = NULL, EntityTypeInterface $entity_type, FieldStorageDefinitionInterface $field_storage_definition = NULL) { if ($field_storage_definition) { - $entity_manager->getFieldStorageDefinitions($entity_type->id()) + $this->entityFieldManager->getFieldStorageDefinitions($entity_type->id()) ->willReturn([$entity_type->getKey('id') => $field_storage_definition]); } - $route = $route_provider->getCanonicalRoute($entity_type); + $route = $this->routeProvider->getCanonicalRoute($entity_type); $this->assertEquals($expected, $route); } public function providerTestGetCanonicalRoute() { $data = []; - $entity_type1 = $this->prophesize(EntityTypeInterface::class); + $entity_type1 = $this->getEntityType(); $entity_type1->hasLinkTemplate('canonical')->willReturn(FALSE); - $data['no_canonical_link_template'] = [$entity_type1, NULL]; + $data['no_canonical_link_template'] = [NULL, $entity_type1->reveal()]; - $entity_type2 = $this->prophesize(EntityTypeInterface::class); + $entity_type2 = $this->getEntityType();; $entity_type2->hasLinkTemplate('canonical')->willReturn(TRUE); $entity_type2->hasViewBuilderClass()->willReturn(FALSE); - $data['no_view_builder'] = [$entity_type2, NULL]; + $data['no_view_builder'] = [NULL, $entity_type2->reveal()]; - $entity_type3 = $this->prophesize(EntityTypeInterface::class); - $entity_type3->hasLinkTemplate('canonical')->willReturn(TRUE); + $entity_type3 = $this->getEntityType($entity_type2); $entity_type3->hasViewBuilderClass()->willReturn(TRUE); $entity_type3->id()->willReturn('the_entity_type_id'); $entity_type3->getLinkTemplate('canonical')->willReturn('/the/canonical/link/template'); $entity_type3->isSubclassOf(FieldableEntityInterface::class)->willReturn(FALSE); - $route3 = (new Route('/the/canonical/link/template')) + $route = (new Route('/the/canonical/link/template')) ->setDefaults([ '_entity_view' => 'the_entity_type_id.full', '_title_callback' => '\Drupal\Core\Entity\Controller\EntityController::title', @@ -109,38 +233,64 @@ public function providerTestGetCanonicalRoute() { ], ], ]); - $data['id_key_type_null'] = [$entity_type3, $route3]; + $data['id_key_type_null'] = [clone $route, $entity_type3->reveal()]; - $entity_type4 = $this->prophesize(EntityTypeInterface::class); - $entity_type4->hasLinkTemplate('canonical')->willReturn(TRUE); - $entity_type4->hasViewBuilderClass()->willReturn(TRUE); - $entity_type4->id()->willReturn('the_entity_type_id'); - $entity_type4->getLinkTemplate('canonical')->willReturn('/the/canonical/link/template'); + $entity_type4 = $this->getEntityType($entity_type3); $entity_type4->isSubclassOf(FieldableEntityInterface::class)->willReturn(TRUE); $entity_type4->getKey('id')->willReturn('id'); - $route4 = (new Route('/the/canonical/link/template')) - ->setDefaults([ - '_entity_view' => 'the_entity_type_id.full', - '_title_callback' => '\Drupal\Core\Entity\Controller\EntityController::title', - ]) - ->setRequirements([ - '_entity_access' => 'the_entity_type_id.view', - 'the_entity_type_id' => '\d+', - ]) - ->setOptions([ - 'parameters' => [ - 'the_entity_type_id' => [ - 'type' => 'entity:the_entity_type_id', - ], - ], - ]); + $route->setRequirement('the_entity_type_id', '\d+'); $field_storage_definition = $this->prophesize(FieldStorageDefinitionInterface::class); $field_storage_definition->getType()->willReturn('integer'); - $data['id_key_type_integer'] = [$entity_type4, $route4, $field_storage_definition]; + $data['id_key_type_integer'] = [clone $route, $entity_type4->reveal(), $field_storage_definition->reveal()]; return $data; } + /** + * @covers ::getEntityTypeIdKeyType + */ + public function testGetEntityTypeIdKeyType() { + $entity_type = $this->prophesize(EntityTypeInterface::class); + $entity_type->isSubclassOf(FieldableEntityInterface::class)->willReturn(TRUE); + $entity_type->id()->willReturn('the_entity_type_id'); + $entity_type->getKey('id')->willReturn('id'); + + $field_storage_definition = $this->prophesize(FieldStorageDefinitionInterface::class); + $field_storage_definition->getType()->willReturn('integer'); + $this->entityFieldManager->getFieldStorageDefinitions('the_entity_type_id')->willReturn(['id' => $field_storage_definition]); + + $type = $this->routeProvider->getEntityTypeIdKeyType($entity_type->reveal()); + $this->assertSame('integer', $type); + } + + /** + * @covers ::getEntityTypeIdKeyType + */ + public function testGetEntityTypeIdKeyTypeNotFieldable() { + $entity_type = $this->prophesize(EntityTypeInterface::class); + $entity_type->isSubclassOf(FieldableEntityInterface::class)->willReturn(FALSE); + $this->entityFieldManager->getFieldStorageDefinitions(Argument::any())->shouldNotBeCalled(); + + $type = $this->routeProvider->getEntityTypeIdKeyType($entity_type->reveal()); + $this->assertNull($type); + } + + /** + * @param \Prophecy\Prophecy\ObjectProphecy $base_entity_type + * @return \Prophecy\Prophecy\ObjectProphecy + */ + protected function getEntityType(ObjectProphecy $base_entity_type = NULL) { + $entity_type = $this->prophesize(EntityTypeInterface::class); + if ($base_entity_type) { + foreach ($base_entity_type->getMethodProphecies() as $method => $prophecies) { + foreach ($prophecies as $prophecy) { + $entity_type->addMethodProphecy(clone $prophecy); + } + } + } + return $entity_type; + } + } class TestDefaultHtmlRouteProvider extends DefaultHtmlRouteProvider { @@ -148,6 +298,12 @@ class TestDefaultHtmlRouteProvider extends DefaultHtmlRouteProvider { public function getEntityTypeIdKeyType(EntityTypeInterface $entity_type) { return parent::getEntityTypeIdKeyType($entity_type); } + public function getAddPageRoute(EntityTypeInterface $entity_type) { + return parent::getAddPageRoute($entity_type); + } + public function getAddFormRoute(EntityTypeInterface $entity_type) { + return parent::getAddFormRoute($entity_type); + } public function getCanonicalRoute(EntityTypeInterface $entity_type) { return parent::getCanonicalRoute($entity_type); } diff --git a/core/tests/Drupal/Tests/Core/EventSubscriber/CustomPageExceptionHtmlSubscriberTest.php b/core/tests/Drupal/Tests/Core/EventSubscriber/CustomPageExceptionHtmlSubscriberTest.php index a7efb7e..e931a0c 100644 --- a/core/tests/Drupal/Tests/Core/EventSubscriber/CustomPageExceptionHtmlSubscriberTest.php +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/CustomPageExceptionHtmlSubscriberTest.php @@ -8,7 +8,12 @@ namespace Drupal\Tests\Core\EventSubscriber; use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\Access\AccessResult; +use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\EventSubscriber\CustomPageExceptionHtmlSubscriber; +use Drupal\Core\Render\HtmlResponse; +use Drupal\Core\Routing\AccessAwareRouterInterface; +use Drupal\Core\Url; use Drupal\Tests\UnitTestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -36,13 +41,6 @@ class CustomPageExceptionHtmlSubscriberTest extends UnitTestCase { protected $configFactory; /** - * The mocked alias manager. - * - * @var \Drupal\Core\Path\AliasManagerInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $aliasManager; - - /** * The mocked logger. * * @var \Psr\Log\LoggerInterface @@ -71,21 +69,50 @@ class CustomPageExceptionHtmlSubscriberTest extends UnitTestCase { protected $redirectDestination; /** + * The mocked access unaware router. + * @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $accessUnawareRouter; + + /** + * The access manager. + * + * @var \Drupal\Core\Access\AccessManagerInterface + */ + protected $accessManager; + + /** * {@inheritdoc} */ protected function setUp() { - $this->configFactory = $this->getConfigFactoryStub(['system.site' => ['page.403' => 'access-denied-page', 'page.404' => 'not-found-page']]); + $this->configFactory = $this->getConfigFactoryStub(['system.site' => ['page.403' => '/access-denied-page', 'page.404' => '/not-found-page']]); - $this->aliasManager = $this->getMock('Drupal\Core\Path\AliasManagerInterface'); $this->kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'); $this->logger = $this->getMock('Psr\Log\LoggerInterface'); $this->redirectDestination = $this->getMock('\Drupal\Core\Routing\RedirectDestinationInterface'); - $this->redirectDestination->expects($this->any()) ->method('getAsArray') ->willReturn(['destination' => 'test']); - - $this->customPageSubscriber = new CustomPageExceptionHtmlSubscriber($this->configFactory, $this->aliasManager, $this->kernel, $this->logger, $this->redirectDestination); + $this->accessUnawareRouter = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface'); + $this->accessUnawareRouter->expects($this->any()) + ->method('match') + ->willReturn([ + '_controller' => 'mocked', + ]); + $this->accessManager = $this->getMock('Drupal\Core\Access\AccessManagerInterface'); + $this->accessManager->expects($this->any()) + ->method('checkNamedRoute') + ->willReturn(AccessResult::allowed()->addCacheTags(['foo', 'bar'])); + + $this->customPageSubscriber = new CustomPageExceptionHtmlSubscriber($this->configFactory, $this->kernel, $this->logger, $this->redirectDestination, $this->accessUnawareRouter, $this->accessManager); + + $path_validator = $this->getMock('Drupal\Core\Path\PathValidatorInterface'); + $path_validator->expects($this->any()) + ->method('getUrlIfValidWithoutAccessCheck') + ->willReturn(Url::fromRoute('foo', ['foo' => 'bar'])); + $container = new ContainerBuilder(); + $container->set('path.validator', $path_validator); + \Drupal::setContainer($container); // You can't create an exception in PHP without throwing it. Store the // current error_log, and disable it temporarily. @@ -100,24 +127,13 @@ protected function tearDown() { } /** - * Sets up an alias manager that does nothing. - */ - protected function setupStubAliasManager() { - $this->aliasManager->expects($this->any()) - ->method('getPathByAlias') - ->willReturnArgument(0); - } - - /** * Tests onHandleException with a POST request. */ public function testHandleWithPostRequest() { - $this->setupStubAliasManager(); - $request = Request::create('/test', 'POST', array('name' => 'druplicon', 'pass' => '12345')); $this->kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { - return new Response($request->getMethod()); + return new HtmlResponse($request->getMethod()); })); $event = new GetResponseForExceptionEvent($this->kernel, $request, 'foo', new NotFoundHttpException('foo')); @@ -127,15 +143,15 @@ public function testHandleWithPostRequest() { $response = $event->getResponse(); $result = $response->getContent() . " " . UrlHelper::buildQuery($request->request->all()); $this->assertEquals('POST name=druplicon&pass=12345', $result); + $this->assertEquals(AccessResult::allowed()->addCacheTags(['foo', 'bar']), $request->attributes->get(AccessAwareRouterInterface::ACCESS_RESULT)); } /** * Tests onHandleException with a GET request. */ public function testHandleWithGetRequest() { - $this->setupStubAliasManager(); - $request = Request::create('/test', 'GET', array('name' => 'druplicon', 'pass' => '12345')); + $request->attributes->set(AccessAwareRouterInterface::ACCESS_RESULT, AccessResult::forbidden()->addCacheTags(['druplicon'])); $this->kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { return new Response($request->getMethod() . ' ' . UrlHelper::buildQuery($request->query->all())); @@ -147,6 +163,7 @@ public function testHandleWithGetRequest() { $response = $event->getResponse(); $result = $response->getContent() . " " . UrlHelper::buildQuery($request->request->all()); $this->assertEquals('GET name=druplicon&pass=12345&destination=test&_exception_statuscode=404 ', $result); + $this->assertEquals(AccessResult::forbidden()->addCacheTags(['druplicon', 'foo', 'bar']), $request->attributes->get(AccessAwareRouterInterface::ACCESS_RESULT)); } } diff --git a/core/tests/Drupal/Tests/Core/EventSubscriber/DefaultExceptionSubscriberTest.php b/core/tests/Drupal/Tests/Core/EventSubscriber/DefaultExceptionSubscriberTest.php new file mode 100644 index 0000000..ffd8e92 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/DefaultExceptionSubscriberTest.php @@ -0,0 +1,45 @@ +getConfigFactoryStub(); + + // Format 'bananas' requested, yet only 'json' allowed. + $kernel = $this->prophesize(HttpKernelInterface::class); + $request = Request::create('/test?_format=bananas'); + $e = new MethodNotAllowedHttpException(['json'], 'test message'); + $event = new GetResponseForExceptionEvent($kernel->reveal(), $request, 'GET', $e); + $subscriber = new DefaultExceptionSubscriber($config_factory); + $subscriber->onException($event); + $response = $event->getResponse(); + + $this->assertInstanceOf(Response::class, $response); + $this->assertEquals('test message', $response->getContent()); + $this->assertEquals(405, $response->getStatusCode()); + } + +} diff --git a/core/tests/Drupal/Tests/Core/EventSubscriber/OptionsRequestSubscriberTest.php b/core/tests/Drupal/Tests/Core/EventSubscriber/OptionsRequestSubscriberTest.php new file mode 100644 index 0000000..7c0e454 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/OptionsRequestSubscriberTest.php @@ -0,0 +1,114 @@ +prophesize(HttpKernelInterface::class); + $request = Request::create('/example', 'GET'); + + $route_provider = $this->prophesize(RouteProviderInterface::class); + $route_provider->getRouteCollectionForRequest($request)->shouldNotBeCalled(); + + $subscriber = new OptionsRequestSubscriber($route_provider->reveal()); + $event = new GetResponseEvent($kernel->reveal(), $request, HttpKernelInterface::MASTER_REQUEST); + $subscriber->onRequest($event); + + $this->assertFalse($event->hasResponse()); + } + + /** + * @covers ::onRequest + */ + public function testWithoutMatchingRoutes() { + $kernel = $this->prophesize(HttpKernelInterface::class); + $request = Request::create('/example', 'OPTIONS'); + + $route_provider = $this->prophesize(RouteProviderInterface::class); + $route_provider->getRouteCollectionForRequest($request)->willReturn(new RouteCollection())->shouldBeCalled(); + + $subscriber = new OptionsRequestSubscriber($route_provider->reveal()); + $event = new GetResponseEvent($kernel->reveal(), $request, HttpKernelInterface::MASTER_REQUEST); + $subscriber->onRequest($event); + + $this->assertFalse($event->hasResponse()); + } + + /** + * @covers ::onRequest + * @dataProvider providerTestOnRequestWithOptionsRequest + */ + public function testWithOptionsRequest(RouteCollection $collection, $expected_header) { + $kernel = $this->prophesize(HttpKernelInterface::class); + $request = Request::create('/example', 'OPTIONS'); + + $route_provider = $this->prophesize(RouteProviderInterface::class); + $route_provider->getRouteCollectionForRequest($request)->willReturn($collection)->shouldBeCalled(); + + $subscriber = new OptionsRequestSubscriber($route_provider->reveal()); + $event = new GetResponseEvent($kernel->reveal(), $request, HttpKernelInterface::MASTER_REQUEST); + $subscriber->onRequest($event); + + $this->assertTrue($event->hasResponse()); + $response = $event->getResponse(); + $this->assertEquals(200, $response->getStatusCode()); + $this->assertEquals($expected_header, $response->headers->get('Allow')); + } + + public function providerTestOnRequestWithOptionsRequest() { + $data = []; + + foreach (['GET', 'POST', 'PATCH', 'PUT', 'DELETE'] as $method) { + $collection = new RouteCollection(); + $collection->add('example.1', new Route('/example', [], [], [], '', [], [$method])); + $data['one_route_' . $method] = [$collection, $method]; + } + + foreach (['GET', 'POST', 'PATCH', 'PUT', 'DELETE'] as $method_a) { + foreach (['GET', 'POST', 'PATCH', 'PUT', 'DELETE'] as $method_b) { + if ($method_a != $method_b) { + $collection = new RouteCollection(); + $collection->add('example.1', new Route('/example', [], [], [], '', [], [$method_a, $method_b])); + $data['one_route_' . $method_a . '_' . $method_b] = [$collection, $method_a . ', ' . $method_b]; + } + } + } + + foreach (['GET', 'POST', 'PATCH', 'PUT', 'DELETE'] as $method_a) { + foreach (['GET', 'POST', 'PATCH', 'PUT', 'DELETE'] as $method_b) { + foreach (['GET', 'POST', 'PATCH', 'PUT', 'DELETE'] as $method_c) { + $collection = new RouteCollection(); + $collection->add('example.1', new Route('/example', [], [], [], '', [], [$method_a])); + $collection->add('example.2', new Route('/example', [], [], [], '', [], [$method_a, $method_b])); + $collection->add('example.3', new Route('/example', [], [], [], '', [], [$method_b, $method_c])); + $methods = array_unique([$method_a, $method_b, $method_c]); + $data['multiple_routes_' . $method_a . '_' . $method_b . '_' . $method_c] = [$collection, implode(', ', $methods)]; + } + } + } + + return $data; + } + +} diff --git a/core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test/hook_include.inc b/core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test/hook_include.inc index 3576f28..00c781d 100644 --- a/core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test/hook_include.inc +++ b/core/tests/Drupal/Tests/Core/Extension/modules/module_handler_test/hook_include.inc @@ -1,3 +1,8 @@ assertSame([], $plugin_settings->getThirdPartySettings()); + $this->assertSame([], $plugin_settings->getThirdPartySettings('test')); + $plugin_settings->setThirdPartySetting('test', 'foo', 'bar'); + $this->assertSame(['foo' => 'bar'], $plugin_settings->getThirdPartySettings('test')); + $this->assertSame([], $plugin_settings->getThirdPartySettings('test2')); + } + +} + +class TestPluginSettingsBase extends PluginSettingsBase { + + public function __construct() { + } + +} diff --git a/core/tests/Drupal/Tests/Core/Form/FormStateTest.php b/core/tests/Drupal/Tests/Core/Form/FormStateTest.php index 24904e3..23d7a00 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormStateTest.php +++ b/core/tests/Drupal/Tests/Core/Form/FormStateTest.php @@ -536,7 +536,7 @@ public function providerTestIsMethodType() { * @covers ::setTemporaryValue */ public function testTemporaryValue() { - $form_state = New FormState(); + $form_state = new FormState(); $this->assertFalse($form_state->hasTemporaryValue('rainbow_sparkles')); $form_state->setTemporaryValue('rainbow_sparkles', 'yes please'); $this->assertSame($form_state->getTemporaryValue('rainbow_sparkles'), 'yes please'); diff --git a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php index dd34082..6eebd80 100644 --- a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php +++ b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php @@ -12,6 +12,8 @@ use Drupal\Tests\UnitTestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; +use Psr\Log\LoggerInterface; +use Psr\Log\LoggerTrait; /** * @coversDefaultClass \Drupal\Core\Logger\LoggerChannel @@ -55,6 +57,21 @@ public function testLog(callable $expected, Request $request = NULL, AccountInte } /** + * Tests LoggerChannel::log() recursion protection. + * + * @covers ::log + */ + public function testLogRecursionProtection() { + $channel = new LoggerChannel('test'); + $logger = $this->getMock('Psr\Log\LoggerInterface'); + $logger->expects($this->exactly(LoggerChannel::MAX_CALL_DEPTH)) + ->method('log'); + $channel->addLogger($logger); + $channel->addLogger(new NaughtyRecursiveLogger($channel)); + $channel->log(rand(0, 7), $this->randomMachineName()); + } + + /** * Tests LoggerChannel::addLoggers(). * * @covers ::addLogger @@ -129,3 +146,19 @@ function ($context) { } } + +class NaughtyRecursiveLogger implements LoggerInterface { + use LoggerTrait; + + protected $channel; + protected $message; + + public function __construct(LoggerChannel $channel) { + $this->channel = $channel; + } + + public function log($level, $message, array $context = []) { + $this->channel->log(rand(0, 7), $message, $context); + } +} + diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php index c29799d..21c8839 100644 --- a/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php @@ -12,7 +12,6 @@ use Drupal\Core\Access\AccessManagerInterface; use Drupal\Core\Access\AccessResult; use Drupal\Core\Access\AccessResultForbidden; -use Drupal\Core\Cache\Cache; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Menu\LocalActionManager; @@ -213,10 +212,7 @@ public function getActionsForRouteProvider() { '#cache' => array( 'contexts' => array(), 'tags' => array(), - // For back-compatibility in 8.0.x the max-age is Cache::PERMANENT - // instead of 0 for any class that does not implement - // \Drupal\Core\Cache\CacheableDependencyInterface. - 'max-age' => Cache::PERMANENT, + 'max-age' => 0, ), ), ), @@ -258,7 +254,7 @@ public function getActionsForRouteProvider() { '#cache' => array( 'contexts' => array(), 'tags' => array(), - 'max-age' => Cache::PERMANENT, + 'max-age' => 0, ), ), ), @@ -301,7 +297,7 @@ public function getActionsForRouteProvider() { '#cache' => array( 'contexts' => array(), 'tags' => array(), - 'max-age' => Cache::PERMANENT, + 'max-age' => 0, ), ), 'plugin_id_2' => array( @@ -316,7 +312,7 @@ public function getActionsForRouteProvider() { '#cache' => array( 'contexts' => array(), 'tags' => array(), - 'max-age' => Cache::PERMANENT, + 'max-age' => 0, ), ), ), @@ -361,7 +357,7 @@ public function getActionsForRouteProvider() { '#cache' => array( 'contexts' => array(), 'tags' => array(), - 'max-age' => Cache::PERMANENT, + 'max-age' => 0, ), ), 'plugin_id_2' => array( @@ -376,7 +372,7 @@ public function getActionsForRouteProvider() { '#cache' => array( 'contexts' => array(), 'tags' => array(), - 'max-age' => Cache::PERMANENT, + 'max-age' => 0, ), ), ), diff --git a/core/tests/Drupal/Tests/Core/ParamConverter/EntityRevisionParamConverterTest.php b/core/tests/Drupal/Tests/Core/ParamConverter/EntityRevisionParamConverterTest.php new file mode 100644 index 0000000..0d2988e --- /dev/null +++ b/core/tests/Drupal/Tests/Core/ParamConverter/EntityRevisionParamConverterTest.php @@ -0,0 +1,82 @@ +converter = new EntityRevisionParamConverter($this->prophesize(EntityTypeManagerInterface::class)->reveal()); + } + + protected function getTestRoute() { + $route = new Route('/test/{test_revision}'); + $route->setOption('parameters', [ + 'test_revision' => [ + 'type' => 'entity_revision:test', + ], + ]); + return $route; + } + + /** + * @covers ::applies + */ + public function testNonApplyingRoute() { + $route = new Route('/test'); + $this->assertFalse($this->converter->applies([], 'test_revision', $route)); + } + + /** + * @covers ::applies + */ + public function testApplyingRoute() { + $route = $this->getTestRoute(); + $this->assertTrue($this->converter->applies($route->getOption('parameters')['test_revision'], 'test_revision', $route)); + } + + /** + * @covers ::convert + */ + public function testConvert() { + $entity = $this->prophesize(EntityInterface::class)->reveal(); + $storage = $this->prophesize(EntityStorageInterface::class); + $storage->loadRevision(1)->willReturn($entity); + + $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class); + $entity_type_manager->getStorage('test')->willReturn($storage->reveal()); + $converter = new EntityRevisionParamConverter($entity_type_manager->reveal()); + + $route = $this->getTestRoute(); + $result = $converter->convert(1, $route->getOption('parameters')['test_revision'], 'test_revision', ['test_revision' => 1]); + $this->assertSame($entity, $result); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Plugin/Discovery/YamlDirectoryDiscoveryTest.php b/core/tests/Drupal/Tests/Core/Plugin/Discovery/YamlDirectoryDiscoveryTest.php new file mode 100644 index 0000000..d7b2698 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Plugin/Discovery/YamlDirectoryDiscoveryTest.php @@ -0,0 +1,102 @@ + [ + 'subdir1' => [ + 'plugin1.yml' => "id: plugin1\ntest_provider: module_a", + 'plugin2.yml' => "id: plugin2\ntest_provider: module_a", + ], + 'subdir2' => [ + 'plugin3.yml' => "id: plugin3\ntest_provider: module_a", + ], + 'subdir3' => [ + ] + ], + 'module_b' => [ + 'subdir1' => [ + 'plugin4.yml' => "id: plugin4\ntest_provider: module_b", + ], + ], + ]); + $directories = [ + 'module_a' => [ + vfsStream::url('modules/module_a/subdir1'), + vfsStream::url('modules/module_a/subdir2'), + // Empty directory. + vfsStream::url('modules/module_a/subdir3'), + // Directory does not exist. + vfsStream::url('modules/module_a/subdir4'), + ], + 'module_b' => vfsStream::url('modules/module_b/subdir1'), + ]; + $discovery = new YamlDirectoryDiscovery($directories, 'test'); + + $definitions = $discovery->getDefinitions(); + + $this->assertInternalType('array', $definitions); + $this->assertCount(4, $definitions); + + foreach ($definitions as $id => $definition) { + foreach (array('id', 'provider', ComponentYamlDirectoryDiscovery::FILE_KEY) as $key) { + $this->assertArrayHasKey($key, $definition); + } + $this->assertEquals($id, $definition['id']); + $this->assertEquals($definition['test_provider'], $definition['provider']); + } + } + + /** + * @covers ::getDefinitions + */ + public function testGetDefinitionsWithTranslatableDefinitions() { + vfsStream::setup('modules', NULL, [ + 'module_a' => [ + 'subdir1' => [ + 'plugin1.yml' => "id: plugin1\ntest_provider: module_a\ntitle: 'test title'", + 'plugin2.yml' => "id: plugin2\ntest_provider: module_a\ntitle: 'test title'\ntitle_context: test-context", + ], + ], + ]); + $directories = [ + 'module_a' => vfsStream::url('modules/module_a/subdir1'), + ]; + + $discovery = new YamlDirectoryDiscovery($directories, 'test'); + $discovery->addTranslatableProperty('title', 'title_context'); + $definitions = $discovery->getDefinitions(); + + $this->assertCount(2, $definitions); + $plugin_1 = $definitions['plugin1']; + $plugin_2 = $definitions['plugin2']; + + $this->assertInstanceOf(TranslatableMarkup::class, $plugin_1['title']); + $this->assertEquals([], $plugin_1['title']->getOptions()); + $this->assertInstanceOf(TranslatableMarkup::class, $plugin_2['title']); + $this->assertEquals(['context' => 'test-context'], $plugin_2['title']->getOptions()); + } + +} diff --git a/core/tests/Drupal/Tests/Core/Render/BubbleableMetadataTest.php b/core/tests/Drupal/Tests/Core/Render/BubbleableMetadataTest.php index 34eec8a..874b169 100644 --- a/core/tests/Drupal/Tests/Core/Render/BubbleableMetadataTest.php +++ b/core/tests/Drupal/Tests/Core/Render/BubbleableMetadataTest.php @@ -11,7 +11,6 @@ use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Render\BubbleableMetadata; use Drupal\Tests\UnitTestCase; -use Drupal\Core\Render\Element; use Symfony\Component\DependencyInjection\ContainerBuilder; /** diff --git a/core/tests/Drupal/Tests/Core/Render/PlaceholderGeneratorTest.php b/core/tests/Drupal/Tests/Core/Render/PlaceholderGeneratorTest.php index d2abbc0..98ff8b7 100644 --- a/core/tests/Drupal/Tests/Core/Render/PlaceholderGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Render/PlaceholderGeneratorTest.php @@ -8,7 +8,6 @@ namespace Drupal\Tests\Core\Render; use Drupal\Component\Utility\Html; -use Drupal\Core\Render\Element; /** * @coversDefaultClass \Drupal\Core\Render\PlaceholderGenerator diff --git a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php index 5867b2b..e8d11b7 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php @@ -9,7 +9,6 @@ use Drupal\Core\Cache\MemoryBackend; use Drupal\Core\KeyValueStore\KeyValueMemoryFactory; -use Drupal\Core\Render\Element; use Drupal\Core\State\State; use Drupal\Core\Cache\Cache; diff --git a/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php b/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php index d32ba5a..75722dc 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererPlaceholdersTest.php @@ -8,7 +8,6 @@ namespace Drupal\Tests\Core\Render; use Drupal\Component\Utility\Html; -use Drupal\Core\Render\Element; use Drupal\Core\Cache\Cache; use Drupal\Core\Render\Markup; diff --git a/core/tests/Drupal/Tests/Core/Render/RendererRecursionTest.php b/core/tests/Drupal/Tests/Core/Render/RendererRecursionTest.php index 7e7a129..8b63fb6 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererRecursionTest.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererRecursionTest.php @@ -7,8 +7,6 @@ namespace Drupal\Tests\Core\Render; -use Drupal\Core\Render\Element; - /** * @coversDefaultClass \Drupal\Core\Render\Renderer * @group Render diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTest.php b/core/tests/Drupal/Tests/Core/Render/RendererTest.php index 48b394e..bed7d3d 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererTest.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererTest.php @@ -7,7 +7,7 @@ namespace Drupal\Tests\Core\Render; -use Drupal\Component\Utility\SafeMarkup; +use Drupal\Component\Render\MarkupInterface; use Drupal\Core\Access\AccessResult; use Drupal\Core\Access\AccessResultInterface; use Drupal\Core\Cache\Cache; @@ -47,13 +47,13 @@ public function testRenderBasic($build, $expected, callable $setup_code = NULL) } if (isset($build['#markup'])) { - $this->assertFalse(SafeMarkup::isSafe($build['#markup']), 'The #markup value is not marked safe before rendering.'); + $this->assertNotInstanceOf(MarkupInterface::class, $build['#markup'], 'The #markup value is not marked safe before rendering.'); } $render_output = $this->renderer->renderRoot($build); $this->assertSame($expected, (string) $render_output); if ($render_output !== '') { - $this->assertTrue(SafeMarkup::isSafe($render_output), 'Output of render is marked safe.'); - $this->assertTrue(SafeMarkup::isSafe($build['#markup']), 'The #markup value is marked safe after rendering.'); + $this->assertInstanceOf(MarkupInterface::class, $render_output, 'Output of render is marked safe.'); + $this->assertInstanceOf(MarkupInterface::class, $build['#markup'], 'The #markup value is marked safe after rendering.'); } } @@ -751,7 +751,7 @@ public function testRenderCacheProperties(array $expected_results) { // #custom_property_array can not be a safe_cache_property. $safe_cache_properties = array_diff(Element::properties(array_filter($expected_results)), ['#custom_property_array']); foreach ($safe_cache_properties as $cache_property) { - $this->assertTrue(SafeMarkup::isSafe($data[$cache_property]), "$cache_property is marked as a safe string"); + $this->assertInstanceOf(MarkupInterface::class, $data[$cache_property], "$cache_property is marked as a safe string"); } } diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php index 199d170..a0ea1bb 100644 --- a/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php +++ b/core/tests/Drupal/Tests/Core/Render/RendererTestBase.php @@ -11,7 +11,6 @@ use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Cache\Context\ContextCacheKeys; use Drupal\Core\Cache\MemoryBackend; -use Drupal\Core\Render\Element; use Drupal\Core\Render\PlaceholderGenerator; use Drupal\Core\Render\PlaceholderingRenderCache; use Drupal\Core\Render\Renderer; diff --git a/core/tests/Drupal/Tests/Core/Routing/CurrentRouteMatchTest.php b/core/tests/Drupal/Tests/Core/Routing/CurrentRouteMatchTest.php index b0ad040..e1c8b0f 100644 --- a/core/tests/Drupal/Tests/Core/Routing/CurrentRouteMatchTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/CurrentRouteMatchTest.php @@ -110,4 +110,30 @@ public function testGetRouteMatchFromRequest() { $this->assertEquals($route, $route_match->getRouteObject()); } + /** + * @covers ::resetRouteMatch + */ + public function testResetRouteMatch() { + $route = new Route('/test-route/{foo}'); + $request = new Request(); + $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'test_route'); + $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, $route); + $request_stack = new RequestStack(); + $request_stack->push($request); + + $current_route_match = new CurrentRouteMatch($request_stack); + + $route_name = $current_route_match->getRouteName(); + $this->assertSame('test_route', $route_name); + + // Replace the matched route on the request. + $request->attributes->set(RouteObjectInterface::ROUTE_NAME, NULL); + $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, NULL); + // Reset the route match. + $current_route_match->resetRouteMatch(); + + $route_name = $current_route_match->getRouteName(); + $this->assertNull($route_name); + } + } diff --git a/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php b/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php index b7ec0bc..47738a8 100644 --- a/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php +++ b/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php @@ -217,6 +217,27 @@ public function testAliasGeneration() { } /** + * Confirms that generated routes will have aliased paths using interface constants. + */ + public function testAliasGenerationUsingInterfaceConstants() { + $url = $this->generator->generate('test_1', array(), UrlGenerator::ABSOLUTE_PATH); + $this->assertEquals('/hello/world', $url); + // No cacheability to test; UrlGenerator::generate() doesn't support + // collecting cacheability metadata. + + $this->routeProcessorManager->expects($this->exactly(3)) + ->method('processOutbound') + ->with($this->anything()); + + + // Check that the two generate methods return the same result. + $this->assertGenerateFromRoute('test_1', [], [], $url, (new BubbleableMetadata())->setCacheMaxAge(Cache::PERMANENT)); + + $path = $this->generator->getPathFromRoute('test_1'); + $this->assertEquals('test/one', $path); + } + + /** * @covers ::generateFromRoute */ public function testUrlGenerationWithDisabledPathProcessing() { @@ -376,6 +397,24 @@ public function testAbsoluteURLGeneration() { } /** + * Confirms that absolute URLs work with generated routes using interface constants. + */ + public function testAbsoluteURLGenerationUsingInterfaceConstants() { + $url = $this->generator->generate('test_1', array(), UrlGenerator::ABSOLUTE_URL); + $this->assertEquals('http://localhost/hello/world', $url); + // No cacheability to test; UrlGenerator::generate() doesn't support + // collecting cacheability metadata. + + $this->routeProcessorManager->expects($this->exactly(2)) + ->method('processOutbound') + ->with($this->anything()); + + $options = array('absolute' => TRUE, 'fragment' => 'top'); + // Extra parameters should appear in the query string. + $this->assertGenerateFromRoute('test_1', ['zoo' => 5], $options, 'http://localhost/hello/world?zoo=5#top', (new BubbleableMetadata())->setCacheMaxAge(Cache::PERMANENT)->setCacheContexts(['url.site'])); + } + + /** * Confirms that explicitly setting the base_url works with generated routes */ public function testBaseURLGeneration() { diff --git a/core/tests/Drupal/Tests/Core/StringTranslation/StringTranslationTraitTest.php b/core/tests/Drupal/Tests/Core/StringTranslation/StringTranslationTraitTest.php index c8000bf..7985c8d 100644 --- a/core/tests/Drupal/Tests/Core/StringTranslation/StringTranslationTraitTest.php +++ b/core/tests/Drupal/Tests/Core/StringTranslation/StringTranslationTraitTest.php @@ -7,7 +7,11 @@ namespace Drupal\Tests\Core\StringTranslation; +use Drupal\Core\StringTranslation\PluralTranslatableMarkup; +use Drupal\Core\StringTranslation\TranslatableMarkup; +use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Tests\UnitTestCase; +use Prophecy\Argument; /** * @coversDefaultClass \Drupal\Core\StringTranslation\StringTranslationTrait @@ -35,11 +39,13 @@ class StringTranslationTraitTest extends UnitTestCase { */ protected function setUp() { $this->translation = $this->getObjectForTrait('\Drupal\Core\StringTranslation\StringTranslationTrait'); - $stub = $this->getStringTranslationStub(); - $stub->expects($this->any()) - ->method('formatPlural') - ->will($this->returnArgument(2)); - $this->translation->setStringTranslation($stub); + $mock = $this->prophesize(TranslationInterface::class); + $mock->translate(Argument::cetera())->shouldNotBeCalled(); + $mock->formatPlural(Argument::cetera())->shouldNotBeCalled(); + $mock->translateString(Argument::cetera())->will(function ($args) { + return $args[0]->getUntranslatedString(); + }); + $this->translation->setStringTranslation($mock->reveal()); $this->reflection = new \ReflectionClass(get_class($this->translation)); } @@ -50,7 +56,9 @@ public function testT() { $method = $this->reflection->getMethod('t'); $method->setAccessible(TRUE); - $this->assertEquals('something', $method->invoke($this->translation, 'something')); + $result = $method->invoke($this->translation, 'something'); + $this->assertInstanceOf(TranslatableMarkup::class, $result); + $this->assertEquals('something', $result); } /** @@ -60,7 +68,9 @@ public function testFormatPlural() { $method = $this->reflection->getMethod('formatPlural'); $method->setAccessible(TRUE); - $this->assertEquals('apples', $method->invoke($this->translation, 2, 'apple', 'apples')); + $result = $method->invoke($this->translation, 2, 'apple', 'apples'); + $this->assertInstanceOf(PluralTranslatableMarkup::class, $result); + $this->assertEquals('apples', $result); } } diff --git a/core/tests/Drupal/Tests/Core/StringTranslation/TranslationManagerTest.php b/core/tests/Drupal/Tests/Core/StringTranslation/TranslationManagerTest.php index 1a1b390..dfaab79 100644 --- a/core/tests/Drupal/Tests/Core/StringTranslation/TranslationManagerTest.php +++ b/core/tests/Drupal/Tests/Core/StringTranslation/TranslationManagerTest.php @@ -7,7 +7,6 @@ namespace Drupal\Tests\Core\StringTranslation; -use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Render\MarkupInterface; use Drupal\Core\StringTranslation\TranslationManager; use Drupal\Tests\UnitTestCase; @@ -64,7 +63,7 @@ public function testFormatPlural($count, $singular, $plural, array $args = array $this->translationManager->addTranslator($translator); $result = $this->translationManager->formatPlural($count, $singular, $plural, $args, $options); $this->assertEquals($expected, $result); - $this->assertTrue(SafeMarkup::isSafe($result)); + $this->assertInstanceOf(MarkupInterface::class, $result); } /** diff --git a/core/tests/Drupal/Tests/Core/Template/TwigSandboxTest.php b/core/tests/Drupal/Tests/Core/Template/TwigSandboxTest.php index d403db3..7a3dd3a 100644 --- a/core/tests/Drupal/Tests/Core/Template/TwigSandboxTest.php +++ b/core/tests/Drupal/Tests/Core/Template/TwigSandboxTest.php @@ -140,6 +140,20 @@ public function testEntitySafeMethods() { $this->assertEquals($result, 'testing', 'Sandbox policy allows get() to be called.'); } + /** + * Tests that safe methods inside Url objects can be called. + */ + public function testUrlSafeMethods() { + $url = $this->getMockBuilder('Drupal\Core\Url') + ->disableOriginalConstructor() + ->getMock(); + $url->expects($this->once()) + ->method('toString') + ->willReturn('http://kittens.cat/are/cute'); + $result = $this->twig->render('{{ url.toString }}', ['url' => $url]); + $this->assertEquals($result, 'http://kittens.cat/are/cute', 'Sandbox policy allows toString() to be called.'); + } + } class TestAttribute extends Attribute {} diff --git a/core/tests/Drupal/Tests/Core/Transliteration/PhpTransliterationTest.php b/core/tests/Drupal/Tests/Core/Transliteration/PhpTransliterationTest.php index 6788011..9dc8388 100644 --- a/core/tests/Drupal/Tests/Core/Transliteration/PhpTransliterationTest.php +++ b/core/tests/Drupal/Tests/Core/Transliteration/PhpTransliterationTest.php @@ -50,7 +50,7 @@ public function testPhpTransliterationWithAlter($langcode, $original, $expected, // The default transliteration of Ä is A, but change it to Z for testing. $overrides[0xC4] = 'Z'; // Also provide transliterations of two 5-byte characters from - // http://en.wikipedia.org/wiki/Gothic_alphabet. + // http://wikipedia.org/wiki/Gothic_alphabet. $overrides[0x10330] = 'A'; $overrides[0x10338] = 'Th'; } @@ -73,7 +73,7 @@ public function providerTestPhpTransliterationWithAlter() { // Note that the 3-byte character is overridden by the 'kg' language. $two_byte = 'Ä Ö Ü Å Ø äöüåøhello'; // These are two Gothic alphabet letters. See - // http://en.wikipedia.org/wiki/Gothic_alphabet + // http://wikipedia.org/wiki/Gothic_alphabet // They are not in our tables, but should at least give us '?' (unknown). $five_byte = html_entity_decode('𐌰𐌸', ENT_NOQUOTES, 'UTF-8'); // Five-byte characters do not work in MySQL, so make a printable version. diff --git a/core/tests/Drupal/Tests/Listeners/DrupalStandardsListener.php b/core/tests/Drupal/Tests/Listeners/DrupalStandardsListener.php index 4bcc2e1..325fadf 100644 --- a/core/tests/Drupal/Tests/Listeners/DrupalStandardsListener.php +++ b/core/tests/Drupal/Tests/Listeners/DrupalStandardsListener.php @@ -3,15 +3,14 @@ /** * @file * Contains \Drupal\Tests\Listeners\DrupalStandardsListener. - * - * Listener for PHPUnit tests, to enforce various coding standards within test - * runs. */ namespace Drupal\Tests\Listeners; /** * Listens for PHPUnit tests and fails those with invalid coverage annotations. + * + * Enforces various coding standards within test runs. */ class DrupalStandardsListener extends \PHPUnit_Framework_BaseTestListener { diff --git a/core/themes/bartik/color/preview.js b/core/themes/bartik/color/preview.js index cee0e0e..da8e0ca 100644 --- a/core/themes/bartik/color/preview.js +++ b/core/themes/bartik/color/preview.js @@ -8,7 +8,7 @@ Drupal.color = { logoChanged: false, - callback: function (context, settings, form, farb, height, width) { + callback: function (context, settings, $form) { // Change the logo to be the real one. if (!this.logoChanged) { $('.color-preview .color-preview-logo img').attr('src', drupalSettings.color.logo); @@ -19,27 +19,31 @@ $('div').remove('.color-preview-logo'); } + var $colorPreview = $form.find('.color-preview'); + var $colorPalette = $form.find('.js-color-palette'); + // Solid background. - form.find('.color-preview').css('backgroundColor', $('.js-color-palette input[name="palette[bg]"]').val()); + $colorPreview.css('backgroundColor', $colorPalette.find('input[name="palette[bg]"]').val()); // Text preview. - form.find('.color-preview .color-preview-main h2, .color-preview .preview-content').css('color', form.find('.js-color-palette input[name="palette[text]"]').val()); - form.find('.color-preview .color-preview-content a').css('color', form.find('.js-color-palette input[name="palette[link]"]').val()); + $colorPreview.find('.color-preview-main h2, .color-preview .preview-content').css('color', $colorPalette.find('input[name="palette[text]"]').val()); + $colorPreview.find('.color-preview-content a').css('color', $colorPalette.find('input[name="palette[link]"]').val()); // Sidebar block. - form.find('.color-preview .color-preview-sidebar .color-preview-block').css('background-color', form.find('.js-color-palette input[name="palette[sidebar]"]').val()); - form.find('.color-preview .color-preview-sidebar .color-preview-block').css('border-color', form.find('.js-color-palette input[name="palette[sidebarborders]"]').val()); + var $colorPreviewBlock = $colorPreview.find('.color-preview-sidebar .color-preview-block'); + $colorPreviewBlock.css('background-color', $colorPalette.find('input[name="palette[sidebar]"]').val()); + $colorPreviewBlock.css('border-color', $colorPalette.find('input[name="palette[sidebarborders]"]').val()); // Footer wrapper background. - form.find('.color-preview .color-preview-footer-wrapper', form).css('background-color', form.find('.js-color-palette input[name="palette[footer]"]').val()); + $colorPreview.find('.color-preview-footer-wrapper').css('background-color', $colorPalette.find('input[name="palette[footer]"]').val()); // CSS3 Gradients. - var gradient_start = form.find('.js-color-palette input[name="palette[top]"]').val(); - var gradient_end = form.find('.js-color-palette input[name="palette[bottom]"]').val(); + var gradient_start = $colorPalette.find('input[name="palette[top]"]').val(); + var gradient_end = $colorPalette.find('input[name="palette[bottom]"]').val(); - form.find('.color-preview .color-preview-header').attr('style', 'background-color: ' + gradient_start + '; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(' + gradient_start + '), to(' + gradient_end + ')); background-image: -moz-linear-gradient(-90deg, ' + gradient_start + ', ' + gradient_end + ');'); + $colorPreview.find('.color-preview-header').attr('style', 'background-color: ' + gradient_start + '; background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(' + gradient_start + '), to(' + gradient_end + ')); background-image: -moz-linear-gradient(-90deg, ' + gradient_start + ', ' + gradient_end + ');'); - form.find('.color-preview .color-preview-site-name').css('color', form.find('.js-color-palette input[name="palette[titleslogan]"]').val()); + $colorPreview.find('.color-preview-site-name').css('color', $colorPalette.find('input[name="palette[titleslogan]"]').val()); } }; })(jQuery, Drupal, drupalSettings); diff --git a/core/themes/bartik/css/components/dropbutton.component.css b/core/themes/bartik/css/components/dropbutton.component.css index db55965..b5113f5 100644 --- a/core/themes/bartik/css/components/dropbutton.component.css +++ b/core/themes/bartik/css/components/dropbutton.component.css @@ -3,10 +3,6 @@ * Visual styles for Bartik's dropbutton component. */ -.js .dropbutton-wrapper .dropbutton-widget { - /* This is required to win over specifity of .js td .dropbutton-widget */ - position: relative; -} .js .dropbutton-widget { border: 1px solid; border-color: #e4e4e4 #d2d2d2 #b4b4b4 #d2d2d2; diff --git a/core/themes/bartik/templates/comment.html.twig b/core/themes/bartik/templates/comment.html.twig index 8927db4..16cd5ba 100644 --- a/core/themes/bartik/templates/comment.html.twig +++ b/core/themes/bartik/templates/comment.html.twig @@ -64,9 +64,6 @@ * @see template_preprocess_comment() */ #} -{% if threaded %} - {{ attach_library('classy/drupal.comment.threaded') }} -{% endif %} {% set classes = [ 'comment', diff --git a/core/themes/bartik/templates/page.html.twig b/core/themes/bartik/templates/page.html.twig index 6fefe94..023204f 100644 --- a/core/themes/bartik/templates/page.html.twig +++ b/core/themes/bartik/templates/page.html.twig @@ -52,7 +52,7 @@ #}
                                  -
                                + {% endif %} + {% else %} +

                                {{ empty }}

                                + {% endif %} +
                              diff --git a/core/themes/classy/templates/navigation/links.html.twig b/core/themes/classy/templates/navigation/links.html.twig index f82f41a..c1bff6b 100644 --- a/core/themes/classy/templates/navigation/links.html.twig +++ b/core/themes/classy/templates/navigation/links.html.twig @@ -13,7 +13,6 @@ * to l() as its $options parameter. * - attributes: (optional) HTML attributes for the anchor, or for the * tag if no 'href' is supplied. - * - link_key: The link CSS class. * - heading: (optional) A heading to precede the links. * - text: The heading text. * - level: The heading level (e.g. 'h2', 'h3'). @@ -41,8 +40,8 @@ {%- endif -%} {%- endif -%} - {%- for key, item in links -%} - + {%- for item in links -%} + {%- if item.link -%} {{ item.link }} {%- elseif item.text_attributes -%} diff --git a/core/themes/seven/css/base/elements.css b/core/themes/seven/css/base/elements.css index 76fcc4c..6644cea 100644 --- a/core/themes/seven/css/base/elements.css +++ b/core/themes/seven/css/base/elements.css @@ -166,9 +166,9 @@ details summary { padding-bottom: 0.5em; } details summary:focus { - border-top: 3px solid #0074bd; outline: none; - color: #0074bd; - margin-top: -3px; +} +details summary:focus, +details summary:hover { text-decoration: underline; } diff --git a/core/themes/seven/css/components/form.css b/core/themes/seven/css/components/form.css index f74eb74..29777e4 100644 --- a/core/themes/seven/css/components/form.css +++ b/core/themes/seven/css/components/form.css @@ -91,7 +91,6 @@ label[for] { background-color: #fcf4f2; } .form-required:after { - background-image: url(../../images/required.svg); background-size: 7px 7px; width: 7px; height: 7px; @@ -199,6 +198,7 @@ textarea.form-textarea { width: auto; } +.form-item-options-group-info-identifier, .form-item-pass .description { clear: both; } diff --git a/core/themes/seven/css/components/user.icons.admin.css b/core/themes/seven/css/components/user.icons.admin.css new file mode 100644 index 0000000..66c2366 --- /dev/null +++ b/core/themes/seven/css/components/user.icons.admin.css @@ -0,0 +1,15 @@ +/** + * @file + * Styling for the user module icons. + */ + +/** + * Toolbar tab icon. + */ +.toolbar-bar .toolbar-icon-user:before { + background-image: url(../../../misc/icons/bebebe/person.svg); +} +.toolbar-bar .toolbar-icon-user:active:before, +.toolbar-bar .toolbar-icon-user.is-active:before { + background-image: url(../../../misc/icons/ffffff/person.svg); +} diff --git a/core/themes/seven/css/components/vertical-tabs.css b/core/themes/seven/css/components/vertical-tabs.css index e3bd0eb..34b5a52 100644 --- a/core/themes/seven/css/components/vertical-tabs.css +++ b/core/themes/seven/css/components/vertical-tabs.css @@ -45,9 +45,6 @@ .vertical-tabs__menu-item:active { z-index: 2; } -.vertical-tabs__menu-item.is-selected:focus { - outline: none; -} .vertical-tabs__menu-item a { display: block; padding: 10px 15px 15px; @@ -64,19 +61,24 @@ .vertical-tabs__menu-item a:focus { background: #fcfcfa; text-shadow: none; +} +.vertical-tabs__menu-item a:focus { + outline: none; +} +.vertical-tabs__menu-item a:focus .vertical-tabs__menu-item-title { + text-decoration: underline; +} +.vertical-tabs__menu-item a:active .vertical-tabs__menu-item-title, +.vertical-tabs__menu-item.is-selected a:focus .vertical-tabs__menu-item-title { text-decoration: none; } .vertical-tabs__menu-item.is-selected a { color: #004f80; - border-left: 4px solid #0074bd; /* LTR */ padding-left: 11px; /* LTR */ border-bottom: none; - outline: none; text-decoration: none; } [dir=rtl] .vertical-tabs__menu-item.is-selected a { - border-left: 0; - border-right: 4px solid #0074bd; padding-left: 15px; padding-right: 11px; } diff --git a/core/themes/seven/css/components/views-ui.css b/core/themes/seven/css/components/views-ui.css index bfd4575..6849781 100644 --- a/core/themes/seven/css/components/views-ui.css +++ b/core/themes/seven/css/components/views-ui.css @@ -236,9 +236,14 @@ details.fieldset-no-legend { /* @group Rearrange filter criteria */ .views-ui-rearrange-filter-form .action-links { - margin: 0; + float: left; + margin: 0 0 1em; padding: 0; } +.views-ui-rearrange-filter-form .tabledrag-toggle-weight-wrapper { + float: right; + margin-bottom: 1em; +} .views-ui-rearrange-filter-form table { border: medium none; diff --git a/core/themes/seven/images/add.png b/core/themes/seven/images/add.png deleted file mode 100644 index 1a2faf6..0000000 --- a/core/themes/seven/images/add.png +++ /dev/null @@ -1,4 +0,0 @@ -PNG - - IHDR gIDATxڅOA ӽYژicJ%B@9[[*:'`H*Ho\)󗭹x- -u^bZJ IENDB` \ No newline at end of file diff --git a/core/themes/seven/images/arrow-asc-active.png b/core/themes/seven/images/arrow-asc-active.png deleted file mode 100644 index 7536eee..0000000 --- a/core/themes/seven/images/arrow-asc-active.png +++ /dev/null @@ -1,3 +0,0 @@ -PNG - - IHDR 6YtEXtSoftwareAdobe ImageReadyqe<PLTEXtRNS0JIDATxb`F8a2X`! ^Cbl!IENDB` \ No newline at end of file diff --git a/core/themes/seven/images/arrow-asc.png b/core/themes/seven/images/arrow-asc.png deleted file mode 100644 index 56d2728..0000000 --- a/core/themes/seven/images/arrow-asc.png +++ /dev/null @@ -1,3 +0,0 @@ -PNG - - IHDR 6YtEXtSoftwareAdobe ImageReadyqe<PLTEHutRNS0JIDATxb`F8a2X`! ^Cbl!IENDB` \ No newline at end of file diff --git a/core/themes/seven/images/arrow-desc-active.png b/core/themes/seven/images/arrow-desc-active.png deleted file mode 100644 index f1bf910..0000000 --- a/core/themes/seven/images/arrow-desc-active.png +++ /dev/null @@ -1,3 +0,0 @@ -PNG - - IHDR 6YtEXtSoftwareAdobe ImageReadyqe<PLTEXtRNS0J IDATxb`a10 BIENDB` \ No newline at end of file diff --git a/core/themes/seven/images/arrow-desc.png b/core/themes/seven/images/arrow-desc.png deleted file mode 100644 index eaf80c5..0000000 --- a/core/themes/seven/images/arrow-desc.png +++ /dev/null @@ -1,3 +0,0 @@ -PNG - - IHDR 6YtEXtSoftwareAdobe ImageReadyqe<PLTEHutRNS0J IDATxb`a10 BIENDB` \ No newline at end of file diff --git a/core/themes/seven/images/arrow-next.png b/core/themes/seven/images/arrow-next.png deleted file mode 100644 index ed551de..0000000 --- a/core/themes/seven/images/arrow-next.png +++ /dev/null @@ -1,3 +0,0 @@ -PNG - - IHDR/e=IDAT[U[ Zub ˶);]6BF  v &g #gKIENDB` \ No newline at end of file diff --git a/core/themes/seven/images/arrow-prev.png b/core/themes/seven/images/arrow-prev.png deleted file mode 100644 index 64b2dac..0000000 --- a/core/themes/seven/images/arrow-prev.png +++ /dev/null @@ -1,3 +0,0 @@ -PNG - - IHDR/e:IDAT+\d&SvBh}KxS+s +?e{IENDB` \ No newline at end of file diff --git a/core/themes/seven/images/required.svg b/core/themes/seven/images/required.svg deleted file mode 100644 index 584c13c..0000000 --- a/core/themes/seven/images/required.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/core/themes/seven/images/task-check.png b/core/themes/seven/images/task-check.png deleted file mode 100644 index 64fadf8..0000000 --- a/core/themes/seven/images/task-check.png +++ /dev/null @@ -1,4 +0,0 @@ -PNG - - IHDR e^|lPLTE6|ٴ:<nyH(|e߿ݼ7ԩdJX<֮A F%戸s`DC#լnUk~hʨTIDAT@A޻hVK + {% for bundle in bundles %} +
                            • {{ bundle.label }}
                              {{ bundle.description }}
                            • + {% endfor %} +
                            +{% elseif add_bundle_message is not empty %} +

                            + {{ add_bundle_message }} +

                            +{% endif %} diff --git a/core/themes/seven/templates/install-page.html.twig b/core/themes/seven/templates/install-page.html.twig index 2ada24d..f1335a8 100644 --- a/core/themes/seven/templates/install-page.html.twig +++ b/core/themes/seven/templates/install-page.html.twig @@ -30,7 +30,7 @@
                            {% if title %} -

                            {{ title }}

                            +

                            {{ title }}

                            {% endif %} {{ page.highlighted }} {{ page.content }} diff --git a/core/themes/stable/css/ckeditor/plugins/language/ckeditor.language.css b/core/themes/stable/css/ckeditor/plugins/language/ckeditor.language.css new file mode 100644 index 0000000..feea125 --- /dev/null +++ b/core/themes/stable/css/ckeditor/plugins/language/ckeditor.language.css @@ -0,0 +1,19 @@ +/** + * @file + * Language: add styling for elements that have a language attribute. + */ + +/** + * Show the user that a 'lang' tag has been applied by adding a thin dotted + * border. We also append the value of the tag between brackets, for example: + * '(en)'. Since the html element has a 'lang' attribute too we only target + * elements within the html scope. + */ +html [lang] { + outline: 1px dotted gray; +} +html [lang]:after { + content: " ("attr(lang)")"; + font-size: 10px; + color: #666; +} diff --git a/core/themes/stable/stable.info.yml b/core/themes/stable/stable.info.yml index e1981fe..3476833 100644 --- a/core/themes/stable/stable.info.yml +++ b/core/themes/stable/stable.info.yml @@ -21,6 +21,10 @@ libraries-override: css: component: css/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css: css/ckeditor/plugins/drupalimagecaption/ckeditor.drupalimagecaption.css + ckeditor/drupal.ckeditor.plugins.language: + css: + component: + css/plugins/language/ckeditor.language.css: css/ckeditor/plugins/language/ckeditor.language.css ckeditor/drupal.ckeditor.admin: css: theme: diff --git a/core/themes/stable/stable.theme b/core/themes/stable/stable.theme new file mode 100644 index 0000000..456a4ad --- /dev/null +++ b/core/themes/stable/stable.theme @@ -0,0 +1,25 @@ + $value) { + if (!is_numeric($key)) { + $class = Html::getClass($key); + $variables['links'][$key]['attributes']->addClass($class); + } + } + } +} diff --git a/core/themes/stable/templates/admin/block-list.html.twig b/core/themes/stable/templates/admin/block-list.html.twig deleted file mode 100644 index 0da7e94..0000000 --- a/core/themes/stable/templates/admin/block-list.html.twig +++ /dev/null @@ -1,21 +0,0 @@ -{# -/** - * @file - * Two column template for the block add/edit form. - * - * This template will be used when a block edit form specifies 'block_edit_form' - * as its #theme callback. Otherwise, by default, block add/edit forms will be - * themed by form.html.twig. - * - * Available variables: - * - form: The block add/edit form. - */ -#} -
                            -
                            - {{ form|without('place_blocks') }} -
                            -
                            - {{ form.place_blocks }} -
                            -
                            diff --git a/core/themes/stable/templates/admin/help-section.html.twig b/core/themes/stable/templates/admin/help-section.html.twig new file mode 100644 index 0000000..4b0d7bd --- /dev/null +++ b/core/themes/stable/templates/admin/help-section.html.twig @@ -0,0 +1,23 @@ +{# +/** + * @file + * Theme override for a section of the help page. + * + * Available variables: + * - title: The section title. + * - description: The description text for the section. + * - links: Links to display in the section. + * - empty: Text to display if there are no links. + */ +#} +

                            {{ title }}

                            +

                            {{ description }}

                            +{% if links %} +
                              + {% for link in links %} +
                            • {{ link }}
                            • + {% endfor %} +
                            +{% else %} +

                            {{ empty }}

                            +{% endif %} diff --git a/core/themes/stable/templates/content-edit/entity-add-list.html.twig b/core/themes/stable/templates/content-edit/entity-add-list.html.twig new file mode 100644 index 0000000..8d73786 --- /dev/null +++ b/core/themes/stable/templates/content-edit/entity-add-list.html.twig @@ -0,0 +1,30 @@ +{# +/** + * @file + * Default theme implementation to present a list of available bundles. + * + * Available variables: + * - bundles: A list of bundles, each with the following properties: + * - label: Bundle label. + * - description: Bundle description. + * - add_link: Link to create an entity of this bundle. + * - add_bundle_message: The message shown when there are no bundles. Only + * available if the entity type uses bundle entities. + * + * @see template_preprocess_entity_add_list() + * + * @ingroup themeable + */ +#} +{% if bundles is not empty %} +
                            + {% for bundle in bundles %} +
                            {{ bundle.add_link }}
                            +
                            {{ bundle.description }}
                            + {% endfor %} +
                            +{% elseif add_bundle_message is not empty %} +

                            + {{ add_bundle_message }} +

                            +{% endif %} diff --git a/core/themes/stable/templates/content/taxonomy-term.html.twig b/core/themes/stable/templates/content/taxonomy-term.html.twig index 09c242b..78203d9 100644 --- a/core/themes/stable/templates/content/taxonomy-term.html.twig +++ b/core/themes/stable/templates/content/taxonomy-term.html.twig @@ -23,7 +23,7 @@ * @see template_preprocess_taxonomy_term() */ #} - + {{ title_prefix }} {% if not page %}

                            {{ name }}

                            diff --git a/core/themes/stable/templates/navigation/links.html.twig b/core/themes/stable/templates/navigation/links.html.twig index f82f41a..c1bff6b 100644 --- a/core/themes/stable/templates/navigation/links.html.twig +++ b/core/themes/stable/templates/navigation/links.html.twig @@ -13,7 +13,6 @@ * to l() as its $options parameter. * - attributes: (optional) HTML attributes for the anchor, or for the * tag if no 'href' is supplied. - * - link_key: The link CSS class. * - heading: (optional) A heading to precede the links. * - text: The heading text. * - level: The heading level (e.g. 'h2', 'h3'). @@ -41,8 +40,8 @@ {%- endif -%} {%- endif -%} - {%- for key, item in links -%} - + {%- for item in links -%} + {%- if item.link -%} {{ item.link }} {%- elseif item.text_attributes -%} diff --git a/example.gitignore b/example.gitignore index e2f0124..d1e35ac 100644 --- a/example.gitignore +++ b/example.gitignore @@ -7,9 +7,12 @@ # Because .gitignore can be specific to your site, this file has a different # name; updating Drupal core will not override your custom .gitignore file. -# Ignore core and vendor when managing dependencies with Composer. +# Ignore core when managing all of a project's dependencies with Composer +# including Drupal core. # core -# vendor + +# Core's dependencies are managed with Composer. +vendor # Ignore configuration files that may contain sensitive information. sites/*/settings*.php diff --git a/sites/example.settings.local.php b/sites/example.settings.local.php index dfcc244..c3347ad 100644 --- a/sites/example.settings.local.php +++ b/sites/example.settings.local.php @@ -93,3 +93,15 @@ * using these parameters in a request to rebuild.php. */ $settings['rebuild_access'] = TRUE; + +/** + * Skip file system permissions hardening. + * + * The system module will periodically check the permissions of your site's + * site directory to ensure that it is not writable by the website user. For + * sites that are managed with a version control system, this can cause problems + * when files in that directory such as settings.php are updated, because the + * user pulling in the changes won't have permissions to modify files in the + * directory. + */ +$settings['skip_permissions_hardening'] = TRUE; diff --git a/vendor/.htaccess b/vendor/.htaccess deleted file mode 100644 index 90748bb..0000000 --- a/vendor/.htaccess +++ /dev/null @@ -1,23 +0,0 @@ -# Deny all requests from Apache 2.4+. - - Require all denied - - -# Deny all requests from Apache 2.0-2.2. - - Deny from all - -# Turn off all options we don't need. -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 - - # Override the handler again if we're run later in the evaluation list. - SetHandler Drupal_Security_Do_Not_Remove_See_SA_2013_003 - - -# If we know how to do it safely, disable the PHP engine entirely. - - php_flag engine off - diff --git a/vendor/autoload.php b/vendor/autoload.php deleted file mode 100644 index 5111b1b..0000000 --- a/vendor/autoload.php +++ /dev/null @@ -1,7 +0,0 @@ - - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/behat/mink-browserkit-driver/README.md b/vendor/behat/mink-browserkit-driver/README.md deleted file mode 100644 index 1903a5f..0000000 --- a/vendor/behat/mink-browserkit-driver/README.md +++ /dev/null @@ -1,54 +0,0 @@ -Mink BrowserKit Driver -====================== - -[![Latest Stable Version](https://poser.pugx.org/behat/mink-browserkit-driver/v/stable.png)](https://packagist.org/packages/behat/mink-browserkit-driver) -[![Latest Unstable Version](https://poser.pugx.org/behat/mink-browserkit-driver/v/unstable.svg)](https://packagist.org/packages/behat/mink-browserkit-driver) -[![Total Downloads](https://poser.pugx.org/behat/mink-browserkit-driver/downloads.png)](https://packagist.org/packages/behat/mink-browserkit-driver) -[![Build Status](https://travis-ci.org/minkphp/MinkBrowserKitDriver.svg?branch=master)](https://travis-ci.org/minkphp/MinkBrowserKitDriver) -[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/minkphp/MinkBrowserKitDriver/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/minkphp/MinkBrowserKitDriver/) -[![Code Coverage](https://scrutinizer-ci.com/g/minkphp/MinkBrowserKitDriver/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/minkphp/MinkBrowserKitDriver/) -[![License](https://poser.pugx.org/behat/mink-browserkit-driver/license.svg)](https://packagist.org/packages/behat/mink-browserkit-driver) - -Usage Example -------------- - -``` php - new Session(new BrowserKitDriver(new Client($app))), -)); - -$mink->getSession('silex')->getPage()->findLink('Chat')->click(); -``` - -Installation ------------- - -``` json -{ - "require": { - "behat/mink": "~1.5", - "behat/mink-browserkit-driver": "~1.1" - } -} -``` - -``` bash -$> curl -sS https://getcomposer.org/installer | php -$> php composer.phar install -``` - -Maintainers ------------ - -* Christophe Coevoet [stof](https://github.com/stof) -* Other [awesome developers](https://github.com/minkphp/MinkBrowserKitDriver/graphs/contributors) diff --git a/vendor/behat/mink-browserkit-driver/composer.json b/vendor/behat/mink-browserkit-driver/composer.json deleted file mode 100644 index 7d12bbc..0000000 --- a/vendor/behat/mink-browserkit-driver/composer.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "behat/mink-browserkit-driver", - "description": "Symfony2 BrowserKit driver for Mink framework", - "keywords": ["Symfony2", "Mink", "testing", "browser"], - "homepage": "http://mink.behat.org/", - "type": "mink-driver", - "license": "MIT", - - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - - "require": { - "php": ">=5.3.6", - "behat/mink": "~1.7@dev", - "symfony/browser-kit": "~2.3", - "symfony/dom-crawler": "~2.3" - }, - - "require-dev": { - "symfony/phpunit-bridge": "~2.7", - "silex/silex": "~1.2" - }, - - "autoload": { - "psr-4": { - "Behat\\Mink\\Driver\\": "src/" - } - }, - - "autoload-dev": { - "psr-4": { - "Behat\\Mink\\Tests\\Driver\\": "tests" - } - }, - - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - } -} diff --git a/vendor/behat/mink-browserkit-driver/phpunit.xml.dist b/vendor/behat/mink-browserkit-driver/phpunit.xml.dist deleted file mode 100644 index d91581c..0000000 --- a/vendor/behat/mink-browserkit-driver/phpunit.xml.dist +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - tests - vendor/behat/mink/driver-testsuite/tests/Basic - vendor/behat/mink/driver-testsuite/tests/Form - - - - - - ./src - - - diff --git a/vendor/behat/mink-browserkit-driver/src/BrowserKitDriver.php b/vendor/behat/mink-browserkit-driver/src/BrowserKitDriver.php deleted file mode 100644 index 70682e7..0000000 --- a/vendor/behat/mink-browserkit-driver/src/BrowserKitDriver.php +++ /dev/null @@ -1,855 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Behat\Mink\Driver; - -use Behat\Mink\Exception\DriverException; -use Behat\Mink\Exception\UnsupportedDriverActionException; -use Symfony\Component\BrowserKit\Client; -use Symfony\Component\BrowserKit\Cookie; -use Symfony\Component\BrowserKit\Response; -use Symfony\Component\DomCrawler\Crawler; -use Symfony\Component\DomCrawler\Field\ChoiceFormField; -use Symfony\Component\DomCrawler\Field\FileFormField; -use Symfony\Component\DomCrawler\Field\FormField; -use Symfony\Component\DomCrawler\Field\InputFormField; -use Symfony\Component\DomCrawler\Field\TextareaFormField; -use Symfony\Component\DomCrawler\Form; -use Symfony\Component\HttpKernel\Client as HttpKernelClient; - -/** - * Symfony2 BrowserKit driver. - * - * @author Konstantin Kudryashov - */ -class BrowserKitDriver extends CoreDriver -{ - private $client; - - /** - * @var Form[] - */ - private $forms = array(); - private $serverParameters = array(); - private $started = false; - private $removeScriptFromUrl = false; - private $removeHostFromUrl = false; - - /** - * Initializes BrowserKit driver. - * - * @param Client $client BrowserKit client instance - * @param string|null $baseUrl Base URL for HttpKernel clients - */ - public function __construct(Client $client, $baseUrl = null) - { - $this->client = $client; - $this->client->followRedirects(true); - - if ($baseUrl !== null && $client instanceof HttpKernelClient) { - $client->setServerParameter('SCRIPT_FILENAME', parse_url($baseUrl, PHP_URL_PATH)); - } - } - - /** - * Returns BrowserKit HTTP client instance. - * - * @return Client - */ - public function getClient() - { - return $this->client; - } - - /** - * Tells driver to remove hostname from URL. - * - * @param Boolean $remove - * - * @deprecated Deprecated as of 1.2, to be removed in 2.0. Pass the base url in the constructor instead. - */ - public function setRemoveHostFromUrl($remove = true) - { - trigger_error( - 'setRemoveHostFromUrl() is deprecated as of 1.2 and will be removed in 2.0. Pass the base url in the constructor instead.', - E_USER_DEPRECATED - ); - $this->removeHostFromUrl = (bool) $remove; - } - - /** - * Tells driver to remove script name from URL. - * - * @param Boolean $remove - * - * @deprecated Deprecated as of 1.2, to be removed in 2.0. Pass the base url in the constructor instead. - */ - public function setRemoveScriptFromUrl($remove = true) - { - trigger_error( - 'setRemoveScriptFromUrl() is deprecated as of 1.2 and will be removed in 2.0. Pass the base url in the constructor instead.', - E_USER_DEPRECATED - ); - $this->removeScriptFromUrl = (bool) $remove; - } - - /** - * {@inheritdoc} - */ - public function start() - { - $this->started = true; - } - - /** - * {@inheritdoc} - */ - public function isStarted() - { - return $this->started; - } - - /** - * {@inheritdoc} - */ - public function stop() - { - $this->reset(); - $this->started = false; - } - - /** - * {@inheritdoc} - */ - public function reset() - { - // Restarting the client resets the cookies and the history - $this->client->restart(); - $this->forms = array(); - $this->serverParameters = array(); - } - - /** - * {@inheritdoc} - */ - public function visit($url) - { - $this->client->request('GET', $this->prepareUrl($url), array(), array(), $this->serverParameters); - $this->forms = array(); - } - - /** - * {@inheritdoc} - */ - public function getCurrentUrl() - { - $request = $this->client->getInternalRequest(); - - if ($request === null) { - throw new DriverException('Unable to access the request before visiting a page'); - } - - return $request->getUri(); - } - - /** - * {@inheritdoc} - */ - public function reload() - { - $this->client->reload(); - $this->forms = array(); - } - - /** - * {@inheritdoc} - */ - public function forward() - { - $this->client->forward(); - $this->forms = array(); - } - - /** - * {@inheritdoc} - */ - public function back() - { - $this->client->back(); - $this->forms = array(); - } - - /** - * {@inheritdoc} - */ - public function setBasicAuth($user, $password) - { - if (false === $user) { - unset($this->serverParameters['PHP_AUTH_USER'], $this->serverParameters['PHP_AUTH_PW']); - - return; - } - - $this->serverParameters['PHP_AUTH_USER'] = $user; - $this->serverParameters['PHP_AUTH_PW'] = $password; - } - - /** - * {@inheritdoc} - */ - public function setRequestHeader($name, $value) - { - $contentHeaders = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true); - $name = str_replace('-', '_', strtoupper($name)); - - // CONTENT_* are not prefixed with HTTP_ in PHP when building $_SERVER - if (!isset($contentHeaders[$name])) { - $name = 'HTTP_' . $name; - } - - $this->serverParameters[$name] = $value; - } - - /** - * {@inheritdoc} - */ - public function getResponseHeaders() - { - return $this->getResponse()->getHeaders(); - } - - /** - * {@inheritdoc} - */ - public function setCookie($name, $value = null) - { - if (null === $value) { - $this->deleteCookie($name); - - return; - } - - $jar = $this->client->getCookieJar(); - $jar->set(new Cookie($name, $value)); - } - - /** - * Deletes a cookie by name. - * - * @param string $name Cookie name. - */ - private function deleteCookie($name) - { - $path = $this->getCookiePath(); - $jar = $this->client->getCookieJar(); - - do { - if (null !== $jar->get($name, $path)) { - $jar->expire($name, $path); - } - - $path = preg_replace('/.$/', '', $path); - } while ($path); - } - - /** - * Returns current cookie path. - * - * @return string - */ - private function getCookiePath() - { - $path = dirname(parse_url($this->getCurrentUrl(), PHP_URL_PATH)); - - if ('\\' === DIRECTORY_SEPARATOR) { - $path = str_replace('\\', '/', $path); - } - - return $path; - } - - /** - * {@inheritdoc} - */ - public function getCookie($name) - { - // Note that the following doesn't work well because - // Symfony\Component\BrowserKit\CookieJar stores cookies by name, - // path, AND domain and if you don't fill them all in correctly then - // you won't get the value that you're expecting. - // - // $jar = $this->client->getCookieJar(); - // - // if (null !== $cookie = $jar->get($name)) { - // return $cookie->getValue(); - // } - - $allValues = $this->client->getCookieJar()->allValues($this->getCurrentUrl()); - - if (isset($allValues[$name])) { - return $allValues[$name]; - } - - return null; - } - - /** - * {@inheritdoc} - */ - public function getStatusCode() - { - return $this->getResponse()->getStatus(); - } - - /** - * {@inheritdoc} - */ - public function getContent() - { - return $this->getResponse()->getContent(); - } - - /** - * {@inheritdoc} - */ - public function findElementXpaths($xpath) - { - $nodes = $this->getCrawler()->filterXPath($xpath); - - $elements = array(); - foreach ($nodes as $i => $node) { - $elements[] = sprintf('(%s)[%d]', $xpath, $i + 1); - } - - return $elements; - } - - /** - * {@inheritdoc} - */ - public function getTagName($xpath) - { - return $this->getCrawlerNode($this->getFilteredCrawler($xpath))->nodeName; - } - - /** - * {@inheritdoc} - */ - public function getText($xpath) - { - $text = $this->getFilteredCrawler($xpath)->text(); - $text = str_replace("\n", ' ', $text); - $text = preg_replace('/ {2,}/', ' ', $text); - - return trim($text); - } - - /** - * {@inheritdoc} - */ - public function getHtml($xpath) - { - // cut the tag itself (making innerHTML out of outerHTML) - return preg_replace('/^\<[^\>]+\>|\<[^\>]+\>$/', '', $this->getOuterHtml($xpath)); - } - - /** - * {@inheritdoc} - */ - public function getOuterHtml($xpath) - { - $node = $this->getCrawlerNode($this->getFilteredCrawler($xpath)); - - return $node->ownerDocument->saveHTML($node); - } - - /** - * {@inheritdoc} - */ - public function getAttribute($xpath, $name) - { - $node = $this->getFilteredCrawler($xpath); - - if ($this->getCrawlerNode($node)->hasAttribute($name)) { - return $node->attr($name); - } - - return null; - } - - /** - * {@inheritdoc} - */ - public function getValue($xpath) - { - if (in_array($this->getAttribute($xpath, 'type'), array('submit', 'image', 'button'), true)) { - return $this->getAttribute($xpath, 'value'); - } - - $node = $this->getCrawlerNode($this->getFilteredCrawler($xpath)); - - if ('option' === $node->tagName) { - return $this->getOptionValue($node); - } - - try { - $field = $this->getFormField($xpath); - } catch (\InvalidArgumentException $e) { - return $this->getAttribute($xpath, 'value'); - } - - return $field->getValue(); - } - - /** - * {@inheritdoc} - */ - public function setValue($xpath, $value) - { - $this->getFormField($xpath)->setValue($value); - } - - /** - * {@inheritdoc} - */ - public function check($xpath) - { - $this->getCheckboxField($xpath)->tick(); - } - - /** - * {@inheritdoc} - */ - public function uncheck($xpath) - { - $this->getCheckboxField($xpath)->untick(); - } - - /** - * {@inheritdoc} - */ - public function selectOption($xpath, $value, $multiple = false) - { - $field = $this->getFormField($xpath); - - if (!$field instanceof ChoiceFormField) { - throw new DriverException(sprintf('Impossible to select an option on the element with XPath "%s" as it is not a select or radio input', $xpath)); - } - - if ($multiple) { - $oldValue = (array) $field->getValue(); - $oldValue[] = $value; - $value = $oldValue; - } - - $field->select($value); - } - - /** - * {@inheritdoc} - */ - public function isSelected($xpath) - { - $optionValue = $this->getOptionValue($this->getCrawlerNode($this->getFilteredCrawler($xpath))); - $selectField = $this->getFormField('(' . $xpath . ')/ancestor-or-self::*[local-name()="select"]'); - $selectValue = $selectField->getValue(); - - return is_array($selectValue) ? in_array($optionValue, $selectValue, true) : $optionValue === $selectValue; - } - - /** - * {@inheritdoc} - */ - public function click($xpath) - { - $crawler = $this->getFilteredCrawler($xpath); - $node = $this->getCrawlerNode($crawler); - $tagName = $node->nodeName; - - if ('a' === $tagName) { - $this->client->click($crawler->link()); - $this->forms = array(); - } elseif ($this->canSubmitForm($node)) { - $this->submit($crawler->form()); - } elseif ($this->canResetForm($node)) { - $this->resetForm($node); - } else { - $message = sprintf('%%s supports clicking on links and submit or reset buttons only. But "%s" provided', $tagName); - - throw new UnsupportedDriverActionException($message, $this); - } - } - - /** - * {@inheritdoc} - */ - public function isChecked($xpath) - { - $field = $this->getFormField($xpath); - - if (!$field instanceof ChoiceFormField || 'select' === $field->getType()) { - throw new DriverException(sprintf('Impossible to get the checked state of the element with XPath "%s" as it is not a checkbox or radio input', $xpath)); - } - - if ('checkbox' === $field->getType()) { - return $field->hasValue(); - } - - $radio = $this->getCrawlerNode($this->getFilteredCrawler($xpath)); - - return $radio->getAttribute('value') === $field->getValue(); - } - - /** - * {@inheritdoc} - */ - public function attachFile($xpath, $path) - { - $field = $this->getFormField($xpath); - - if (!$field instanceof FileFormField) { - throw new DriverException(sprintf('Impossible to attach a file on the element with XPath "%s" as it is not a file input', $xpath)); - } - - $field->upload($path); - } - - /** - * {@inheritdoc} - */ - public function submitForm($xpath) - { - $crawler = $this->getFilteredCrawler($xpath); - - $this->submit($crawler->form()); - } - - /** - * @return Response - * - * @throws DriverException If there is not response yet - */ - protected function getResponse() - { - $response = $this->client->getInternalResponse(); - - if (null === $response) { - throw new DriverException('Unable to access the response before visiting a page'); - } - - return $response; - } - - /** - * Prepares URL for visiting. - * Removes "*.php/" from urls and then passes it to BrowserKitDriver::visit(). - * - * @param string $url - * - * @return string - */ - protected function prepareUrl($url) - { - $replacement = ($this->removeHostFromUrl ? '' : '$1') . ($this->removeScriptFromUrl ? '' : '$2'); - - return preg_replace('#(https?\://[^/]+)(/[^/\.]+\.php)?#', $replacement, $url); - } - - /** - * Returns form field from XPath query. - * - * @param string $xpath - * - * @return FormField - * - * @throws DriverException - */ - protected function getFormField($xpath) - { - $fieldNode = $this->getCrawlerNode($this->getFilteredCrawler($xpath)); - $fieldName = str_replace('[]', '', $fieldNode->getAttribute('name')); - - $formNode = $this->getFormNode($fieldNode); - $formId = $this->getFormNodeId($formNode); - - if (!isset($this->forms[$formId])) { - $this->forms[$formId] = new Form($formNode, $this->getCurrentUrl()); - } - - if (is_array($this->forms[$formId][$fieldName])) { - return $this->forms[$formId][$fieldName][$this->getFieldPosition($fieldNode)]; - } - - return $this->forms[$formId][$fieldName]; - } - - /** - * Returns the checkbox field from xpath query, ensuring it is valid. - * - * @param string $xpath - * - * @return ChoiceFormField - * - * @throws DriverException when the field is not a checkbox - */ - private function getCheckboxField($xpath) - { - $field = $this->getFormField($xpath); - - if (!$field instanceof ChoiceFormField) { - throw new DriverException(sprintf('Impossible to check the element with XPath "%s" as it is not a checkbox', $xpath)); - } - - return $field; - } - - /** - * @param \DOMElement $element - * - * @return \DOMElement - * - * @throws DriverException if the form node cannot be found - */ - private function getFormNode(\DOMElement $element) - { - if ($element->hasAttribute('form')) { - $formId = $element->getAttribute('form'); - $formNode = $element->ownerDocument->getElementById($formId); - - if (null === $formNode || 'form' !== $formNode->nodeName) { - throw new DriverException(sprintf('The selected node has an invalid form attribute (%s).', $formId)); - } - - return $formNode; - } - - $formNode = $element; - - do { - // use the ancestor form element - if (null === $formNode = $formNode->parentNode) { - throw new DriverException('The selected node does not have a form ancestor.'); - } - } while ('form' !== $formNode->nodeName); - - return $formNode; - } - - /** - * Gets the position of the field node among elements with the same name - * - * BrowserKit uses the field name as index to find the field in its Form object. - * When multiple fields have the same name (checkboxes for instance), it will return - * an array of elements in the order they appear in the DOM. - * - * @param \DOMElement $fieldNode - * - * @return integer - */ - private function getFieldPosition(\DOMElement $fieldNode) - { - $elements = $this->getCrawler()->filterXPath('//*[@name=\''.$fieldNode->getAttribute('name').'\']'); - - if (count($elements) > 1) { - // more than one element contains this name ! - // so we need to find the position of $fieldNode - foreach ($elements as $key => $element) { - /** @var \DOMElement $element */ - if ($element->getNodePath() === $fieldNode->getNodePath()) { - return $key; - } - } - } - - return 0; - } - - private function submit(Form $form) - { - $formId = $this->getFormNodeId($form->getFormNode()); - - if (isset($this->forms[$formId])) { - $this->mergeForms($form, $this->forms[$formId]); - } - - // remove empty file fields from request - foreach ($form->getFiles() as $name => $field) { - if (empty($field['name']) && empty($field['tmp_name'])) { - $form->remove($name); - } - } - - foreach ($form->all() as $field) { - // Add a fix for https://github.com/symfony/symfony/pull/10733 to support Symfony versions which are not fixed - if ($field instanceof TextareaFormField && null === $field->getValue()) { - $field->setValue(''); - } - } - - $this->client->submit($form); - - $this->forms = array(); - } - - private function resetForm(\DOMElement $fieldNode) - { - $formNode = $this->getFormNode($fieldNode); - $formId = $this->getFormNodeId($formNode); - unset($this->forms[$formId]); - } - - /** - * Determines if a node can submit a form. - * - * @param \DOMElement $node Node. - * - * @return boolean - */ - private function canSubmitForm(\DOMElement $node) - { - $type = $node->hasAttribute('type') ? $node->getAttribute('type') : null; - - if ('input' === $node->nodeName && in_array($type, array('submit', 'image'), true)) { - return true; - } - - return 'button' === $node->nodeName && (null === $type || 'submit' === $type); - } - - /** - * Determines if a node can reset a form. - * - * @param \DOMElement $node Node. - * - * @return boolean - */ - private function canResetForm(\DOMElement $node) - { - $type = $node->hasAttribute('type') ? $node->getAttribute('type') : null; - - return in_array($node->nodeName, array('input', 'button'), true) && 'reset' === $type; - } - - /** - * Returns form node unique identifier. - * - * @param \DOMElement $form - * - * @return string - */ - private function getFormNodeId(\DOMElement $form) - { - return md5($form->getLineNo() . $form->getNodePath() . $form->nodeValue); - } - - /** - * Gets the value of an option element - * - * @param \DOMElement $option - * - * @return string - * - * @see \Symfony\Component\DomCrawler\Field\ChoiceFormField::buildOptionValue - */ - private function getOptionValue(\DOMElement $option) - { - if ($option->hasAttribute('value')) { - return $option->getAttribute('value'); - } - - if (!empty($option->nodeValue)) { - return $option->nodeValue; - } - - return '1'; // DomCrawler uses 1 by default if there is no text in the option - } - - /** - * Merges second form values into first one. - * - * @param Form $to merging target - * @param Form $from merging source - */ - private function mergeForms(Form $to, Form $from) - { - foreach ($from->all() as $name => $field) { - $fieldReflection = new \ReflectionObject($field); - $nodeReflection = $fieldReflection->getProperty('node'); - $valueReflection = $fieldReflection->getProperty('value'); - - $nodeReflection->setAccessible(true); - $valueReflection->setAccessible(true); - - $isIgnoredField = $field instanceof InputFormField && - in_array($nodeReflection->getValue($field)->getAttribute('type'), array('submit', 'button', 'image'), true); - - if (!$isIgnoredField) { - $valueReflection->setValue($to[$name], $valueReflection->getValue($field)); - } - } - } - - /** - * Returns DOMElement from crawler instance. - * - * @param Crawler $crawler - * - * @return \DOMElement - * - * @throws DriverException when the node does not exist - */ - private function getCrawlerNode(Crawler $crawler) - { - $crawler->rewind(); - $node = $crawler->current(); - - if (null !== $node) { - return $node; - } - - throw new DriverException('The element does not exist'); - } - - /** - * Returns a crawler filtered for the given XPath, requiring at least 1 result. - * - * @param string $xpath - * - * @return Crawler - * - * @throws DriverException when no matching elements are found - */ - private function getFilteredCrawler($xpath) - { - if (!count($crawler = $this->getCrawler()->filterXPath($xpath))) { - throw new DriverException(sprintf('There is no element matching XPath "%s"', $xpath)); - } - - return $crawler; - } - - /** - * Returns crawler instance (got from client). - * - * @return Crawler - * - * @throws DriverException - */ - private function getCrawler() - { - $crawler = $this->client->getCrawler(); - - if (null === $crawler) { - throw new DriverException('Unable to access the response content before visiting a page'); - } - - return $crawler; - } -} diff --git a/vendor/behat/mink-goutte-driver/.gitignore b/vendor/behat/mink-goutte-driver/.gitignore deleted file mode 100644 index 1d034f4..0000000 --- a/vendor/behat/mink-goutte-driver/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -vendor -composer.phar -composer.lock -phpunit.xml diff --git a/vendor/behat/mink-goutte-driver/.travis.yml b/vendor/behat/mink-goutte-driver/.travis.yml deleted file mode 100644 index 5b86531..0000000 --- a/vendor/behat/mink-goutte-driver/.travis.yml +++ /dev/null @@ -1,27 +0,0 @@ -language: php - -sudo: false - -cache: - directories: - - $HOME/.composer/cache/files - -php: [5.3, 5.4, 5.5, 5.6, hhvm] - -before_install: - # Force using Goutte 2 on HHVM for now because Guzzle 6 is broken there - - if [ "hhvm" = "$TRAVIS_PHP_VERSION" ]; then composer require fabpot/goutte '~2' --no-update; fi - -install: - - composer install - -before_script: - - export WEB_FIXTURES_HOST=http://localhost:8000 - - # Start a webserver for web fixtures. Force using PHP 5.6 to be able to run it on PHP 5.3 and HHVM jobs too - - ~/.phpenv/versions/5.6/bin/php -S localhost:8000 -t vendor/behat/mink/driver-testsuite/web-fixtures > /dev/null 2>&1 & - -script: phpunit -v --coverage-clover=coverage.clover - -after_script: - - wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/vendor/behat/mink-goutte-driver/CHANGELOG.md b/vendor/behat/mink-goutte-driver/CHANGELOG.md deleted file mode 100644 index 581eb5e..0000000 --- a/vendor/behat/mink-goutte-driver/CHANGELOG.md +++ /dev/null @@ -1,31 +0,0 @@ -1.2.0 / 2015-09-21 -================== - -New features: - -* Added support for Goutte 3.1+ - -Misc: - -* Updated the repository structure to PSR-4 - -1.1.0 / 2014-10-09 -================== - -The driver now relies on BrowserKitDriver 1.2.x, so all changes of this driver are relevant. -The changes below only describe the changes related to GoutteDriver specifically. - -New features: - -* Added the possibility to use a normal Goutte client instead of requiring to use an extended one -* Added the support of Goutte 2.0 as well - -Bug fixes: - -* Fixed the support of disabling basic auth -* Fixed the resetting of the driver to reset the basic auth - -Testing: - -* Updated the testsuite to use the new Mink 1.6 driver testsuite -* Added testing on HHVM diff --git a/vendor/behat/mink-goutte-driver/LICENSE b/vendor/behat/mink-goutte-driver/LICENSE deleted file mode 100644 index 3365ae6..0000000 --- a/vendor/behat/mink-goutte-driver/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2012-2013 Konstantin Kudryashov - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/behat/mink-goutte-driver/README.md b/vendor/behat/mink-goutte-driver/README.md deleted file mode 100644 index cba60a0..0000000 --- a/vendor/behat/mink-goutte-driver/README.md +++ /dev/null @@ -1,60 +0,0 @@ -Mink Goutte Driver -================== - -[![Latest Stable Version](https://poser.pugx.org/behat/mink-goutte-driver/v/stable.svg)](https://packagist.org/packages/behat/mink-goutte-driver) -[![Latest Unstable Version](https://poser.pugx.org/behat/mink-goutte-driver/v/unstable.svg)](https://packagist.org/packages/behat/mink-goutte-driver) -[![Total Downloads](https://poser.pugx.org/behat/mink-goutte-driver/downloads.svg)](https://packagist.org/packages/behat/mink-goutte-driver) -[![Build Status](https://travis-ci.org/minkphp/MinkGoutteDriver.svg?branch=master)](https://travis-ci.org/minkphp/MinkGoutteDriver) -[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/minkphp/MinkGoutteDriver/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/minkphp/MinkGoutteDriver/) -[![Code Coverage](https://scrutinizer-ci.com/g/minkphp/MinkGoutteDriver/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/minkphp/MinkGoutteDriver/) -[![License](https://poser.pugx.org/behat/mink-goutte-driver/license.svg)](https://packagist.org/packages/behat/mink-goutte-driver) - -Usage Example -------------- - -``` php - new Session(new GoutteDriver(new GoutteClient())), -)); - -$session = $mink->getSession('goutte'); -$session->visit("http://php.net/"); -$session->getPage()->clickLink('Downloads'); -echo $session->getCurrentUrl() . PHP_EOL; -``` - -Installation ------------- - -Add a file composer.json with content: - -``` json -{ - "require": { - "behat/mink": "~1.5", - "behat/mink-goutte-driver": "~1.0" - } -} -``` - -(or merge the above into your project's existing composer.json file) - -``` bash -$> curl -sS https://getcomposer.org/installer | php -$> php composer.phar install -``` - -Maintainers ------------ - -* Christophe Coevoet [stof](https://github.com/stof) -* Other [awesome developers](https://github.com/minkphp/MinkGoutteDriver/graphs/contributors) diff --git a/vendor/behat/mink-goutte-driver/composer.json b/vendor/behat/mink-goutte-driver/composer.json deleted file mode 100644 index 80d6ecf..0000000 --- a/vendor/behat/mink-goutte-driver/composer.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "behat/mink-goutte-driver", - "description": "Goutte driver for Mink framework", - "keywords": ["goutte", "headless", "testing", "browser"], - "homepage": "http://mink.behat.org/", - "type": "mink-driver", - "license": "MIT", - - "authors": [ - { - "name": "Konstantin Kudryashov", - "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" - } - ], - - "require": { - "php": ">=5.3.1", - "behat/mink": "~1.6@dev", - "behat/mink-browserkit-driver": "~1.2@dev", - "fabpot/goutte": "~1.0.4|~2.0|~3.1" - }, - - "require-dev": { - "symfony/phpunit-bridge": "~2.7" - }, - - "autoload": { - "psr-4": { - "Behat\\Mink\\Driver\\": "src/" - } - }, - - "autoload-dev": { - "psr-4": { - "Behat\\Mink\\Tests\\Driver\\": "tests" - } - }, - - "extra": { - "branch-alias": { - "dev-master": "1.2.x-dev" - } - } -} diff --git a/vendor/behat/mink-goutte-driver/phpunit.xml.dist b/vendor/behat/mink-goutte-driver/phpunit.xml.dist deleted file mode 100644 index cbe57c4..0000000 --- a/vendor/behat/mink-goutte-driver/phpunit.xml.dist +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - tests - vendor/behat/mink/driver-testsuite/tests/Basic - vendor/behat/mink/driver-testsuite/tests/Form - - - - - - - - - - - - ./src - - - diff --git a/vendor/behat/mink-goutte-driver/src/Goutte/Client.php b/vendor/behat/mink-goutte-driver/src/Goutte/Client.php deleted file mode 100644 index 9d64be7..0000000 --- a/vendor/behat/mink-goutte-driver/src/Goutte/Client.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Behat\Mink\Driver\Goutte; - -use Goutte\Client as BaseClient; -use Symfony\Component\BrowserKit\Response; - -/** - * Client overrides to support Mink functionality. - */ -class Client extends BaseClient -{ - /** - * Reads response meta tags to guess content-type charset. - * - * @param Response $response - * - * @return Response - */ - protected function filterResponse($response) - { - $contentType = $response->getHeader('Content-Type'); - - if (!$contentType || false === strpos($contentType, 'charset=')) { - if (preg_match('/\]+charset *= *["\']?([a-zA-Z\-0-9]+)/i', $response->getContent(), $matches)) { - $headers = $response->getHeaders(); - $headers['Content-Type'] = $contentType.';charset='.$matches[1]; - - $response = new Response($response->getContent(), $response->getStatus(), $headers); - } - } - - return parent::filterResponse($response); - } -} diff --git a/vendor/behat/mink-goutte-driver/src/GoutteDriver.php b/vendor/behat/mink-goutte-driver/src/GoutteDriver.php deleted file mode 100644 index a42d5a2..0000000 --- a/vendor/behat/mink-goutte-driver/src/GoutteDriver.php +++ /dev/null @@ -1,75 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Behat\Mink\Driver; - -use Behat\Mink\Driver\Goutte\Client as ExtendedClient; -use Goutte\Client; - -/** - * Goutte driver. - * - * @author Konstantin Kudryashov - */ -class GoutteDriver extends BrowserKitDriver -{ - /** - * Initializes Goutte driver. - * - * @param Client $client Goutte client instance - */ - public function __construct(Client $client = null) - { - parent::__construct($client ?: new ExtendedClient()); - } - - /** - * {@inheritdoc} - */ - public function setBasicAuth($user, $password) - { - if (false === $user) { - $this->getClient()->resetAuth(); - - return; - } - - $this->getClient()->setAuth($user, $password); - } - - /** - * Gets the Goutte client. - * - * The method is overwritten only to provide the appropriate return type hint. - * - * @return Client - */ - public function getClient() - { - return parent::getClient(); - } - - /** - * {@inheritdoc} - */ - public function reset() - { - parent::reset(); - $this->getClient()->resetAuth(); - } - - /** - * {@inheritdoc} - */ - protected function prepareUrl($url) - { - return $url; - } -} diff --git a/vendor/behat/mink/.gitignore b/vendor/behat/mink/.gitignore deleted file mode 100644 index 66de342..0000000 --- a/vendor/behat/mink/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.tgz -*.phar -phpunit.xml -composer.lock -vendor diff --git a/vendor/behat/mink/.travis.yml b/vendor/behat/mink/.travis.yml deleted file mode 100644 index 677ee0e..0000000 --- a/vendor/behat/mink/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -language: php - -sudo: false - -php: [5.3, 5.4, 5.5, 5.6, 7.0, hhvm] - -matrix: - fast_finish: true - include: - - php: 5.3 - env: COMPOSER_FLAGS='--prefer-lowest --prefer-stable' SYMFONY_DEPRECATIONS_HELPER=weak - allow_failures: - - php: 7.0 - -cache: - directories: - - $HOME/.composer/cache/files - -before_install: - - composer self-update - -install: - - composer update $COMPOSER_FLAGS - -script: phpunit -v --coverage-clover=coverage.clover - -after_script: - - if [[ "7.0" != "$TRAVIS_PHP_VERSION" && "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi diff --git a/vendor/behat/mink/CHANGES.md b/vendor/behat/mink/CHANGES.md deleted file mode 100644 index 4ef4ecb..0000000 --- a/vendor/behat/mink/CHANGES.md +++ /dev/null @@ -1,297 +0,0 @@ -1.7.0 / 2015-09-20 -================== - -New features: - -* Added `Session::getResponseHeader` to access a response header easily -* Added support for header assertions -* Added a forward compatibility layer for drivers to allow them to prepare - for Mink 2.0 (they won't require any change if they use it). They should - now overwrite `CoreDriver::findElementXpaths` instead of implementing `find` - and `setSession` themselves. -* Added escaping of the locator in the NamedSelector rather than expecting - the caller to perform the escaping. Passing an escaped locator is still - supported but deprecated. -* Remove the dependency on the Session in expectation exceptions. Passing - the session in the exception constructor is now deprecated. The driver - should be passed instead. - -Bug fixes: - -* Fixed the URL assertions when comparing paths ending in ``.php`` -* Silenced deprecation warnings (following the Symfony convention) to make - them less invasive. Use the `symfony/phpunit-bridge` to get them reported - when using Mink in your PHPUnit tests. -* Fixed `NodeElement::hasClass` in case the class attribute contains newlines - -Testsuite: - -* Made the testsuite compatible with PHPUnit strict timing mode (only the library testsuite, not the driver one) -* Added testing against PHP 7 -* Added testing against lowest version of dependencies to ensure we got the lower bounds right - -Driver testsuite: - -* Added an extra test to ensure the right behavior when getting the HTML with empty elements -* Added a few more safeguards to ensure test failures rather than fatal errors for misbehaving drivers -* Added a test ensuring that drivers follow recommended practices - -Misc: - -* Added a few missing deprecation warnings for deprecated APIs or classes. - -1.6.1 / 2015-02-04 -================== - -Bug fixes: - -* Added a check for empty path in `WebAssert::cleanUrl()` - -Driver testsuite: - -* Added an extra test to ensure the right behavior for traversal - -Misc: - -* Changed the description in the composer.json -* Switched the repository structure to use PSR-4 -* Updated URLs for the move to the new Github organization - -1.6.0 / 2014-09-26 -================== - - * [BC break] Changed the named selector to prefer exact matches over partial matches - * [BC break] Changed `NodeElement::getValue` for checkboxes to return the value rather than the checked state (use `isChecked` for that) - * Fixed the XPath prefixing when searching inside an existing element - * Refactored the driver testsuite entirely and expand it to cover drivers entirely (covering many more cases for consistency) - * Changed `NodeElement::setValue` to support any fields rather than only input elements - * Removed the wrapping of any driver-level exception in a MinkException on invalid usage as it was making the code too complex - * Fixed the matching of the input type in the named selector to be case insensitive according to the HTML spec - * Introduced `Behat\Mink\Selector\Xpath\Escaper` to allow reusing the XPath escaping - * Deprecated `Element::getSession`. Code needing the session should get it from outside rather than the element - * Changed ElementNotFoundException to extend from ExpectationException - * Added `Element::getOuterHtml` to get the HTML code of the element including itself - * Fixed the name selectors to match on the `placeholder` only for textual inputs - * Enforced consistent behavior for drivers on 4xx and 5xx response to return the response rather than throwing an exception - * Added `Element::waitFor` to allow retrying some code until it succeeds or the timeout is reached - * Added `Element::isValid` to check whether an element still exists in the page - * Made `Session::executeScript` compatible across drivers by ensuring they all support the same syntaxes for the JS expression - * Made `Session::evaluateScript` compatible across drivers by ensuring they all support the same syntaxes for the JS expression - * Removed `hasClass` from `DocumentElement` (instead of triggering a fatal error) - * Added testing on HHVM to ensure consistency - * Fixed `NodeElement::getTagName` to ensure that the tag name is lowercase for all drivers - * Fixed `Element::hasAttribute` to ensure it supports attributes with an empty value - * Fixed the `field` selector to avoid matching inputs with the type `submit` or `reset` - * Changed the button XPath selection to accept `reset` buttons as well - * Changed `Session::wait` to return the condition value rather than nothing - * Added `Session::getWindowName` and `Session::getWindowNames` to get the name of the current and of all windows - * Added `Session::maximizeWindow` to maximize the window - * Added `NodeElement::isSelected` to check whether an `